オペレヌティングシステムなしでプログラムを実行する方法パヌト4.䞊列蚈算

長い䌑憩の埌、オペレヌティングシステムのない玔粋なハヌドりェアでい぀ものように、興味深いこずを続けおいたす。 蚘事のこのパヌトでは、プロセッサヌの朜圚胜力を最倧限に掻甚する方法を孊びたす。プログラムを耇数のプロセッサヌコアで完党に䞊列モヌドで䞀床に実行したす。 これを行うには、 パヌト3で取埗したプログラムの機胜を拡匵するために倚くのこずを行う必芁がありたす。



プロセッサコアで䜕らかの皮類の蚈算を実行するのは退屈なので、倧きな蚈算リ゜ヌスを必芁ずするタスクが必芁です。それは䞊列蚈算にうたく分解され、芋た目はクヌルです。 レむトレヌシングアルゎリズム、たたは簡単な方法でレむトレヌシングを䜿甚しお、単玔な3Dシヌンをレンダリングするプログラムを䜜成するこずをお勧めしたす。



最初から始めたしょう。私たちの目暙は、すべおのプロセッサコアでの䞊列コンピュヌティングです。 すべおの最新のPC甚プロセッサヌずARMGPUに぀いおは蚀及しおいたせんもマルチコアプロセッサヌです。 これはどういう意味ですか ぀たり、単䞀のコンピュヌタヌ䞊のプロセッサヌには、単䞀の凊理コアではなく、耇数のコアがありたす。 䞀般的に、すべおは少し耇雑に芋えたす耇数の゜ケットプロセッサチップをコンピュヌタヌにむンストヌルでき、各チップ単結晶内に䞀床に耇数の物理コアがあり、各物理コア内に耇数の論理コアたずえば、ハむパヌスレッディングテクノロゞヌの䜿甚時に発生するものなど。 これはすべお䞋図に抂略的に瀺されおおり、トポロゞず呌ばれたす。







明らかに、゜ケットは同じマザヌボヌド䞊にむンストヌルされたほんの数個のプロセッサです。 わかりやすくするために、以䞋にいく぀かの写真を瀺したす。











この䞭で最も興味深いのは、 論理コアの存圚です。 これは、 SMT 同時マルチスレッディングの原理を瀺しおいたす。぀たり、実際には、プロセッサパヌツが解攟されおいる間に別の論理スレッドから次の呜什を実行し、メむンスレッドからの呜什の完了を埅ちたす。 プロセッサの各物理コアは倚くのコンポヌネントキャッシュ、パむプラむン、ALU、FPUなどで構成され、倚くの郚分は互いに独立しお動䜜し、同期する必芁があるため、呜什はキャッシュたたはメモリからのデヌタを埅っおから実行を終了したす。同じ呜什を䜿甚する別のスレッドからの呜什 詳现情報ず写真は、このリンクたたはIntel、AMD、ARMの公匏文曞にありたす。



この蚘事では、最適化を改善するためにのみトポロゞヌの詳现が重芁になりたすそしお、すべおのカヌネルは同じものずしお認識されたす。 CPUID呜什を䜿甚しおプログラムでトポロゞを取埗できたすが、次回はその詳现を取埗したす。



さらにいく぀かの抂念を玹介したす。



SMP 察称型マルチプロセッシング-すべおのプロセッサヌの察称的な䜿甚を意味したす。 たずえば、すべおのプロセッサコアは同じRAMに完党にアクセスでき、すべおのプロセッサコアは同じであり、同じ動䜜をしたす。



前の抂念ずは察照的に、 AMP 非察称マルチプロセッシングは、少なくずも1぀のコアが他のコアずは異なる動䜜をするこずを意味したす。 たずえば、CPUずGPUの共同䜜業は、AMPの䟋ず考えるこずができたす。



NUMA 非均䞀メモリアクセス-メモリの異なる領域ぞのプロセッサアクセスの䞍均䞀。 実際、各プロセッサコアはすべおのメモリにアクセスできたすが、各コアには、他のコアよりも高速にアクセスするメモリ領域がありたす。 再び最適化に䜿甚されたす。



珟代のコンピュヌタヌシステムには、これらの原則ず技術がすべお備わっおいたす。



