九色国产,午夜在线视频,新黄色网址,九九色综合,天天做夜夜做久久做狠狠,天天躁夜夜躁狠狠躁2021a,久久不卡一区二区三区

打開APP
userphoto
未登錄

開通VIP,暢享免費電子書等14項超值服

開通VIP
linux進(jìn)程及進(jìn)程控制

http://blog.csdn.net/kennyrose/article/details/7533534

2012  

程序是一組可執(zhí)行的靜態(tài)指令集,而進(jìn)程(process)是一個執(zhí)行中的程序?qū)嵗?。利用分時技術(shù),在linux操作系統(tǒng)上同時可以運行多個進(jìn)程。分時技術(shù)的基本原理是把CPU的運行時間劃分成一個個規(guī)定長度的時間片,讓每個進(jìn)程在一個時間片內(nèi)運行。當(dāng)進(jìn)程的時間片用完時系統(tǒng)就利用調(diào)度程序切換到另一個進(jìn)程去運行。因此實際上對于具體單個CPU的機器來說某一個時刻只能運行一個進(jìn)程。但由于每個進(jìn)程運行的時間片很短(例如15個系統(tǒng)滴答=150ms),所以表面看起來好像所有進(jìn)程在同時運行著。

  對于Linux0.11內(nèi)核來講,系統(tǒng)最多可由64個進(jìn)程同時存在。除了第一個進(jìn)程是"手工"建立以外,其余的都是進(jìn)程使用系統(tǒng)調(diào)用fork創(chuàng)建的新進(jìn)程,被創(chuàng)建的進(jìn)程成為子進(jìn)程(Child Process),創(chuàng)建者,則稱為父進(jìn)程(parent process)。內(nèi)核程序使用進(jìn)程標(biāo)識號(process ID,pid)來標(biāo)識每個進(jìn)程。進(jìn)程由可執(zhí)行的指令代碼,數(shù)據(jù)和堆棧區(qū)組成。進(jìn)程中的代碼和數(shù)據(jù)部分分別對應(yīng)一個可執(zhí)行文件中的代碼段,數(shù)據(jù)段。每個進(jìn)程只能執(zhí)行自己的代碼和訪問自己的數(shù)據(jù)及堆棧區(qū)。進(jìn)程之間相互之間的通信需要通過系統(tǒng)調(diào)用來進(jìn)行。對于只有一個CPU的系統(tǒng),在某一個時刻只能有一個進(jìn)程正在運行。內(nèi)核通過進(jìn)程調(diào)度程序分時調(diào)度各個進(jìn)程運行。

  Linux系統(tǒng)中,一個進(jìn)程可以在內(nèi)核態(tài)(Kernal mode)或者用戶態(tài)(user mode)下執(zhí)行,因此Linux內(nèi)核堆棧和用戶堆棧是分開的。用戶堆棧用于進(jìn)程在用戶態(tài)下臨時保存調(diào)用函數(shù)的參數(shù),局部變量等數(shù)據(jù)。內(nèi)核堆棧則含有內(nèi)核程序執(zhí)行函數(shù)調(diào)用時的信息。


1.1任務(wù)數(shù)據(jù)結(jié)構(gòu)

  內(nèi)核程序通過進(jìn)程表對進(jìn)程進(jìn)行管理,每個進(jìn)程在進(jìn)程表中占有一項。在Linux系統(tǒng)中,進(jìn)程表項是一個task_struct任務(wù)結(jié)構(gòu)指針。任務(wù)數(shù)據(jù)結(jié)構(gòu)定義在頭文件include/linux/sched.h中。有些書上稱其為進(jìn)程控制塊PCB(Process Control Block)或者進(jìn)程描述符PD(Processor Descriptor)。其中保存著用于控制和管理進(jìn)程的所有信息。主要包括進(jìn)程當(dāng)前運行的狀態(tài)信息,信號,進(jìn)程號,父進(jìn)程號,運行時間累計值,正在使用的文件和本任務(wù)的局部描述符以及任務(wù)狀態(tài)段信息。該結(jié)構(gòu)每個字段的含義如下所示。




