日本不卡不码高清免费观看,久久国产精品久久w女人spa,黄色aa久久,三上悠亚国产精品一区二区三区

您的位置:首頁技術文章
文章詳情頁

UNIX 進程揭秘

瀏覽:38日期:2024-06-17 14:26:51

分配給系統管理員的許多工作之一是確保用戶的程序正確運行。因為系統上存在其他并發運行的程序,所以此任務變得更加復雜。由于種種原因,這些程序可能會失敗、掛起或行為異常。在構建更可靠的系統時,了解 Unix® 環境如何創建、管理和銷毀這些作業是至關重要的步驟。

開發人員還必須積極了解內核如何管理進程,因為與系統的其他部分和睦相處的應用程序會占用更少的資源,并且不會頻繁地給系統管理員帶來麻煩。由于導致僵死進程(將在稍后對其進行描述)而頻繁重新啟動的應用程序明顯是不可取的。對控制進程的 UNIX 系統調用的了解可以使開發人員編寫能夠在后臺自動運行的軟件,而不是需要一個始終保持在用戶屏幕上的終端會話。

管理這些程序的基本構件就是進程。進程是賦予某個由操作系統執行的程序的名稱。如果您熟悉 ps 命令,則您應該熟悉進程清單,如清單 1 所示。

清單 1. ps 命令的輸出

sunbox#ps -ef UID  PID PPID  CSTIME TTY TIME CMDroot 0 0  0 20:15:23 ?  0:14 schedroot 1 0  0 20:15:24 ?  0:00 /sbin/initroot 2 0  0 20:15:24 ?  0:00 pageoutroot 3 0  0 20:15:24 ?  0:00 fsflush daemon  240 1  0 20:16:37 ?  0:00 /usr/lib/nfs/statd...

前三列對這里的討論非常重要。第一列列出用于運行該進程的用戶身份,第二列列出進程的 ID,第三列列出該進程的父進程 ID。最后一列是進程的描述,通常是所運行的二進制文件的名稱。每個進程都被分配一個標識符,稱為進程標識符(Process IdentifIEr,PID)。進程還有父進程,在大多數情況下就是啟動它的進程的 PID。

父 PID (PPID) 的存在意味著這是一個由別的進程創建的進程。最初創建進程的原始進程名為 init,它始終被賦予 PID 1。init 是將在內核啟動時啟動的第一個實際進程。啟動系統的其余部分是 init 的工作。init 和其他具有 PPID 0 的進程屬于內核。

使用 fork 系統調用

fork(2) 系統調用創建一個新進程。清單 2 顯示了一個簡單 C 代碼片段中使用的 fork。

清單 2. 簡單的 fork(2) 用法

sunbox$ cat fork1.c#include <unistd.h>#include <stdio.h>int main (void) {pid_t p; /* fork returns type pid_t */p = fork();printf("fork returned %dn", p);}sunbox$ gcc fork1.c -o fork1sunbox$ ./fork1fork returned 0fork returned 698

fork1.c 中的代碼不過就是發出 fork 調用,并通過一個 printf 調用來打印整數結果。雖然只發出了一個調用,但是打印了兩次輸出。這是因為在 fork 調用中創建了一個新進程。現在有兩個單獨的進程在從該調用返回結果。這通常被描述為“調用一次,返回兩次。

fork 返回的值非常有趣。其中一個返回 0;另一個返回一個非零值。獲得 0 的進程稱為子進程,非零結果屬于原始進程,即父進程。您將使用返回值來確定哪個是父進程,哪個是子進程。由于兩個進程都在同一空間中繼續運行,唯一有實際意義的區別是從 fork 返回的值。

0 和非零返回值的基本原理在于,子進程始終可以通過 getppid(2) 調用來找出其父進程是誰,但是父進程要找出它的所有子進程卻很困難。因此,要告訴父進程關于其新的子進程的信息,而子進程可在需要時查找其父進程。

考慮到 fork 的返回值,現在該代碼可以檢查確定它是父進程還是子進程,并進行相應的操作。清單 3 顯示了一個基于 fork 的結果來打印不同輸出的程序。

清單 3. 更完整的 fork 用法示例

