一,進(jìn)程 1. 進(jìn)程的概念 簡單的說,一個執(zhí)行中的程序就是一個進(jìn)程。具體說, 進(jìn)程是一個分配系統(tǒng)資源的活動實體,它包含以下兩點: (1)實體,每個進(jìn)程都有一段地址空間,在這段空間中,用于存放程序的代碼,數(shù)據(jù),指令的地址等靜態(tài)的事物; (2)程序運行的過程,是一個動態(tài)的概念,即需不斷申請占用系統(tǒng)資源的當(dāng)前的活動過程。 總之,進(jìn)程不僅包含有關(guān)程序的一些靜態(tài)事物,還有需不斷申請系統(tǒng)資源的動態(tài)的過程。 2. 進(jìn)程的創(chuàng)建過程 進(jìn)程形成的方法: (1)一個程序要運行起來,首先要被加載到內(nèi)存上,這時,就形成了進(jìn)程。該進(jìn)程的父進(jìn)程是bush。 (2)通過系統(tǒng)調(diào)用來創(chuàng)建,在一個進(jìn)程中利用fork,vfork函數(shù)創(chuàng)建子進(jìn)程,該子進(jìn)程的父進(jìn)程就是創(chuàng)建它的的進(jìn)程。(關(guān)于fork,vfork的使用,在后文中有詳細(xì)說明) 形成之后: 操作系統(tǒng)要對該進(jìn)程進(jìn)行管理,但并不和該進(jìn)程的程序代碼等直接接觸。而是: (1)描述:將該進(jìn)程的相關(guān)屬性信息描述起來,放在一個叫進(jìn)程控制塊的數(shù)據(jù)結(jié)構(gòu)中,將進(jìn)程控制塊統(tǒng)稱為PCB,在Linux中稱PCB為task_struct,task_struct由操作系統(tǒng)創(chuàng)建。 (2)組織:操作系統(tǒng)將各進(jìn)程的PCB利用鏈表的形式組織起來,放在內(nèi)核中。這樣,操作系統(tǒng)就可以對各進(jìn)程進(jìn)行統(tǒng)一管理了。 (3)為各進(jìn)程分別創(chuàng)建一個虛擬地址空間(也是一個結(jié)構(gòu)體:mm_struct),頁表,映射關(guān)系。 各進(jìn)程的相關(guān)代碼和數(shù)據(jù)都存放在物理內(nèi)存中,但操作系統(tǒng)并不直接對物理內(nèi)存進(jìn)行操作,而是將虛擬地址通過頁表映射到物理內(nèi)存地址上,從而對其進(jìn)行操作。該虛擬地址空間由上述的PCB中的指針指向,這樣,操作系統(tǒng)便可管理該進(jìn)程了。 總之,一個進(jìn)程的創(chuàng)建,操作系統(tǒng)需: (1)描述并創(chuàng)建PCB,并將其鏈入鏈表組織起來放入內(nèi)核 (2)創(chuàng)建虛擬地址空間,頁表,映射關(guān)系 3. task_struct的內(nèi)容 標(biāo)識符PID:描述本進(jìn)程的唯一標(biāo)識符,用于區(qū)別其他進(jìn)程,類似于學(xué)生的學(xué)號,人的身份證號。 狀態(tài):包括任務(wù)狀態(tài),退出代碼,退出信號等。 優(yōu)先級:一個CPU一次只能運行一個進(jìn)程,運行哪個進(jìn)程要根據(jù)其優(yōu)先級來判斷 程序計數(shù)器:存放程序中即將被執(zhí)行的下一條指令的地址,類似于寄存器PC的存在 內(nèi)存指針:包含程序代碼和進(jìn)程數(shù)據(jù)相關(guān)的指針,還有和其他進(jìn)程共享的內(nèi)存塊的指針 上下文數(shù)據(jù):進(jìn)程執(zhí)行時處理器的寄存器中的數(shù)據(jù) I/O狀態(tài)信息:包括顯示的I/O請求,分配給進(jìn)程的I/O設(shè)備,被進(jìn)程使用的文件列表 記賬信息:包括進(jìn)程存在的時間總和等。 其中: 進(jìn)程的狀態(tài)包含: (1)R:運行狀態(tài),處于該狀態(tài)的進(jìn)程要么在運行要么處于運行隊列中 (2)S:淺度睡眠狀態(tài),可以被中斷,可以用kill命令殺死 (3)D:深度睡眠狀態(tài),不可被中斷,不可被殺死,一般在I/O時發(fā)生,要結(jié)束該狀態(tài)的進(jìn)程,要么等I/O結(jié)束,要么關(guān)機 (4)T:停止?fàn)顟B(tài) (5)Z:僵尸狀態(tài):進(jìn)程退出后一直維持該狀態(tài),直到該進(jìn)程的父進(jìn)程讀取了他的退出信息后,將其釋放。 狀態(tài)的優(yōu)先級:CPU根據(jù)優(yōu)先級來決定運行進(jìn)程的先后順序 可以通過ps -l命令來查看優(yōu)先級,其中: PRI:表示進(jìn)程的優(yōu)先級,此值越小優(yōu)先級越高 NI:優(yōu)先級的修正數(shù)值 所以,PRI=PRI(80)+NI(-20~19) 可以通過命令來修改NI: (1)renice:修改已存在進(jìn)程的優(yōu)先級:renice NI的值 -p 要修改進(jìn)程PID (2)nice:修改啟動前的進(jìn)程的優(yōu)先級:nice -n NI的值 可執(zhí)行程序路徑 運行如下進(jìn)程:
執(zhí)行如下命令: 或者,在進(jìn)程啟動前,輸入以下命令: 4. 查看進(jìn)程 在一個進(jìn)程運行過程中,需要知道如何查看該進(jìn)程是否被創(chuàng)建成功。 (1)Linux下輸入命令:
該命令用于顯示進(jìn)程目錄/proc中所有當(dāng)前正在運行的進(jìn)程 這些數(shù)字即為正在運行進(jìn)程的PID。 (2)通過命令ps或top獲取進(jìn)程信息。 (3)利用系統(tǒng)調(diào)用獲取進(jìn)程標(biāo)識符
這兩個函數(shù)均需引用頭文件<sys/types.h>和<unistd.h> 5. 創(chuàng)建進(jìn)程 在了解了進(jìn)程的相關(guān)信息后,接下來,我們來創(chuàng)建一個進(jìn)程。 (1)通過運行程序來創(chuàng)建 注意:進(jìn)程是一個動態(tài)的過程,程序在運行期間,進(jìn)程被創(chuàng)建,一旦程序運行結(jié)束,進(jìn)程也就退出了。 那么,程序在一瞬間就運行結(jié)束,我們?nèi)绾沃肋M(jìn)程是否被創(chuàng)建呢? 編寫如下代碼,使程序一直運行:
編譯鏈接運行該程序: 我們發(fā)現(xiàn)該程序一直在運行,也就是該進(jìn)程一直存在,但怎么獲取該進(jìn)程相關(guān)的信息呢? 這時,需要該進(jìn)程在后臺運行,從而獲取其相關(guān)信息,輸入以下指令: 輸出的數(shù)字為該進(jìn)程的PID,這時,查看/proc目錄,可以看到該進(jìn)程的PID在該目錄中。說明該進(jìn)程已經(jīng)被創(chuàng)建成功,當(dāng)輸入:
可通過該命令殺死該進(jìn)程,此時,在查看/proc目錄時,已經(jīng)沒有該進(jìn)程的PID了。說明該進(jìn)程已經(jīng)結(jié)束。 (2)利用fork函數(shù)創(chuàng)建子進(jìn)程 fork創(chuàng)建子進(jìn)程后,子,父進(jìn)程對fork語句之后的代碼和沒有修改的數(shù)據(jù)進(jìn)行共享,如果數(shù)據(jù)被父或子進(jìn)程修改,則在父,子進(jìn)程中會各自開辟存放該數(shù)據(jù)的內(nèi)存,采取寫實拷貝。 fork函數(shù)的返回值情況: 1)若子進(jìn)程創(chuàng)建成功,向父進(jìn)程返回子進(jìn)程的PID,向子進(jìn)程返回0; 2)若子進(jìn)程創(chuàng)建失敗,則返回-1。 運行如下程序:
運行結(jié)果: 可以看到,在子進(jìn)程創(chuàng)建后,先運行父進(jìn)程,在運行子進(jìn)程,返回值情況確實滿足上述兩點。 注意:fork創(chuàng)建子進(jìn)程后,誰先運行是不確定的。 (3)利用vfork創(chuàng)建子進(jìn)程 vfork的用法與fork相同,只是,有下列區(qū)別: 1)vfork創(chuàng)建子進(jìn)程后,子,父進(jìn)程共享代碼和數(shù)據(jù) 2)先運行子進(jìn)程,子進(jìn)程只有通過調(diào)用exit或exec結(jié)束后,父進(jìn)程才開始運行 6. 僵尸進(jìn)程 就是處于僵尸狀態(tài)的進(jìn)程,當(dāng)子進(jìn)程退出,父進(jìn)程沒有讀取子進(jìn)程的退出信息時,子進(jìn)程就一直處于僵尸狀態(tài)。 運行以下程序來創(chuàng)建僵尸進(jìn)程:
在該程序運行5s內(nèi),子,父進(jìn)程都在運行。5s后,子進(jìn)程運行結(jié)束,此時,父進(jìn)程還在運行,還沒來得及獲取子進(jìn)程的退出信息,所以,5s后子進(jìn)程變?yōu)榻┦M(jìn)程;30s后父進(jìn)程運行結(jié)束,子父進(jìn)程均退出。 輸入以下命令,觀察處于僵尸狀態(tài)的子進(jìn)程: 進(jìn)程開始運行后5s內(nèi): 5s后: 30s后: 7. 孤兒進(jìn)程 如果父進(jìn)程先退出,而子進(jìn)程還在進(jìn)行,此時,子進(jìn)程就會變成孤兒進(jìn)程。 孤兒進(jìn)程會被1號進(jìn)程領(lǐng)養(yǎng),該孤兒進(jìn)程退出后,就會被1還進(jìn)程回收。 下面,演示一個孤兒進(jìn)程的例子:
在該例中,父進(jìn)程執(zhí)行完畢后,即退出,而子進(jìn)程30s后才退出,所以,來觀察下子進(jìn)程的父進(jìn)程的PID來驗證它是否被1號進(jìn)程領(lǐng)養(yǎng)。 結(jié)果如下: 二,環(huán)境變量 環(huán)境變量是操作系統(tǒng)中指定程序運行環(huán)境的一些變量,比如,在鏈接庫文件時,這些庫文件在哪里,去哪里找,這些信息都存放在環(huán)境變量中。 環(huán)境變量通常具有某些特殊用途,在系統(tǒng)中通常具有全局特性。 1. 常見環(huán)境變量 (1)PATH:存放系統(tǒng)常用命令的搜索路徑的變量 查看該變量的內(nèi)容,輸入命令:
輸出結(jié)果:
每個路徑用分號相隔。比如,常用的命令ls,我們在運行它的時候,并沒有加上的路徑,并不知道該命令的可執(zhí)行程序放在哪里,這時,操作系統(tǒng),就會根據(jù)PATH中提供的路徑,一條路徑一條路徑的找,直到找到為止。 我們自己編寫形成的可執(zhí)行程序a.out在運行時,必須加上路徑,如:./a.out,表示運行當(dāng)前路徑下的a.out。那如何能像運行l(wèi)s一樣不加路徑呢,有如下兩種方法: 1)將可執(zhí)行程序a.out放在PATH指定的路徑下,那么在運行時,就會根據(jù)PATH中的路徑去找a.out所在的位置。 如:cp ./a.out /bin 2)將a.out的路徑放入到PATH變量中。這時,需要先明白a.out的路徑,再進(jìn)行存放,輸入命令: $PATH表示原有的內(nèi)容,即上述查看PATH時輸出的內(nèi)容。 :/home/admin/bit_code/Linux表示在原有PATH的基礎(chǔ)上追加該路徑;然后把原有PATH的內(nèi)容和追加后的路徑一起賦給變量PATH;export是導(dǎo)出變量PATH,即更新PATH。 再次進(jìn)行查看時,便可看到a.out的路徑已經(jīng)在變量PATH中: 若要刪除新存放的路徑,只需要關(guān)閉終端,再次打開即可。 如果不想關(guān)閉終端來刪除新追加的路徑,只需輸入以下命令即可:
(2)HOME:指定用戶的主工作目錄(可以用echo命令來進(jìn)行查看) (3)HISTSIZE:系統(tǒng)中默認(rèn)保存的歷史命令的條數(shù),一般是1000。(以同樣的方法可查看) 若要查看歷史命令,只需輸入命令:history (4)SHELL:查看當(dāng)前使用的shell 2. 常用的有關(guān)環(huán)境變量的命令 (1)echo:查看環(huán)境變量的內(nèi)容,上述已經(jīng)說明 (2)export:設(shè)置一個新的環(huán)境變量 1)更新原有環(huán)境變量的值,(這里不加export也可以,因為該變量已經(jīng)是環(huán)境變量,但最好加上)上述已經(jīng)驗證 2)將本地變量變成環(huán)境變量。 將MYENV設(shè)置為環(huán)境變量,在終端運行以下命令:
用echo查看時,沒有結(jié)果,說明該環(huán)境變量沒有設(shè)置成功,他只是一個本地變量。 再在終端運行以下命令:
用echo查看時,可以看到結(jié)果,說明該環(huán)境變量設(shè)置成功。 所以,export的作用是設(shè)置環(huán)境變量。如果環(huán)境變量已經(jīng)存在,可以理解為更新環(huán)境變量的值(也可以不加);如果環(huán)境變量不存在,就必須用export設(shè)置環(huán)境變量。 注意:使用export設(shè)置的環(huán)境變量在關(guān)閉中斷后即失效 (3)env:查看系統(tǒng)中存在的所有環(huán)境變量及其值 (4)set:顯示所有本地定義的shell變量及環(huán)境變量和其相應(yīng)的值(注意與env的區(qū)別) 運行以下命令:
上述已經(jīng)說明,該變量不是環(huán)境變量(用env查看沒有結(jié)果),所以是本地變量,用set查看:
這時會顯示結(jié)果:
說明,set可以顯示本地變量,同樣可以顯示環(huán)境變量,這里不再說明。 (5)unset:清除環(huán)境變量 先用export設(shè)置環(huán)境變量MYENV,再用env查看是否創(chuàng)建成功,創(chuàng)建成功后,在用unset清除該環(huán)境變量,在用env查看是否清除成功。如下: 3. 環(huán)境變量的全局特性 首先通過命令在終端設(shè)置本地變量:MYENV=“hello world”,然后通過以下程序(即子進(jìn)程,bash為父進(jìn)程)輸出該環(huán)境變量的值。 下面先介紹一個系統(tǒng)調(diào)用getenv
再運行以下代碼,輸出環(huán)境變量的值:
當(dāng)運行該代碼時,出現(xiàn)如下結(jié)果: 發(fā)現(xiàn)沒有結(jié)果,說明子進(jìn)程不能看到本地變量的值。 通過export將MYENV設(shè)置為環(huán)境變量,在運行程序時,會輸出:hello world。說明,此時子進(jìn)程能夠看到環(huán)境變量的值。因為環(huán)境變量具有全局特性。 注意:本地變量沒有全局特性,只會被本shell看到,不會被子進(jìn)程看到。 而環(huán)境變量具有全局特性,所以會被本shell及以下的所有子進(jìn)程看到,即被子進(jìn)程繼承下去。
|
|