當(dāng)一個進(jìn)程在執(zhí)行時,CPU的所有寄存器中的值,進(jìn)程的狀態(tài)以及堆棧中的內(nèi)容被稱為該進(jìn)程的上下文。當(dāng)內(nèi)核需要切換(switch)至另一個進(jìn)程時,它就需要保存當(dāng)前進(jìn)程的所有狀態(tài),也即保存當(dāng)前進(jìn)程的上下文,以便在再次執(zhí)行該進(jìn)程時,能夠恢復(fù)到切換時的狀態(tài)執(zhí)行下去。在Linux中,當(dāng)前進(jìn)程上下文均保存在進(jìn)程的任務(wù)數(shù)據(jù)結(jié)構(gòu)task_struct中。在發(fā)生中斷時,內(nèi)核就在被中斷進(jìn)程的上下文中,在內(nèi)核狀態(tài)下執(zhí)行中斷服務(wù)例程。但同時會保留所有需要用到的資源,以便中斷服務(wù)結(jié)束時能恢復(fù)被中斷進(jìn)程的執(zhí)行。


1.2 進(jìn)程運行狀態(tài)

  一個進(jìn)程在其生存期內(nèi),可處于一組不同的狀態(tài)下,稱為進(jìn)程狀態(tài)。見下圖2-6所示。進(jìn)程狀態(tài)保存在進(jìn)程任務(wù)結(jié)構(gòu)的state字段中。當(dāng)進(jìn)程正在等待系統(tǒng)中的資源而處于等待狀態(tài)時,則稱其處于睡眠等待狀態(tài)。在Linux系統(tǒng)中,睡眠等待狀態(tài)被分為可中斷的和不可中斷的等待狀態(tài)。


運行狀態(tài)(TASK_RUNNING)

  當(dāng)進(jìn)程正在被CPU執(zhí)行,或已經(jīng)準(zhǔn)備就緒隨時可以由調(diào)度程序執(zhí)行,則稱該進(jìn)程為處于運行狀態(tài)(running)。進(jìn)程可以在內(nèi)核態(tài)運行,也可以在用戶態(tài)運行。當(dāng)系統(tǒng)資源已經(jīng)可用時,進(jìn)程就被喚醒而進(jìn)入準(zhǔn)備運行狀態(tài),該狀態(tài)稱為就緒態(tài)。這些狀態(tài)在內(nèi)核中表示方法相同,都被稱為處于TASK_RUNNING狀態(tài)。


可中斷睡眠狀態(tài)(TASK_INTERRUPTIBLE)

  當(dāng)進(jìn)程處于可中斷等待狀態(tài)時,系統(tǒng)不會調(diào)度該進(jìn)程執(zhí)行。當(dāng)系統(tǒng)產(chǎn)生一個中斷或者釋放了進(jìn)程正在等待的資源,或者進(jìn)程收到一個信號,都可以喚醒進(jìn)程轉(zhuǎn)換到就緒狀態(tài)(運行狀態(tài))。


不可中斷睡眠狀態(tài)(TASK_UNINTERRUPTIBLE)

  與可中斷睡眠狀態(tài)類似。但處于該狀態(tài)的進(jìn)程只有被使用wake_up()函數(shù)明確喚醒時才能被轉(zhuǎn)換到可運行就緒狀態(tài)。


暫停狀態(tài)(TASK_STOPPED)

  當(dāng)進(jìn)程收到信號SIGSTOP,SIGTSTP,SIGTTIN或SIGTTOU時就會進(jìn)入暫停狀態(tài)。可向其發(fā)送SIGCONT信號讓進(jìn)程轉(zhuǎn)換到可運行狀態(tài)。在Linux0.11中,還為實現(xiàn)對該狀態(tài)的轉(zhuǎn)換處理。處于該狀態(tài)的進(jìn)程將被作為進(jìn)程終止來處理。


僵死狀態(tài)(TASK_ZOMBIE)

  當(dāng)進(jìn)程已停止運行,但其父進(jìn)程還沒有詢問其狀態(tài)時,則稱該進(jìn)程處于僵死狀態(tài)。