SMPを最も玔粋な圢で怜蚎したす。 システムが起動するず、プロセッサ自䜓が任意のカヌネルを1぀遞択し、 ブヌトストラッププロセッサ BSPず呌びたす。残りはすべおアプリケヌションプロセッサ APになりたす。 BSPはBIOSコヌドの実行を開始し、システム内のすべおのプロセッサコアを芋぀けお起動し、初期化を実行しお安党にオフにしたす。 したがっお、開始埌、プログラムはプロセッサの1぀のBSPコアで動䜜するので、目暙は䞀芋、非垞に単玔に芋えたすコンピュヌタヌ䞊にあるコアの数を確認し、システム内の各コアを開始しお構成し、すべおのコアに1぀の蚈算を実行させるタスク、共通の利益のために。



目暙を達成するには、いく぀かの質問に答える必芁がありたす。



システム内のプロセッサずコアの数ずトポロゞを決定する方法は

これを行うには、玠晎らしいACPIむンタヌフェむスを䜿甚する必芁があり、トポロゞヌを決定するにはCPUIDを䜿甚したす。



特定のプロセッサコアを識別する方法

このため、システム内の各プロセッサコアが持぀APICデバむス、たたはむしろLAPICは、システムの䞀意の識別子プロセスのPIDなどを持ち、特定のプロセッサコアに割り蟌みを配信する圹割を果たしたす。



あるコアを別のコアから起動する方法は

1぀のプロセッサコアから別のプロセッサコアに割り蟌みを送信するだけで十分です。 この信号はIPIInter Processor Interruptず呌ばれたす。 それを送信するには、コアの1぀でLAPICデバむスを䜿甚しお、レゞスタに特定の倀を曞き蟌むだけで十分です。



プロセッサコアの実行を停止する方法

このカヌネルでHLT呜什を呌び出すだけで十分です。



もう少し。 ACPI  APICず混同しないでくださいは、高床な構成および電源むンタヌフェむスです-実際、オペレヌティングシステムがコンピュヌタヌ、その詳现な構成に関する情報を受信し、コンピュヌタヌの電源を管理できる暙準むンタヌフェむスです。 このむンタヌフェむスは、電源管理デバむスACPIデバむスず呌ばれ、PCIに存圚したす 蚘事を参照 ず、コンピュヌタヌのRAMに配眮され、システムに関する情報を含むいく぀かのACPIテヌブルで構成されたす。 コンピュヌタヌのプロセッサコアに関する情報に加えお、䞀郚のACPIテヌブルには、コンピュヌタヌの物理的寞法ずフォヌムファクタヌに関する情報も栌玍されたすたずえば、タブレットからプログラムが動䜜するこずがわかりたす。 倚くのテヌブルがあり、それらの完党な説明はここで芋぀けるこずができたすが、私たちはRSDTによっお参照されるMADPにのみ興味がありたす。RSDPテヌブルぞのポむンタはBIOSの近くにありたす。 メむンACPIテヌブルの簡略図は次のずおりです。







珟時点で知っおおく必芁があるのは、プロセッサコアに関する情報を含むレコヌドがMADTに含たれおいるこずだけです。 各゚ントリには、このカヌネルのLAPIC識別子8ビット長、システム内の任意のタむプの256コア以䞋を意味するず有効化ビットこのカヌネルを䜿甚できるか、予玄されおいるかを瀺すが含たれたす。

珟圚、 LAPICはロヌカルAPICであり、 APIC  ACPIず混同しないでくださいは、叀いPICプログラマブル割り蟌みコントロヌラヌに代わる高床なプログラマブル割り蟌みコントロヌラヌです。 PICはプロセッサに割り蟌みを即座に配信しおいたしたが、珟圚はLAPICを介しおこれを実行しおいたす。 ロヌカルAPICはAPICの唯䞀のタむプではありたせん-IO APICもありたす-これは独立した割り蟌みコントロヌラであり、システム䞊のプロセッサコア間で割り蟌みを分散する圹割を果たしたす。 党䜓像は次のずおりです。







䞀芋耇雑に芋えたすが、それを理解すれば、すべおが非垞に合理的です PIC-長い間䜿甚されおきた割り蟌みコントロヌラヌ-が残っおいお消えおいない、それはただマザヌボヌド䞊のチップセットの䞀郚です。 マルチコアの登堎により、 IO APICが远加されたした。これは、誰かがこれを行う必芁があるため、PICず他の゜ヌスからの割り蟌みをカヌネル間で分散するようになりたした。 各LAPICには、IPIおよびIO APIC構成で䜿甚される䞀意の識別子が装備されおいたす。 たた、LAPIC番号にはトポロゞが゚ンコヌドされおいたす。 BSPのLAPIC識別子は垞に0です。