sunbox$ cat fork2.c#include <unistd.h>#include <stdio.h>int main (void) {pid_t p;printf("Original program, pid=%dn", getpid());p = fork();if (p == 0) {printf("In child process, pid=%d, ppid=%dn",getpid(), getppid());} else {printf("In parent, pid=%d, fork returned=%dn",getpid(), p);}}sunbox$ gcc fork2.c -o fork2sunbox$ ./fork2Original program, pid=767In child process, pid=768, ppid=767In parent, pid=767, fork returned=768

清單 3 在每個步驟打印出 PID,并且該代碼檢查從 fork 返回的值來確定哪個進程是父進程,哪個進程是子進程。對所打印的 PID 進行比較,可以看到原始進程是父進程 (PID 767),并且子進程 (PID 768) 知道其父進程是誰。請注意子進程如何通過 getppid 來知道其父進程以及父進程如何使用 fork 來定位其子進程。

現在您已經了解了復制某個進程的方法,下面讓我們研究如何運行一個不同的進程。fork 只是進程機制中的一半。exec 系列系統調用運行實際的程序。

使用 exec 系列系統調用

exec 的工作是將當前進程替換為一個新進程。請注意“替換這個措詞的含義。在您調用 exec 以后,當前進程就消失了,新進程就啟動了。如果希望創建一個單獨的進程,您必須首先運行 fork,然后在子進程中執行 (exec) 新的二進制文件。清單 4 顯示了這樣一種情況。

清單 4. 通過將 fork 與 exec 配合使用來運行不同的程序

sunbox$ cat exec1.c#include <unistd.h>#include <stdio.h>int main (void) {/* Define a null terminated array of the command to run  followed by any parameters, in this case none */char *arg[] = { "/usr/bin/ls", 0 };/* fork, and exec within child process */if (fork() == 0) {printf("In child process:n");execv(arg[0], arg);printf("I will never be calledn");}printf("Execution continues in parent processn");}sunbox$ gcc exec1.c -o exec1sunbox$ ./exec1In child process:fork1.c exec1fork2  exec1.c fork1fork2.c Execution continues in parent process

清單 4 中的代碼首先定義一個數組,其中第一個元素是要執行的二進制文件的路徑,其余元素充當命令行參數。根據手冊頁的描述,該數組以 Null 結尾。在從 fork 系統調用返回以后,將指示子進程執行 (execv) 新的二進制文件。

execv 調用首先取得一個指向要運行的二進制文件名稱的指針,然后取得一個指向您前面聲明的參數數組的指針。該數組的第一個元素實際上是二進制文件的名稱,因此參數實際上是從第二個元素開始的。請注意,該子進程一直沒有從 execv 調用返回。這表明正在運行的進程已被新進程所替換。

還存在其他執行 (exec) 某個進程的系統調用,它們的區別在于接受參數的方式和是否需要傳遞環境變量。execv(2) 是替換當前映像的較簡單方法之一,因為它不需要關于環境的信息,并且它使用以 Null 結尾的數組。其他選項包括 execl(2)(它單獨接受各個參數)或 execvp(2)(它也接受一個以 Null 結尾的環境變量數組)。使問題復雜化的是,并非所有操作系統都支持所有變體。關于使用哪一種變體的決定取決于平臺、編碼風格和是否需要定義任何環境變量。

調用 fork 時,打開的文件會發生什么情況呢?

當某個進程復制它自身時,內核生成所有打開的文件描述符的副本。文件描述符是指向打開的文件或設備的整數,并用于執行讀取和寫入。如果在調用 fork 前,某個程序已經打開了一個文件,如果兩個進程都嘗試執行讀取或寫入,會發生什么情況呢?一個進程會改寫另一個進程中的數據嗎?是否會讀取該文件的兩個副本?清單 5 對此進行了研究,它打開兩個文件——一個文件用于讀取,另一個文件用于寫入——并讓父進程和子進程同時執行讀取和寫入。

清單 5. 同時對同一文件執行讀取和寫入的兩個進程