當(dāng)一個進(jìn)程的運行時間片用完,系統(tǒng)就會使用調(diào)度程序強制切換到其他的進(jìn)程去執(zhí)行。另外,如果進(jìn)程在內(nèi)核態(tài)執(zhí)行時需要等待系統(tǒng)的某個資源,此時該進(jìn)程就會調(diào)用sleep_on()或者sleep_on_interruptible()自愿放棄CPU使用權(quán),而讓調(diào)度程序去執(zhí)行其他程序。進(jìn)程則進(jìn)入睡眠狀態(tài)(TASK_UNINTERRUPTIBLE或TASK_INTERRUPTIBLE)。

  只有當(dāng)進(jìn)程從"內(nèi)核運行態(tài)"轉(zhuǎn)移到"睡眠狀態(tài)"時,內(nèi)核才會進(jìn)行進(jìn)程切換操作。在內(nèi)核態(tài)下運行的進(jìn)程不能被其他進(jìn)程搶占,而且一個進(jìn)程不能改變另一個進(jìn)程的狀態(tài)。為了避免進(jìn)程切換時造成內(nèi)核數(shù)據(jù)錯誤,內(nèi)核在執(zhí)行臨街區(qū)代碼時禁止一切中斷。


1.3 進(jìn)程初始化

  在boot/目錄中引導(dǎo)程序把內(nèi)核從磁盤上加載到內(nèi)存中,并讓系統(tǒng)進(jìn)入保護(hù)模式下運行后,就開始執(zhí)行系統(tǒng)初始化程序init/main.c。該程序首先確定如何分配使用系統(tǒng)物理內(nèi)存,然后調(diào)用內(nèi)核各部分的初始化函數(shù)分別對內(nèi)存管理,中斷處理,塊設(shè)備和字符設(shè)備,進(jìn)程管理以及硬盤和軟盤硬件進(jìn)行初始化處理。在完成了這些操作之后,系統(tǒng)各部分已經(jīng)處于可運行狀態(tài)。此后程序把自己"手工"移動到任務(wù)0(進(jìn)程0)中運行,并使用fork()調(diào)用首次創(chuàng)建出進(jìn)程1。在進(jìn)程1種程序?qū)⒗^續(xù)進(jìn)行應(yīng)用環(huán)境的初始化并執(zhí)行shell登陸程序。而原進(jìn)程0則會在系統(tǒng)空閑時被調(diào)度執(zhí)行,此時任務(wù)0僅執(zhí)行pause()系統(tǒng)調(diào)用,并又會調(diào)用調(diào)度函數(shù)。

  "移動到任務(wù)0種執(zhí)行"這個過程由宏move_to_user_mode(include/asm/system.h)完成。它把main.c程序執(zhí)行流從內(nèi)核態(tài)(特權(quán)級0)移動到了用戶態(tài)(特權(quán)級3)的任務(wù)0種繼續(xù)運行。在移動之前,系統(tǒng)在對調(diào)度程序的初始化過程(sched_init())中,首先對任務(wù)0的運行環(huán)境進(jìn)行的設(shè)置。這包括人工預(yù)先設(shè)置好 任務(wù)0數(shù)據(jù)結(jié)構(gòu)各字段的值(include/linux/shed.h),在全局描述符中添入任務(wù)0的任務(wù)狀態(tài)段(TSS)描述符和局部描述符表(LDT)的段描述符,并把它們分別加載到任務(wù)寄存器tr和局部描述符表寄存器ldtr中。

  這里需要強調(diào)的是,內(nèi)核初始化是一個特殊過程,內(nèi)核初始化代碼也即是任務(wù)0的代碼。從任務(wù)0數(shù)據(jù)結(jié)構(gòu)中設(shè)置的初始化數(shù)據(jù)可知,任務(wù)0的代碼段和數(shù)據(jù)段的基址是0,段限長是640KB。而內(nèi)核代碼段和數(shù)據(jù)段的基地址時0,段限長是16MB,因此任務(wù)0的代碼段和數(shù)據(jù)段分別包含在內(nèi)核代碼段和數(shù)據(jù)段中。內(nèi)核初始化程序main.c也即是任務(wù)0中的代碼,只是在移動到任務(wù)0之前系統(tǒng)正以內(nèi)核態(tài)特權(quán)級0運行著main.c程序。宏move_to_user_mode的功能就是把運行特權(quán)級內(nèi)核態(tài)的0級變換到用戶態(tài)的3級,但是仍然繼續(xù)執(zhí)行原來的代碼指令流。

  在移動到任務(wù)0的過程中,宏move_to_user_mode使用了中斷返回指令造成特權(quán)級改變的方法。該方法的主要思想是在對站中構(gòu)筑中斷返回指令需要的內(nèi)容,把返回地址的段選擇符設(shè)置成任務(wù)0代碼段選擇符,其特權(quán)級為3。此后執(zhí)行中斷返回指令iret時將導(dǎo)致系統(tǒng)CPU從特權(quán)級0跳轉(zhuǎn)到外層的特權(quán)級3上運行。參見下圖所示的特權(quán)級發(fā)生變化時中斷返回堆棧結(jié)構(gòu)示意圖。


         宏move_to_user_mode首先往內(nèi)核堆棧中壓入任務(wù)0數(shù)據(jù)段選擇符和內(nèi)核堆棧指針。然后壓入標(biāo)志寄存器內(nèi)容。最后壓入任務(wù)0代碼段選擇符合執(zhí)行中斷返回后需要執(zhí)行的下一條指令的偏移位置。該偏移位置是iret后的一條指令處。

  當(dāng)執(zhí)行iret指令時,CPU把返回地址送入CS:EIP中,同時探出對站中標(biāo)志寄存器內(nèi)容。由于CPU判斷出墓地代碼段的特權(quán)級是3,與當(dāng)前內(nèi)核態(tài)的0級不同。于是CPU會把堆棧中的堆棧段選擇符合堆棧指針彈出到SS:ESP中。由于特權(quán)級發(fā)生了變化,段寄存器DS,ES,FS和GS的值變得無效,此時CPU會把這些段寄存器清零。因此在執(zhí)行了iret指令后需要重新加載這些段寄存器。此后,系統(tǒng)就開始特權(quán)級3運行在任務(wù)0的代碼上。所使用的用戶態(tài)堆棧還是原來在移動之前使用的堆棧。而其內(nèi)核態(tài)堆棧則被指定為其任務(wù)數(shù)據(jù)解噢股所在頁面的頂端開始(PAGE_SIZE+(long)&init_task)。由于以后在創(chuàng)建新進(jìn)程時,需要復(fù)制任務(wù)0的任務(wù)數(shù)據(jù)結(jié)構(gòu),包括其用戶堆棧指針,因此需要任務(wù)0的用戶態(tài)堆棧在創(chuàng)建任務(wù)1(進(jìn)程1)之前保持"干凈"狀態(tài)。