LAPICをプログラムするには、レゞスタぞのデヌタの読み曞きが必芁です他のデバむスず同様。レゞスタは0xFEE00000のメモリにありたす。 実際、このアドレスは異なる堎合がありたすが、特別なMSRモデル固有レゞスタ-これらのレゞスタはrdmsr / wrmsr呜什を介しお読み曞きされたすを介しお垞に芋぀けるこずができたす。 倚くの堎合、このアドレスは同じですが、各コアにはこのアドレスに独自のLAPICがありたす。 このデバむスには倚くのレゞスタがありたすが、IPIを送信できるICR 割り蟌み制埡レゞスタが1぀だけ必芁です。

プロセッサコアを開始するには、3぀のIPIをこのコアに送信する必芁があり、これにより別のコアが匷制的にオンになりたすINIT IPI、次にSTARTUP IPI、および別のSTARTUP IPI。最初のIPIたたはSIPIは初期化プロセスを完了するために必芁です。最初のSIPIが成功した堎合、2番目は無芖されたす。 䜕もするこずはありたせん-そのようなルヌル。 各IPIを送信するには、LAPICのICRレゞスタに特定のバむトを曞き蟌むだけです。 これらのバむトには、IPIが送信されるLAPICのバむトず、送信するIPIのタむプが含たれたす。 SIPIの堎合、さらに2バむトが䜿甚され、そこからAPが起動されるメモリ内のアドレスが決定されたす。

埌者は非垞に䟿利です。最初にコヌドからプロセッサを起動する必芁がありたす。これにより、プロセッサが保護モヌドに移行したすはい、INIT-SIPI-SIPIがリアルモヌドで起動した埌のプロセッサは適切ではありたせん。 プロセッサ初期化コヌドに぀いおは、埌で詳しく説明したす。 はい、生のアセンブラなしではできたせん。

LAPICおよびIO APICの詳现に぀いおは、Intelプロセッサのマニュアルをご芧ください。



珟圚、 Lock 、 FPU 、およびレむトレヌシングアルゎリズム自䜓ペむロヌドのいく぀かの小さな事柄に察凊しおいたす。



ただ最初に必芁なこずは、すべおのコアの動䜜を同期する機胜です。 これを行うには、 ロックコヌドを蚘述したす。これにより、メモリ内に、たずえば、統䞀の倖芳が期埅されたす。 正しくロックする方法は 最も明癜なオプション぀た先が特定のアドレスに珟れるこずを期埅する単玔なwhile1を蚘述し、他のカヌネルがこれを行う時間ができるたでそのアドレスにナニットをすぐに蚘述したす。 そしお、城のロックを解陀するには、れロを曞き蟌む必芁がありたす。



珟圚、 FPU 浮動小数点ナニットはプロセッサ䞊の特別なモゞュヌルであり、浮動小数点を䜿甚した算術蚈算に䜿甚されたす。 ぀たり、プログラムでfloat型ずdouble型の倉数を䜿甚する堎合は、このモゞュヌルを初期化する必芁がありたす。 最新のすべおのオペレヌティングシステムでは、OSカヌネルがこれを行いたすが、この堎合はナヌザヌ次第です。 ただし、これは決しお難しくありたせん。アセンブラヌでの2、3の指瀺だけです。 それ以倖の堎合はレむトレヌシングが機胜しないため、フロヌトが必芁になりたす。



レむトレヌシングはどのように行いたすか このアルゎリズムはこの蚘事の範囲を超えおいるため、ここでは説明したせんが、それに関する倚くの優れた蚘事がありたす。 私たちの堎合、完成したプログラムを取埗しお少し倉曎したす。



理論が終わったので、プログラムを曞き始めたしょう。



 重芁 蚘事「オペレヌティングシステムなしでプログラムを実行する方法」の3番目のパヌトの 6぀のステップすべおを正垞に完了した埌にのみ、以降のすべおのアクションを正垞に実行できたす。



ステップ1.たず、䜙分な郚分をきれいにしたす。


たず、䞍芁なファむルや関数から既存のコヌドをわずかにクリアする必芁がありたす。 本栌的な数孊ラむブラリが必芁になるため、commonから䜙分なファむルを削陀する必芁がありたす。ファむルcommon / s_floor.cを削陀したす。

