當前位置:編程學習大全網 - 源碼下載 - 過程通信源代碼

過程通信源代碼

Windows是壹種閉源商業軟件,受商業版權法保護。別說沒人有,就算有也不能給妳(被微軟起訴可不是小事)。

Linux是開源的,為什麽不去讀Linux的源代碼?

新版本太長,寫不下去。我給妳最經典的0.11版本的流程管理源代碼。剩下的妳自己查。作為壹名優秀的程序員,妳必須學會充分利用搜索引擎。

-

Linux0.11通過進程數組管理進程,最多允許64個進程。進程的狀態有就緒、運行、暫停、睡眠和死亡。睡眠狀態可以分為可中斷和不可中斷。對於流程的管理,我覺得根據流程的狀態會更清晰。

任務1.0

0號比較特殊,是“純手工”的,下面是制作過程。

mem_init(主_內存_開始,內存_結束);

trap_init()。

blk _ dev _ init();

char _ dev _ init();

tty _ init();

time _ init();

sched _ init();

buffer_init(緩沖區_內存_結束);

HD _ init();

floppy_init()。

STI();

move _ to _ user _ mode();

當然,這麽多init並不都是為0任務準備的,他們是在初始化系統。對於任務0,sched_init()和move_to_user_mode()很重要。Sched_init()手動設置任務0的任務段描述符tss和本地段描述符ldt,並設置tr和ldtr寄存器:

set_tss_desc(init _ task . task . TSS));//設置tss段描述符

set _ ldt _ desc(gdt+FIRST _ LDT _ ENTRY & amp;(init _ task . task . ldt));//設置ldt描述符

...

ltr(0);//將tss描述符地址加載到tr

lldt(0);//將ldt描述符地址加載到ldtr

我們來看看任務0的tss和ldt是什麽樣子的。

/*ldt*/ {0,0},\

{0x9f,0xc0fa00},\

{0x9f,0xc0f200},\

/*tss*/{0,PAGE _ SIZE+(long)& amp;init_task,0x10,0,0,0,0,(long)& amp;pg_dir,\

0,0,0,0,0,0,0,0,\

0,0,0x17,0x17,0x17,0x17,0x17,\

_LDT(0),0X80000000,\

{}\

},\

從ldt可以看出,任務的代碼和數據段都是640k,基址是0,DPL=3。這說明task 0的代碼雖然還在內核段,但是task的級別已經在用戶模式了。這也可以從tss中看出,其中代碼和數據都設置為0x17,這是本地段描述符表中的第二項。還可以看到task 0的內核棧設置在init_task之後的壹頁,用戶態棧是move_to_user_mode之前的內核態棧。這與普通進程不同,普通進程的用戶模式堆棧位於其64Mb地址空間的末端。

Move_to_user_mode是在堆棧中創建任務開關的假象。用iret跳轉到外層3,這樣cpu會根據tr自動加載tss,初始化各個寄存器運行任務0。因此,任務0實際上是內核空間中的用戶態任務。

2.流程創建

進程創建由系統調用sys_fork完成,主要使用find_empty_process和copy_process兩個函數。前者在進程數組中為子進程找到壹個未使用的進程號,後者完成子進程信息的創建,主要是復制父進程的信息。

我們來看看copy_process的代碼:

int copy_process(int nr,long ebp,long edi,long esi,long gs,long none,

長ebx,長ecx,長edx,長fs,長es,長ds,

長eip、長cs、長eflags、長esp、長ss)