1.4 創(chuàng)建新進(jìn)程

  Linux系統(tǒng)中創(chuàng)建新進(jìn)程使用fork()系統(tǒng)調(diào)用。所有進(jìn)程都是通過復(fù)制進(jìn)程0而得到的,都是進(jìn)程0的子進(jìn)程。

  在創(chuàng)建新進(jìn)程的過程中,系統(tǒng)首先在任務(wù)數(shù)組中找出一個還沒有被任何進(jìn)程使用的空項(空槽)。如果系統(tǒng)已經(jīng)有64個進(jìn)程在運行,則fork()系統(tǒng)調(diào)用會因為任務(wù)數(shù)組表中沒有可用空項而出錯返回。然后系統(tǒng)為新建進(jìn)程在主內(nèi)存區(qū)中申請一頁內(nèi)存來存放其任務(wù)數(shù)據(jù)結(jié)構(gòu)信息,并復(fù)制當(dāng)前進(jìn)程任務(wù)數(shù)據(jù)結(jié)構(gòu)中所有內(nèi)容作為新進(jìn)程人物數(shù)據(jù)結(jié)構(gòu)的模板。為了防止這個還未處理完成的新進(jìn)程被調(diào)度函數(shù)執(zhí)行,此時應(yīng)該立刻將新進(jìn)程狀態(tài)置為不可中斷的等待狀態(tài)(TASK_UNINTERRUPTIBLE)。

  隨后對復(fù)制的任務(wù)數(shù)據(jù)結(jié)構(gòu)進(jìn)行修改。把當(dāng)前進(jìn)程設(shè)置為新進(jìn)程的父進(jìn)程,清除信號位圖并復(fù)位新進(jìn)程各統(tǒng)計值,并設(shè)置初始運行時間片值為15個系統(tǒng)嘀嗒數(shù)(150ms)。接著根據(jù)當(dāng)前進(jìn)程設(shè)置任務(wù)狀態(tài)段(TSS)中各寄存器的值。由于創(chuàng)建進(jìn)程時新進(jìn)程返回值應(yīng)為0,所以需要設(shè)置tss.eax=0。新建進(jìn)程內(nèi)核態(tài)堆棧指針tss.esp0被設(shè)置成新進(jìn)程任務(wù)數(shù)據(jù)結(jié)構(gòu)所在內(nèi)存頁面的頂端,而堆棧段tss.ss0被設(shè)置成內(nèi)核數(shù)據(jù)段選擇符。tss.ldt被設(shè)置為局部表描述符在GDT中所引值。如果當(dāng)前進(jìn)程使用了協(xié)處理器,把還需要協(xié)處理器的完整狀態(tài)保存到新進(jìn)程的tss.i387結(jié)構(gòu)中。

  此后系統(tǒng)設(shè)置新任務(wù)的代碼和數(shù)據(jù)段基址,限長并輔之當(dāng)前進(jìn)程內(nèi)存分頁管理的頁表。如果父進(jìn)程中也有文件是打開的,則應(yīng)將對應(yīng)文件的打開次數(shù)增1。接著在GDT中設(shè)置新任務(wù)的TSS和LDT描述符項,其中基地址信息指向新進(jìn)程人物結(jié)構(gòu)中的tss和ldt。最后再將新任務(wù)設(shè)置成可運行狀態(tài)并返回新進(jìn)程號。