#include <stdio.h>#include <strings.h>#include <unistd.h>#include <sys/types.h>#include <sys/stat.h>#include <fcntl.h>int main(void) {int fd_in, fd_out;char buf[1024];memset(buf, 0, 1024); /* clear buffer*/fd_in = open("/tmp/infile", O_RDONLY);fd_out = open("/tmp/outfile", O_WRONLY|O_CREAT);fork(); /* It doesn't matter about child vs parent */while (read(fd_in, buf, 2) > 0) { /* Loop through the infile */printf("%d: %s", getpid(), buf);/* Write a line */sprintf(buf, "%d Hello, world!nr", getpid());write(fd_out, buf, strlen(buf));sleep(1);memset(buf, 0, 1024); /* clear buffer*/}sleep(10);}sunbox$ gcc fdtest1.c -o fdtest1sunbox$ ./fdtest12875: 12874: 22875: 32874: 42875: 52874: 62874: 7sunbox$ cat /tmp/outfile2875 Hello, world!2874 Hello, world!2875 Hello, world!2874 Hello, world!2875 Hello, world!2874 Hello, world!2874 Hello, world!

清單 5 是用于打開文件的簡單程序,并派生 (fork) 為父進程和子進程。每個進程從同一文件描述符(它只是一個包含數字 1 至 7 的文本文件)執行讀取操作,并連同 PID 一起打印所讀取的內容。在讀取一行之后,將 PID 寫到輸出文件。當輸入文件中沒有其他字符可供讀取時,循環結束。

清單 5 的輸出表明,當一個進程從該文件讀取時,兩個進程的文件指針都在移動。同樣地,當向某個文件寫入時,下一個字符被寫到文件結尾。這是非常有意義的,因為內核跟蹤打開文件的信息。文件描述符只不過是進程的標識符。

您可能還知道,標準輸出(屏幕)也是一個文件描述符。此文件描述符在 fork 調用期間被復制,這就是兩個進程都能對屏幕執行寫入操作的原因。

父進程或子進程的終止

進程必須在某個時候終止。問題只是哪個進程首先終止:父進程還是子進程。

父進程在子進程之前終止

如果父進程在子進程之前終止,孤立的子進程需要知道它們的父進程是誰。記住,每個進程都有父進程,并且您可以跟蹤從每個子進程一直到 PID 1(或稱為 init)的整個進程家族樹。當某個父進程終止時,init 將接納所有子進程,如清單 6 所示。

清單 6. 在子進程之前終止的父進程

#include <unistd.h>#include <stdio.h>int main(void) {int i;if (fork()) {/* Parent */sleep(2);_exit(0);}for (i=0; i < 5; i++) {printf("My parent is %dn", getppid());sleep(1);}}sunbox$ gcc dIE1.c -o die1sunbox$ ./die1My parent is 2920My parent is 2920sunbox$ My parent is 1My parent is 1My parent is 1

在此例中,父進程調用 fork,等待兩秒鐘,然后退出。子進程在五秒鐘內繼續打印其父 PID。可以看到,PPID 在父進程終止后更改為 1。Shell 提示符的返回也是非常有趣的。由于子進程在后臺運行,父進程一終止,控制即返回到 Shell。

子進程在父進程之前終止

清單 7 與清單 6 相反——即在父進程之前終止的子進程。為更好地說明所發生的事情,進程本身中沒有打印任何內容。而有趣的信息來自于進程清單。

清單 7. 子進程在父進程之前終止

sunbox$ cat dIE2.c#include <unistd.h>#include <stdio.h>int main(void) {int i;if (!fork()) {/* Child exits immediately*/_exit(0);}/* Parent waits around for a minute */sleep(60);}sunbox$ gcc die2.c -o die2sunbox$ ./die2 &[1] 2934sunbox$ ps -ef | grep 2934sean 2934 2885  0 21:43:05 pts/1  0:00 ./die2sean 2935 2934  0- ?  0:00 <defunct>sunbox$ ps -ef | grep 2934[1]+ Exit 199./die2

die2 使用 & 操作符在后臺運行,然后顯示一個進程清單,并且僅顯示正在運行的進程及其子進程。PID 2934 是父進程,PID 2935 是派生 (fork) 并立即終止的進程。盡管子進程提前退出,但它仍然在進程表中作為失效 (defunct) 進程存在,或稱為僵死 (zombie) 進程。當父進程在 60 秒以后終止時,兩個進程都消失了。

當子進程終止時,會使用一個名為 SIGCHLD 的信號來通知其父進程。該通知的確切機制現在對您并不重要。重要的是父進程必須以某種方式確認子進程的終止。子進程從終止時起就一直處于僵死狀態,直到父進程確認該信號為止。僵死進程不運行或消耗 CPU 周期;它只是占用進程表空間。當父進程終止時,內核最終能夠回收未確認的子進程以及父進程。這意味著可消除僵死進程的唯一方法是終止父進程。處理僵死進程的最好方法是首先確保它們不會發生。清單 8 中的代碼實現了一個處理傳入的 SIGCHLD 信號的信號處理程序。