{

struct task _ struct * p;

int I;

結構文件* f;

//首先,為子進程的進程描述符分配壹塊內存。

p =(struct task _ struct *)get _ free _ page();

如果(!p)

return-EAGAIN;

//將新任務結構指針添加到數組中。

task[NR]= p;

//將當前用戶的任務結構復制到子進程中。當然,堆棧不會復制。

* p = *電流;

//將子進程的狀態設置為不間斷等待,以防止它現在被調度。

p->;state = TASK _不間斷;

p->;pid = last _ pid

p->;父親=當前-& gt;pid

p->;count = p-& gt;優先級;

p->;信號= 0;

p->;報警= 0;

p->;leader = 0;

p->;utime = p-& gt;stime = 0;

p->;cutime = p-& gt;cstime = 0;

p->;start _ time = jiffies

p->;TSS . back _ link = 0;

//新進程的內核狀態堆棧在進程描述符頁的末尾。

p->;TSS . esp0 = PAGE _ SIZE+(long)p;

p->;TSS . ss0 = 0x 10;

//ip為父進程調用fork的下壹條指令。

p->;tss.eip = eip

fork的返回值對於子進程為0,對於父進程為其pid。通過這種差異,在fork調用返回後,父進程和子進程的代碼段被分割。

p->;TSS . eax = 0;

//盡管它們是在代碼文件中編寫的。

p->;tss.ecx = ecx

p->;tss.edx = edx

p->;tss.ebx = ebx

p->;tss.esp = esp

p->;tss.ebp = ebp

p->;tss.esi = esi

p->;tss.edi = edi

p->;tss.es = es & amp0xffff

p->;tss.cs = cs & amp0xffff

p->;= ss & amp0xffff

p->;tss.ds = ds & amp0xffff

p->;= fs & amp0xffff

p->;tss.gs = gs & amp0xffff

p->;TSS . ldt = _ LDT;

p->;tss.trace _ bitmap = 0x80000000

//如果父任務使用了協處理器,則保存在tss中。

if(last _ task _ used _ math = =當前)

_ ASM(" clts;fn save % 0 "::" m "(p-& gt;TSS . i387));

//為新任務設置新的代碼和數據段基址。註意因為linux0.11只支持64M的進程空間,所以進程的線性地址空間在64M的邊界。

//然後為新任務復制父進程的頁表。通過copy_page_tales,父子任務此時使用的是只讀代碼數據段。寫入時,寫入時復制將為新進程申請新的物理頁面。

if(copy_mem(nr,p)){

task[NR]= NULL;

free_page((長)p);

return-EAGAIN;

}

for(I = 0;我& ltNR _ OPENi++)

if(f = p-& gt;filp)

f-& gt;f _ count++;

如果(當前-& gt;pwd)

當前->;pwd-& gt;I _ count++;

如果(當前-& gt;根)

當前->;root-& gt;I _ count++;

如果(當前-& gt;可執行)

當前->;可執行-& gt;I _ count++;

//為新任務設置tss和ldt描述符。

set _ TSS _ desc(gdt+(NR & lt;& lt1)+FIRST _ TSS _ ENTRY & amp;(p->;TSS));

set _ ldt _ desc(gdt+(NR & lt;& lt1)+FIRST _ LDT _ ENTRY;(p->;ldt));

//返回子進程的pid。

返回last _ pid

}

參數都是棧中的值,nr是調用copy_process之前find_empty_process的返回值,是任務數組的序號。

3.進程調度

流程在創建後不會立即執行。系統將在適當的時間調用系統調度函數schedule,並選擇適當的進程來運行。調度函數可能在很多地方被調用,比如系統調用、進程暫停/休眠/退出、時鐘中斷等。調度功能主要是通過進程的時間片選擇壹個運行時間最短的進程。如果沒有找到,運行空閑暫停系統調用,在暫停中,調度函數會被再次調用。以下是主要代碼

while(1){

c =-1;

next = 0;

i = NR _ TASKS

p = & amp任務[NR _ TASKS];

//在就緒任務中找出最短的運行任務。

while( - i){

如果(!(* - p))

繼續;

if((* p)-& gt;狀態= =任務運行& amp& amp(* p)-& gt;計數器& gtc)

c =(* p)-& gt;計數器,next = I;

}

//如果找到,退出。否則,重新計算任務的時間片。註意,如果沒有任務,next總是0,結果切換到任務0。

如果(c)斷裂;

for(p = & amp;LAST _ TASKp & gt& ampFIRST _ TASK- p)

如果(*p)

(* p)-& gt;counter =((* p)-& gt;計數器& gt& gt1)+(*)-& gt;優先級;

}

switch_to(下壹個);//通過long jump將任務轉換為新任務。

}

時鐘中斷是執行調度的重要手段。正是由於調度的不斷執行,系統才能在多任務間切換,完成多任務操作系統的目標。當調度程序初始化時,除了手動設置任務0,它還啟動8253的定時器中斷,並“點燃”系統。do_timer函數在中斷中被調用。這是壹個非常短的函數:

void do_timer(長cpl)

{

...

//根據優先級調整進程運行時間。

中頻(cpl)

當前->;utime++;

其他

當前->;stime++;

//處理定時器中斷隊列

if(next_timer){

next _ timer-& gt;jiffies-;

while(next _ timer & amp;& ampnext _ timer-& gt;jiffies & lt=0){

void(* fn)(void);

fn = next _ timer-& gt;fn;

next _ timer-& gt;fn = NULL

next _ timer = next _ timer-& gt;接下來;

(fn)();

}

}

..

//進程時間片減去1。如果大於0,則表示該時間片尚未用完。繼續。

如果((-當前-& gt;計數器& gt0)返回;

//註意,內核態的任務是不能被搶占的。在0.11中,除非內核任務主動放棄處理器,否則會壹直運行。

如果(!cpl)返回;

//調度

調度();

}

4.過程的終止

該過程可以自行停止,也可以由停止信號停止。Do_exit執行退出。如果它有壹個子進程,那麽這個子進程就會變成壹個孤兒進程,它的子進程會委托給進程1(也就是init進程)進行管理。變成僵屍進程後,妳也要通知父進程,因為他的父進程可能在等它。

  • 上一篇:Redis、Kafka或RabbitMQ:哪個作為微服務消息代理最合適?
  • 下一篇:群英問源碼
  • copyright 2024編程學習大全網