1.5 進(jìn)程調(diào)度

  由前面描述可知,Linux進(jìn)程是搶占式的。被搶占的進(jìn)程仍然處于TASK_RUNNING狀態(tài),只是暫時沒有被CPU運行。進(jìn)程的搶占發(fā)生在進(jìn)程處于用戶態(tài)執(zhí)行階段,在內(nèi)核執(zhí)行時是不能被搶占的。

  為了能讓進(jìn)程有效地使用系統(tǒng)資源,又能使進(jìn)程有較快的響應(yīng)時間,就需要對進(jìn)程的切換調(diào)度采用一定的調(diào)度策略。在Linux0.11中采用了基于優(yōu)先級排隊的調(diào)度策略。

  調(diào)度程序:  

  schedule()函數(shù)首先掃描任務(wù)數(shù)組。通過比較每個就緒態(tài)(TASK_RUNNING)任務(wù)的運行時間遞減滴答計數(shù)counter的值來確定當(dāng)前哪個進(jìn)程運行時間最少。哪一個的值大,就表示運行時間還不長,于是就選中該進(jìn)程,并使用任務(wù)切換宏函數(shù)切換到該進(jìn)程運行。

  如果此時所有處于TASK_RUNNING狀態(tài)進(jìn)程的時間片已經(jīng)用完,系統(tǒng)就會根據(jù)每個進(jìn)程的優(yōu)先權(quán)值priority,對系統(tǒng)中所有進(jìn)程(包括正在睡眠的進(jìn)程)重新計算每個任務(wù)需要運行的時間片值counter。

  計算公式是:  counter= counter/2 + priority

  然后schedule()函數(shù)重新掃描人物數(shù)組中所有處于TASK_RUNNING狀態(tài),重復(fù)上述過程,直到選擇出一個進(jìn)程位置。最后調(diào)用switch_to()執(zhí)行實際的進(jìn)程切換操作。

  如果此時沒有其他進(jìn)程可運行,系統(tǒng)就會選擇進(jìn)程0運行。對于linux0.11來說,進(jìn)程0會調(diào)用pause()把自己置為可中斷的睡眠狀態(tài)并在此調(diào)用schedule()。不過在調(diào)度進(jìn)程運行時,schedule()并不在意進(jìn)程0處于什么狀態(tài)。只要系統(tǒng)空閑就調(diào)度進(jìn)程0運行。

  進(jìn)程切換:

  執(zhí)行實際進(jìn)程切換的任務(wù)由switch_to()宏定義的一段匯編代碼完成。在進(jìn)行切換之前,switch_to()首先檢查要切換到的進(jìn)程是否就是當(dāng)前進(jìn)程,如果是則什么也不做,直接退出。否則就首先把內(nèi)核全局變量current置為新任務(wù)的指針,然后長跳轉(zhuǎn)到新任務(wù)的任務(wù)狀態(tài)段TSS組成的地指處,造成CPU執(zhí)行任務(wù)切換操作。此時CPU會把其所有寄存器的轉(zhuǎn)改保存到當(dāng)前人物寄存器TR中TSS段選擇符所指向的當(dāng)前進(jìn)程任務(wù)數(shù)據(jù)結(jié)構(gòu)的tss結(jié)構(gòu)中,然后把新任務(wù)狀態(tài)段選擇符所指向的新任務(wù)數(shù)據(jù)結(jié)構(gòu)中tss結(jié)構(gòu)中的寄存器信息恢復(fù)到CPU中,系統(tǒng)就正式開始運行新切換的任務(wù)了。這個過程可參考下圖