清單 8. 實際操作中的信號處理程序

#include <unistd.h>#include <stdio.h>#include <sys/types.h>#include <sys/wait.h>void sighandler(int sig) {printf("In signal handler for signal %dn", sig);/* wait() is the key to acknowledging the SIGCHLD */wait(0);}int main(void) {int i;/* Assign a signal handler to SIGCHLD */sigset(SIGCHLD, &sighandler);if (!fork()) {/* Child */_exit(0);}sleep(60);}sunbox$ gcc dIE3.c -o die3sunbox$ ./die3 &[1] 3116sunbox$ In signal handler for signal 18ps -ef | grep 3116sean 3116 2885  0 22:37:26 pts/1  0:00 ./die3

由于使用了 sigset 函數(它向信號處理程序分配一個函數指針),清單 8 比前一個示例稍微復雜一點,。每當進程接收到某個已處理的信號時,就會調用通過 sigset 分配的函數。對于 SIGCHLD 信號,應用程序必須調用 wait(3c) 函數,以等待子進程退出。由于該進程已經退出,這相當于向內核確認了子進程的終止。實際上,父進程所做的工作可能不只是確認該信息。它還可能需要清理子進程的數據。

在執行 die3 以后,代碼檢查了進程清單,并干凈地執行子進程。然后使用值 18 (SIGCHLD) 來調用信號處理程序,確認子進程的退出,并且父進程返回到 sleep(60)。

總結

Unix 進程是在某個進程調用 fork 時創建的,fork 將正在運行的可執行進程一分為二。然后該進程可以執行 exec 系列中的某個系統調用,從而將當前運行的映像替換為新的映像。

當父進程終止時,其所有子進程將由 PID 為 1 的 init 接納。如果子進程在父進程之前終止,則會向父進程發送一個信號,然后子進程轉變為僵死狀態,直到該信號得到確認,或父進程被終止。

現在您已了解了進程是如何創建和銷毀的,您已經為處理運行您系統的進程作了更好的準備,尤其是大量使用多進程的系統,例如 Apache。如果您需要執行某些故障排除,能夠跟蹤某個特定進程的進程樹還允許您將任何應用程序追溯到創建它的進程。