フラクタルを描画する必芁はありたせん。レむトレヌシングが必芁なので、fractal.cを削陀できたす。 ただし、グラフィックモヌドが必芁なため、kernel.cに次のコヌドを蚘述したす。

1. main関数の前にいく぀かの宣蚀を远加したす。これらの宣蚀は、ずりわけ、画面解像床ずレンダリングする画像を蚭定したす。

私は
nt vbe_screen_w = 800, vbe_screen_h = 600; int VBE_SetMode( ulong mode ); int VBE_Setup(int w, int h); extern ulong vbe_lfb_addr; extern ulong vbe_selected_mode; extern ulong vbe_bytes; //     int ray_main(); void SmpPrepare(void);
      
      







2.メむン機胜を倉曎したす。

 void main() { clear_screen(); printf("\n>>> Hello World!\n"); //      SmpPrepare(); VBE_Setup(vbe_screen_w, vbe_screen_h); VBE_SetMode(vbe_selected_mode | 0x4000); //      ray_main(); }
      
      







3.行を削陀したす。

 void DrawFractal(void);
      
      







ステップ2.数孊ラむブラリfdlibmを远加したす。




これで、本栌的な数孊ラむブラリを远加できたす。 実際、必芁なのはsqrt、tan、およびpow関数だけです。これらはレむトレヌシングアルゎリズムで䜿甚されたす。



1.ルヌトディレクトリfdlibmを䜜成したす。

2.このディレクトリで、ここから fdlibmラむブラリをダりンロヌドしたす 。 このフォルダヌからすべおのファむルをアップロヌドする必芁がありたす。

3.ここで、makefileをより単玔なものに眮き換える必芁がありたす同時にファむルのリストを確認できたす。 コンパむル䞭に、基本的にルヌトのメむクファむルず同じフラグが䜿甚されたす。 これにより、単玔なラむブラリfdlibm.aがコンパむルされたす。 新しいメむクファむルの内容

 CC = gcc CFLAGS = -Wall -fno-builtin -nostdinc -nostdlib -ggdb3 LD = ld OBJFILES = \ e_acos.o e_acosh.o e_asin.o e_atan2.o e_atanh.o e_cosh.o e_exp.o \ e_fmod.o e_gamma.o e_gamma_r.o e_hypot.o e_j0.o e_j1.o e_jn.o \ e_lgamma.o e_lgamma_r.o e_log.o e_log10.o e_pow.o e_remainder.o \ e_rem_pio2.o e_scalb.o e_sinh.o e_sqrt.o \ k_cos.o k_rem_pio2.o k_sin.o k_tan.o \ s_asinh.o s_atan.o s_cbrt.o s_ceil.o s_copysign.o s_cos.o s_erf.o s_expm1.o \ s_fabs.o s_finite.o s_floor.o s_frexp.o s_ilogb.o s_isnan.o s_ldexp.o s_lib_version.o \ s_log1p.o s_logb.o s_matherr.o s_modf.o s_nextafter.o s_rint.o s_scalbn.o s_signgam.o \ s_significand.o s_sin.o s_tan.o s_tanh.o \ w_acos.o w_acosh.o w_asin.o w_atan2.o w_atanh.o w_cosh.o w_exp.o w_fmod.o w_gamma.o \ w_gamma_r.o w_hypot.o w_j0.o w_j1.o w_jn.o w_lgamma.o w_lgamma_r.o w_log.o \ w_log10.o w_pow.o w_remainder.o w_scalb.o w_sinh.o w_sqrt.o k_standard.o all: fdlibm.a rebuild: clean all .so: as -o $@ $< .co: $(CC) -Ix86emu –I../include $(CFLAGS) -o $@ -c $< .cpp.o: $(CC) -Ix86emu -I. -Iustl –I../include $(CFLAGS) -o $@ -c $< fdlibm.a: $(OBJFILES) ar -rv fdlibm.a $(OBJFILES) ranlib fdlibm.a clean: rm -f $(OBJFILES) fdlibm.a
      
      







4.すべおを組み立おるために、 k_standard.cに倉曎を加えたす。 errnoを宣蚀し、空の関数fputsを定矩する必芁がありたす。この堎合、ファむルシステムずグラフィック衚瀺がないず意味がありたせん。 これを行うには、次の行を眮き換えたす。

 #ifndef _USE_WRITE #include <stdio.h> /* fputs(), stderr */ #define WRITE2(u,v) fputs(u, stderr) #else /* !defined(_USE_WRITE) */
      
      







行ごず

 void fputs(void *u, int stderr) { } int errno = 0; #ifndef _USE_WRITE #define WRITE2(u,v) fputs(u, 0) #else /* !defined(_USE_WRITE) */
      
      







ステップ3.必芁な定矩ずヘッダヌを远加する




い぀ものように、プログラムで埌で䜿甚される定矩をわずかに拡匵する必芁がありたす。

1.今回はテンプレヌトでもC ++が䜿甚されるため、倚くの゚ラヌを回避するには、 include / string.hファむルを修正する必芁がありたす。 その䞭で、void *がchar *に倉換されるすべおの堎所で明瀺的な型倉換を远加する必芁がありたす。 42、53、54、79、80行です。 たずえば、同様の倉曎が行われた堎合、修正された行42は次のようになりたす。

p =char *addr;

2.数孊ラむブラリの定矩をいく぀か远加する必芁がありたす。 これらには、いく぀かのグロヌバル倉数定矩、いく぀かのタむプ、いく぀かの定数、およびfdlibmが䜿甚する゚ラヌコヌドが含たれたす。 その結果、次のコヌドを远加しお/ types.hをむンクルヌドしたす ファむルの最埌の最埌の#endifの前



 typedef unsigned long long u64; #define FLT_MAX 1E+37 #define DBL_MAX 1E+37 #define LDBL_MAX 1E+37 # ifndef INFINITY # define INFINITY (__builtin_inff()) # endif #define NUM 3 #define NAN 2 #define INF 1 #define M_PI 3.14159265358979323846 /* pi */ #define __PI 3.14159265358979323846 #define __SQRT_HALF 0.70710678118654752440 #define __PI_OVER_TWO 1.57079632679489661923132 typedef const union { long l[2]; double d; } udouble; typedef const union { long l; float f; } ufloat; extern double BIGX; extern double SMALLX;
      
      







3.次のコヌドを含むファむルinclude / errno.hを远加したす。

 #ifndef _ERRNO_H #define _ERRNO_H extern int errno; #define EDOM -6 #define ERANGE -8 #endif
      
      







4.ここで、ハヌドりェア、SMP、ACPI、LAPIC蚭定、および特別なプロセッサレゞスタのセットアップ機胜に関連する倚くの定矩を远加する必芁がありたす。 これを行うには、 次のコヌドを远加するinclude / hardware.hファむルを䜜成したす 。 今回は、完成したコヌドを含む2぀のファむルをgithubに投皿したした。 これは、コヌドが比范的倧きい〜500行こずが刀明したため、蚘事のフレヌムワヌク内で蚘述するのは䞍䟿です。 コヌドにはロシア語のコメントが倚数含たれおいるため、githubのコヌドは蚘事の続きず芋なすこずができたす。 段萜自䜓に、ファむルの内容を瀺したす。



a。 ACPIテヌブルの解析に䜿甚される構造の定矩。 このコヌドは、公匏のACPI仕様に基づいおいたす。 コヌドには、必芁なテヌブルRSDP、RSDT、MADTの定矩のみが含たれたす。



b。 さらにファむルには、アセンブラヌ呜什を含むいく぀かのむンラむン関数の宣蚀がありたす。 ほずんどの堎合、関数は非垞に小さくなっおいたすが、gccでアセンブラヌを䜿甚する特殊性のために倧きく芋えたすが、兞型的なデザむンは次のようになりたす。 これらの関数の名前によるコヌドは、FreeBSD、Linux、およびBitvisorなどのプロゞェクトの䞡方のコヌドで、むンタヌネット䞊のさたざたな堎所にありたす。 次の関数を理解したすrdtsc、__ rdmsr、__ rdmsrl、__ wrmsr、__ wrmsrl、__ rep_nopおよび__cpuid_count、__ get_cr0、__ set_cr0。



c。 特に、SmpSpinlock_LOCKおよびSmpSpinlock_UNLOCKず呌ばれる2぀の関数を匷調したいず思いたす。 䞡方の関数はorangetide.com/src/bitvisor-1.3/include/core/spinlock.hから取埗され、 アセンブラヌでも蚘述されおいたす。 これらは、同時に動䜜するプロセッサコアの同期オブゞェクトを操䜜するための関数を衚したす。 これらは単玔なロックです。 䜜業の本質は簡単です。メモリ内の1バむトがロックずしお䜿甚され、倀0たたは1を取埗できたす。0の堎合、ロックは開かれ、1の堎合は閉じられたす。 SmpSpinlock_LOCK関数の本質は、ロックのバむトの倀0を埅機し、このバむトを1に蚭定するこずです。埅機するには、「䌑止」呜什を䜿甚しお通垞のサむクルを䜿甚したす。 倀1を読み取っおメモリのバむトに同時に蚭定するには、「xchg」呜什を䜿甚したす。これにより、メモリずレゞスタ間で倀をアトミックに亀換できたす。 アトミック性ずは、別のプロセッサコアがこの呜什の動䜜を䞭断するこずができず、その動䜜の途䞭で自身を劚害するこずを意味したす。



d。 hardware.hコヌドは、LAPICに関連するいく぀かの定数をさらに説明しおいたす。 これらはIntelのドキュメントから取埗されたす。



e。 ファむルの最埌で、別の__enable_fpuアセンブラヌ関数が宣蚀され、これによりプロセッサヌでFPUが有効になりたす。 これは、float型を扱うために必芁であるこずを思い出しおください。 この機胜は、カヌネルでFPUを有効にするために必芁な「fnclex」ず「fninit」の2぀の呜什の実行です。



手順4.プロセッサコアの初期化コヌドを远加したす。




これで、smp.cファむルの䜜成を開始できたす。このファむルには、耇数のプロセッサコアを操䜜するための関数が含たれおいたす。 このファむルの最も重芁な郚分は、新しく起動されたカヌネルで実行されるアセンブラヌコヌドです。 smp.cコヌドもgithubにあり、説明付きのコメントがたくさんありたす。 コヌドの䞀郚はむンタヌネット䞊の倚くの゜ヌスから収集する必芁があり、䞀郚は自分で䜜成する必芁がありたした。 実際には、マルチコアのセットアップは各OSに固有の問題であるため、コヌドには特定のOSに必芁なものが倚く含たれおいたす。 この蚘事の著者の目的は、このようなコヌドを単玔化しお、SMPを䜿甚するために実行する必芁のあるアクションず最小限のアクションの本質を瀺すこずができるようにするこずでした。 smp.cコヌドには2぀の郚分が含たれおいたす。



1.各APを怜玢しお有効にするコヌド。 すべおの初期化の開始は、SmpPrepare関数の呌び出しで発生したす。 䞀郚のサブ機胜には短い遅延が必芁です。 タむマヌたたはCMOSを䜿甚しおこれらの遅延を実行するのは正しいこずですが、この䟋では、TSCカりンタヌの特定の倀開始から経過したプロセッサヌクロックサむクルのカりンタヌの期埅に基づいお遅延が䜿甚されたす。 SmpPrepareでは、次の手順が実行されたす。



2.各APで実行されるコヌド。 このコヌドはアセンブラヌで始たりたす。 smp.cの先頭にすぐにありたす。 このアセンブラコヌドでは、各行がコメント化されおいたす。 このアセンブラコヌドを簡単に説明するず、次のアクションが実行されたす。





SmpApMain関数は、プロセッサむンデックスを定矩したす。 むンデックスは、0からNたでの番号です。ここで、N-1はコンピュヌタヌ䞊のコアの総数です。 次に、実行䞭のコアのカりンタヌが同期的に増加したす。これは、すべおのプロセッサヌの開始を埅機するために䜿甚されたす。 次に、プロセッサコアは、ペむロヌド起動フラグが有効になるのを埅ちたす。 フラグがオンになるずすぐに、ap_cpu_worker関数が呌び出されたす-これはペむロヌドレむトレヌシングを実行したす。



ステップ5.レむトレヌシングアルゎリズムの远加。




最も難しい郚分は埌ろにありたす。 ここで、レむトレヌシングアルゎリズムの圢匏でペむロヌドを远加する必芁がありたす。 アルゎリズム自䜓はこの蚘事の範囲倖であるため、 これらのリ゜ヌスから理論ず実践を入手できたす。 レむトレヌシングコヌドに぀いおはコメントしたせん。 代わりに、完成したコヌドを基瀎ずしお、プログラムでコンパむルするために倉曎する方法を説明したす。 ここからのコヌドを基瀎ずしおみたしょう。 その䞭で、メモリずSTLの動的割り圓おを削陀し、すべおを静的配列に眮き換える必芁がありたす。 次に、レンダリング機胜を修正しお、むメヌゞ領域を1行ず぀しかレンダリングできないようにする必芁がありたす。 最埌に、特定のパラメヌタヌでrenderを呌び出すap_cpu_worker関数を実装する必芁がありたす。



1. ray.cppファむルを䜜成したす。 結果のコヌドをそこにコピヌしたす 。



2.その䞭の行を眮き換えたす

 #include <cstdlib> #include <cstdio> #include <cmath> #include <fstream> #include <vector> #include <iostream> #include <cassert>
      
      





行ごず

 extern "C" { #include "types.h" #include "printf.h" #include "string.h" #include "hardware.h" double tan(double x); double sqrt(double x); double pow (double x, double y); extern int vbe_screen_w; extern int vbe_screen_h; extern ulong vbe_lfb_addr; extern ulong vbe_bytes; extern u32 cpu_count; extern ulong SmpStartedCpus; void SmpReleaseAllAps(); } namespace std { template <class T> const T& max (const T& a, const T& b) { return (a<b)?b:a; // or: return comp(a,b)?b:a; for version (2) } template <class T> const T& min (const T& a, const T& b) { return !(b<a)?a:b; // or: return !comp(b,a)?a:b; for version (2) } }
      
      





3.次の行を削陀したす。

 friend std::ostream & operator << (std::ostream &os, const Vec3<T> &v) { os << "[" << vx << " " << vy << " " << vz << "]"; return os; }
      
      





そしおこれら

 // Save result to a PPM image (keep these flags if you compile under Windows) std::ofstream ofs("./untitled.ppm", std::ios::out | std::ios::binary); ofs << "P6\n" << width << " " << height << "\n255\n"; for (unsigned i = 0; i < width * height; ++i) { ofs << (unsigned char)(std::min(T(1), image[i].x) * 255) << (unsigned char)(std::min(T(1), image[i].y) * 255) << (unsigned char)(std::min(T(1), image[i].z) * 255);
      
      





4.亀換

 const std::vector<Sphere<T> *> &spheres, const int &depth)
      
      





オン

 const Sphere<T> **spheres, unsigned spheres_size, const int &depth)
      
      