1.6 終止進(jìn)程

  當(dāng)一個進(jìn)程結(jié)束了運行或在半途中終止了運行,那么內(nèi)核就需要釋放該進(jìn)程所占用的系統(tǒng)資源。這包括進(jìn)程運行時打開的文件,申請的內(nèi)存等。

  當(dāng)一個用戶程序調(diào)用exit()系統(tǒng)調(diào)用時,就會執(zhí)行內(nèi)核函數(shù)do_exit()。該函數(shù)會首先釋放進(jìn)程代碼段和數(shù)據(jù)段占用的內(nèi)存頁面,關(guān)閉進(jìn)程打開著的所有文件,對進(jìn)程使用的當(dāng)前工作目錄,根目錄和運行程序的i節(jié)點進(jìn)行同步操作。如果進(jìn)程有子進(jìn)程,則讓init進(jìn)程作為其所有子進(jìn)程的父進(jìn)程。如果進(jìn)程是一個會話頭進(jìn)程并且有控制終端,則釋放控制終端,并向?qū)儆谠摃挼乃羞M(jìn)程發(fā)送掛斷信號SIGHUP,這通常會終止該會話中的所有進(jìn)程。然后把進(jìn)程狀態(tài)置為僵死狀態(tài)TASK_ZOMBIE。并向其原父進(jìn)程發(fā)送SIGCHILD信號,通知其某個子進(jìn)程已經(jīng)終止。最后do_exit()調(diào)用調(diào)度函數(shù)去執(zhí)行其他進(jìn)程。由此可見在進(jìn)程終止時,它的task_struct任務(wù)數(shù)據(jù)結(jié)構(gòu)仍然保留著。因為其父進(jìn)程還需要使用其中的信息。

  在子進(jìn)程在執(zhí)行期間,父進(jìn)程通常使用wait()或waitpid()函數(shù)等待其某個子進(jìn)程終止。當(dāng)子進(jìn)程被終止并處于僵死狀態(tài)時,父進(jìn)程就會把子進(jìn)程運行所使用的時間累加到自己進(jìn)程中。最終釋放已終止子進(jìn)程任務(wù)數(shù)據(jù)結(jié)構(gòu)所占用的內(nèi)存頁面,并置空子進(jìn)程在任務(wù)數(shù)組中占用的指針項。

轉(zhuǎn)載:

http://www.cnblogs.com/hongzg1982/articles/2112224.html


本站僅提供存儲服務(wù),所有內(nèi)容均由用戶發(fā)布,如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請點擊舉報
打開APP,閱讀全文并永久保存 查看更多類似文章
猜你喜歡
類似文章
Linux內(nèi)核模式
進(jìn)程調(diào)度
Linux進(jìn)程-進(jìn)程的創(chuàng)建
Linux內(nèi)核中的init
Linux內(nèi)核CFS調(diào)度器:實現(xiàn)高效多任務(wù)處理
進(jìn)程上下文意思
更多類似文章 >>
生活服務(wù)
熱點新聞
分享 收藏 導(dǎo)長圖 關(guān)注 下載文章
綁定賬號成功
后續(xù)可登錄賬號暢享VIP特權(quán)!
如果VIP功能使用有故障,
可點擊這里聯(lián)系客服!

聯(lián)系客服