標簽: Unix系統
日本不卡不码高清免费观看,久久国产精品久久w女人spa,黄色aa久久,三上悠亚国产精品一区二区三区
91欧美日韩在线| 不卡专区在线| 国产精品magnet| 亚洲性色视频| 久久狠狠久久| 伊人www22综合色| 成人av动漫在线观看| 欧美精品福利| 中文字幕一区二区三区在线视频| 久久午夜影视| 99久久久久| 伊人成人在线视频| 天堂精品久久久久| 国产精品伦理久久久久久| 亚洲我射av| 国产亚洲高清视频| 久久久国产精品网站| 青青草91久久久久久久久| 韩国三级一区| 正在播放日韩精品| 国产精品国产三级国产在线观看| 国产精品嫩模av在线| 欧美久久一区二区三区| 97久久超碰| 国产精品主播在线观看| 国产精品九九| 欧美成人精品一级| 日本色综合中文字幕| 国产精品一线| 日韩精品午夜视频| 国产亚洲人成a在线v网站 | 奶水喷射视频一区| 国产亚洲网站| 亚洲综合中文| 国产精品igao视频网网址不卡日韩 | 午夜国产欧美理论在线播放| 久久久五月天| 亚洲日产国产精品| 免费成人性网站| 婷婷综合国产| 国产极品久久久久久久久波多结野 | 日韩精品一级| 你懂的国产精品永久在线| 日本黄色精品| 久久久久午夜电影| 亚洲欧洲日本mm| 久久久久99| 中文字幕av一区二区三区四区| 日韩成人一级| 麻豆理论在线观看| 亚洲高清久久| 欧美+日本+国产+在线a∨观看| 日韩中文字幕一区二区高清99| 精品国产一区二区三区噜噜噜| 性欧美xxxx免费岛国不卡电影| 日韩精彩视频在线观看| 伊人久久在线| 日本一区二区三区中文字幕| 九九精品调教| 欧美日一区二区三区在线观看国产免| 樱桃视频成人在线观看| 天堂久久一区| 国产一区二区中文| 久久精品国产久精国产爱| 亚洲欧美日本视频在线观看| 久久99免费视频| 亚洲欧洲一区二区天堂久久| 久久精品国产999大香线蕉| 老牛影视一区二区三区| 伊人网在线播放| 青青青国产精品| 欧美 日韩 国产精品免费观看| 国产精品hd| 亚洲日本国产| av不卡免费看| 久久婷婷av| 狂野欧美性猛交xxxx| 午夜精品福利影院| 亚洲精品一区二区妖精| 亚洲伊人av| 你懂的国产精品| 爽好多水快深点欧美视频| 久久久久久免费视频| 风间由美中文字幕在线看视频国产欧美| 亚洲精品三级| 亚洲五月婷婷| 国产成人精品一区二区免费看京| 日韩二区三区在线观看| 中文字幕系列一区| 国产高清精品二区| 亚洲精品一级二级三级| 欧美在线资源| 色综合www| 国产中文欧美日韩在线| 国产精品一页| 久久激情综合网| 国产视频欧美| 午夜av成人| 欧美www视频在线观看| 国产免费av国片精品草莓男男| 亚洲九九精品| 午夜在线精品| 国产精品日本欧美一区二区三区| 激情综合自拍| 激情婷婷久久| 日本精品影院| 久久国产欧美| 免费污视频在线一区| 在线中文字幕播放| 国产精品麻豆久久| 国产成人在线中文字幕| 国产精品一级| 国产精品一区高清| 欧美日韩一区自拍| 青青草精品视频| 欧美日韩一区二区三区在线电影| 日韩一区二区三区四区五区| 日本亚洲最大的色成网站www| 亚洲狼人精品一区二区三区| 亚洲最新av| 日韩av一区二区三区四区| 日本免费一区二区视频| 欧美一区久久| 国产精品一级在线观看| 国产精品欧美三级在线观看| 你懂的国产精品永久在线| 国产精品高清一区二区| 免费在线观看一区| 激情综合五月| 精品国模一区二区三区| 欧美99久久| 亚洲欧美日韩在线观看a三区| 免费精品视频| 日本在线成人| 国产精品久久久久久久久久妞妞 | 欧洲亚洲一区二区三区| 日本免费一区二区三区四区| 亚洲精品一级二级| 国产综合色产| 久久国产福利| 日韩高清欧美激情| 国产精品三级| 欧美激情国产在线| 韩日一区二区三区| 天堂成人国产精品一区| 日韩不卡一区二区| 国产精品久久久久久模特| 国产精品99久久精品| 亚洲播播91| 国产精品社区| 日韩久久99| 国产欧美一区二区三区米奇| 精品亚洲a∨一区二区三区18| 日韩久久精品| 999在线观看精品免费不卡网站| 亚洲精品第一| 国产精品magnet| 韩国三级一区| 蜜臀精品久久久久久蜜臀| 国产精品一区二区99| 电影亚洲精品噜噜在线观看| 国产手机视频一区二区 | 精品日韩视频| 99在线|亚洲一区二区| 日韩精品中文字幕一区二区| 久久久久亚洲精品中文字幕| 久久精品国产www456c0m| 亚洲毛片在线免费| 久久久久亚洲精品中文字幕| 天堂网在线观看国产精品| 欧美日韩一区二区三区不卡视频| 国产一区二区三区精品在线观看| 婷婷综合社区| 国产精品欧美三级在线观看| 欧美日韩一二三四| 欧美日本一区| av亚洲一区二区三区| 亚洲精品麻豆| 黑森林国产精品av| 免费不卡在线观看| 久久久久久久欧美精品| 性色一区二区| 国产欧美精品久久| 久久一区二区三区电影| 日韩三级视频| 另类专区亚洲| 首页国产欧美日韩丝袜| 久久精品国产99国产| 在线亚洲激情| 精品国产精品久久一区免费式| 黄色成人精品网站| 卡一卡二国产精品| 亚洲欧美不卡| 国产一区国产二区国产三区| 免费成人av在线播放| 韩国久久久久久| 亚洲区第一页| 香蕉久久99| 国产精品成人国产| 久久不射中文字幕|