国产一级a片免费看高清,亚洲熟女中文字幕在线视频,黄三级高清在线播放,免费黄色视频在线看

打開APP
userphoto
未登錄

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

開通VIP
linux進程及進程控制

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

2012  

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

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

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




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


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

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


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

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


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

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


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

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


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

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


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

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


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

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


1.3 進程初始化

  在boot/目錄中引導(dǎo)程序把內(nèi)核從磁盤上加載到內(nèi)存中,并讓系統(tǒng)進入保護模式下運行后,就開始執(zhí)行系統(tǒng)初始化程序init/main.c。該程序首先確定如何分配使用系統(tǒng)物理內(nèi)存,然后調(diào)用內(nèi)核各部分的初始化函數(shù)分別對內(nèi)存管理,中斷處理,塊設(shè)備和字符設(shè)備,進程管理以及硬盤和軟盤硬件進行初始化處理。在完成了這些操作之后,系統(tǒng)各部分已經(jīng)處于可運行狀態(tài)。此后程序把自己"手工"移動到任務(wù)0(進程0)中運行,并使用fork()調(diào)用首次創(chuàng)建出進程1。在進程1種程序?qū)⒗^續(xù)進行應(yīng)用環(huán)境的初始化并執(zhí)行shell登陸程序。而原進程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)境進行的設(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)建新進程時,需要復(fù)制任務(wù)0的任務(wù)數(shù)據(jù)結(jié)構(gòu),包括其用戶堆棧指針,因此需要任務(wù)0的用戶態(tài)堆棧在創(chuàng)建任務(wù)1(進程1)之前保持"干凈"狀態(tài)。


1.4 創(chuàng)建新進程

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

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

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

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


1.5 進程調(diào)度

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

  為了能讓進程有效地使用系統(tǒng)資源,又能使進程有較快的響應(yīng)時間,就需要對進程的切換調(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)前哪個進程運行時間最少。哪一個的值大,就表示運行時間還不長,于是就選中該進程,并使用任務(wù)切換宏函數(shù)切換到該進程運行。

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

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

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

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

  進程切換:

  執(zhí)行實際進程切換的任務(wù)由switch_to()宏定義的一段匯編代碼完成。在進行切換之前,switch_to()首先檢查要切換到的進程是否就是當(dāng)前進程,如果是則什么也不做,直接退出。否則就首先把內(nèi)核全局變量current置為新任務(wù)的指針,然后長跳轉(zhuǎn)到新任務(wù)的任務(wù)狀態(tài)段TSS組成的地指處,造成CPU執(zhí)行任務(wù)切換操作。此時CPU會把其所有寄存器的轉(zhuǎn)改保存到當(dāng)前人物寄存器TR中TSS段選擇符所指向的當(dāng)前進程任務(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 終止進程

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

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

  在子進程在執(zhí)行期間,父進程通常使用wait()或waitpid()函數(shù)等待其某個子進程終止。當(dāng)子進程被終止并處于僵死狀態(tài)時,父進程就會把子進程運行所使用的時間累加到自己進程中。最終釋放已終止子進程任務(wù)數(shù)據(jù)結(jié)構(gòu)所占用的內(nèi)存頁面,并置空子進程在任務(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)核模式
進程調(diào)度
Linux進程-進程的創(chuàng)建
Linux內(nèi)核中的init
Linux內(nèi)核CFS調(diào)度器:實現(xiàn)高效多任務(wù)處理
進程上下文意思
更多類似文章 >>
生活服務(wù)
分享 收藏 導(dǎo)長圖 關(guān)注 下載文章
綁定賬號成功
后續(xù)可登錄賬號暢享VIP特權(quán)!
如果VIP功能使用有故障,
可點擊這里聯(lián)系客服!

聯(lián)系客服