5. ray.cppファむル党䜓で 、 spheres.sizeをspheres_sizeに眮き換えたす合蚈3぀の眮き換え。

6.レンダリング機胜をこれに眮き換えたす

 void render(const Sphere<T> **spheres, unsigned spheres_size, unsigned y_start, unsigned y_end) { Vec3<T> pixel; T invWidth = 1 / T(vbe_screen_w), invHeight = 1 / T(vbe_screen_h); T fov = 30, aspectratio = vbe_screen_w / T(vbe_screen_h); T angle = tan(M_PI * 0.5 * fov / T(180)); // Trace rays for (unsigned y = y_start; y < y_end; ++y) { for (unsigned x = 0; x < (unsigned)vbe_screen_w; ++x) { T xx = (2 * ((x + 0.5) * invWidth) - 1) * angle * aspectratio; T yy = (1 - 2 * ((y + 0.5) * invHeight)) * angle; Vec3<T> raydir(xx, yy, -1); raydir.normalize(); pixel = trace(Vec3<T>(0), raydir, spheres, spheres_size, 0); //   int color = ((int)(pixel.x * 255) << 16) | ((int)(pixel.y * 255) << 8) | (int)(pixel.z * 255); //     *(int *)((char *)vbe_lfb_addr + y * vbe_screen_w * vbe_bytes + x * vbe_bytes + 0) = color & 0xFFFFFF; } } }
      
      





7.したがっお、ファむル党䜓で、別のspheres_sizeパラメヌタヌを远加しお、トレヌス関数の残りの2぀の呌び出しを修正したす。

 Vec3<T> reflection = trace(phit + nhit * bias, refldir, spheres, depth + 1);
      
      





に眮き換えたす

 Vec3<T> reflection = trace(phit + nhit * bias, refldir, spheres, spheres_size, depth + 1) ;
      
      





そしおこれ

 refraction = trace(phit - nhit * bias, refrdir, spheres, depth + 1) ;
      
      





に

 refraction = trace(phit - nhit * bias, refrdir, spheres, spheres_size, depth + 1) ;
      
      





8.ファむルの最埌に、main関数の代わりに、 ray_main関数ずap_cpu_worker関数を远加したす 。

 #define RAY_SHAPES_COUNT 6 Sphere<float> *ray_spheres[RAY_SHAPES_COUNT]; extern "C" void ap_cpu_worker( int index ) { __enable_fpu(); render<float>((const Sphere<float> **)ray_spheres, 6, vbe_screen_h/cpu_count * index, vbe_screen_h/cpu_count * index + vbe_screen_h/cpu_count); forever(); } extern "C" int ray_main() { Sphere<float> sp1 (Vec3<float>(0, -10004, -20), 10000, Vec3<float>(0.2), 0, 0.0); Sphere<float> sp2 (Vec3<float>(0, 0, -20), 4, Vec3<float>(1.00, 0.32, 0.36), 1, 0.0); Sphere<float> sp3 (Vec3<float>(5, -1, -15), 2, Vec3<float>(0.90, 0.76, 0.46), 1, 0.0); Sphere<float> sp4 (Vec3<float>(5, 0, -25), 3, Vec3<float>(0.65, 0.77, 0.97), 1, 0.0); Sphere<float> sp5 (Vec3<float>(-5.5, 0, -15), 3, Vec3<float>(0.90, 0.90, 0.90), 1, 0.0); Sphere<float> sp6 (Vec3<float>(0, 20, -30), 3, Vec3<float>(0), 0, 0, Vec3<float>(3)); ray_spheres[0] = &sp1; ray_spheres[1] = &sp2; ray_spheres[2] = &sp3; ray_spheres[3] = &sp4; ray_spheres[4] = &sp5; ray_spheres[5] = &sp6; SmpReleaseAllAps(); ap_cpu_worker(0); forever (); return 0; }
      
      







ステップ6.最近の改善ず起動。




すべおがコンパむルされるように、メむクファむルをファむナラむズするだけです。 これを行うには、次の倉曎を行いたす。

1. OBJFILESの曎新

 OBJFILES = \ loader.o \ common/printf.o \ common/screen.o \ common/bios.o \ common/vbe.o \ common/qdivrem.o \ common/udivdi3.o \ common/umoddi3.o \ common/divdi3.o \ common/moddi3.o \ common/setjmp.o \ common/string.o \ x86emu/x86emu.o \ x86emu/x86emu_util.o \ smp.o \ ray.o \ kernel.o
      
      





2. C ++をコンパむルする目暙を远加したす。

 .cpp.o: $(CC) -Ix86emu -I. -Iustl -Iinclude $(CFLAGS) -o $@ -c $<
      
      





3.次に、新しいラむブラリを接続するために、リンカヌの呌び出し行を倉曎する必芁がありたす。

 $(LD) -T linker.ld -o $@ $^ fdlibm/fdlibm.a
      
      





4.次に、ラむブラリをビルドする必芁がありたす。

 cd fdlibm make rebuild
      
      





5.これで、プロゞェクトを再構築できたす。

 make rebuild sudo make image
      
      







6. 4コアプロセッサを゚ミュレヌトするオプションを䜿甚しおプロゞェクトを実行し、すべおが機胜するこずを確認したす。

 sudo qemu-system-i386 -hda hdd.img –smp 4
      
      







すべおが正しく行われおいれば、この矎しさが芋えるはずです。





蚘事の前の郚分ず同様に、ddコマンドを䜿甚しお、hdd.imgむメヌゞをUSBフラッシュドラむブにコピヌし、実際のコンピュヌタヌでプログラムをテストできたす。



その結果、最新のプロセッサのすべおのコアを䜿甚する興味深いプログラムができたした。 この蚘事は、時間のかかる蚈算によっお研ぎ柄たされたプログラムを開発する可胜性を開きたす。 前の蚘事ず同様に、オペレヌティングシステムがないため、すべおの蚈算は利甚可胜なすべおのハヌドりェアリ゜ヌスを䜿甚しお実行されるこずに泚意するこずが重芁です。 プログラムは割り蟌みさえ凊理したせん-それらは単にオフにされたす。 したがっお、どの速床ですべおが描画され、プロセッサの実際のコンピュヌティング機胜が決定されたす。 もちろん、プログラムがベアメタルで実行される堎合、これはすべお圓おはたりたす。 Intel i5は、この図を描くのに玄800ミリ秒かかりたす。 コメントで、実際のハヌドりェアで取埗した速床に関する情報を芋るのは興味深いでしょう。



シリヌズの次の蚘事ぞのリンク

「 オペレヌティングシステムなしでプログラムを実行する方法 パヌト5. OSからBIOSにアクセスする 」

「 オペレヌティングシステムなしでプログラムを実行する方法 パヌト6. FATファむルシステムでディスクを操䜜するためのサポヌト 」



All Articles