电脑知识|欧美黑人一区二区三区|软件|欧美黑人一级爽快片淫片高清|系统|欧美黑人狂野猛交老妇|数据库|服务器|编程开发|网络运营|知识问答|技术教程文章 - 好吧啦网

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

UNIX 進程揭秘

瀏覽:4日期: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系統
主站蜘蛛池模板: 超声波_清洗机_超声波清洗机专业生产厂家-深圳市好顺超声设备有限公司 | 酒水灌装机-白酒灌装机-酒精果酒酱油醋灌装设备_青州惠联灌装机械 | 仿古瓦,仿古金属瓦,铝瓦,铜瓦,铝合金瓦-西安东申景观艺术工程有限公司 | 东莞爱加真空科技有限公司-进口真空镀膜机|真空镀膜设备|Polycold维修厂家 | 密集柜_档案密集柜_智能密集架_密集柜厂家_密集架价格-智英伟业 密集架-密集柜厂家-智能档案密集架-自动选层柜订做-河北风顺金属制品有限公司 | 铝扣板-铝方通-铝格栅-铝条扣板-铝单板幕墙-佳得利吊顶天花厂家 elisa试剂盒价格-酶联免疫试剂盒-猪elisa试剂盒-上海恒远生物科技有限公司 | 耐高温硅酸铝板-硅酸铝棉保温施工|亿欧建设工程 | 石膏基自流平砂浆厂家-高强石膏基保温隔声自流平-轻质抹灰石膏粉砂浆批发-永康市汇利建设有限公司 | 匀胶机旋涂仪-声扫显微镜-工业水浸超声-安赛斯(北京)科技有限公司 | 砖机托板价格|免烧砖托板|空心砖托板厂家_山东宏升砖机托板厂 | SMC-ASCO-CKD气缸-FESTO-MAC电磁阀-上海天筹自动化设备官网 | 蔬菜清洗机_环速洗菜机_异物去除清洗机_蔬菜清洗机_商用洗菜机 - 环速科技有限公司 | 武汉高低温试验机-现货恒温恒湿试验箱-高低温湿热交变箱价格-湖北高天试验设备 | 宝宝药浴-产后药浴-药浴加盟-艾裕-专注母婴调养泡浴 | 成都顶呱呱信息技术有限公司-贷款_个人贷款_银行贷款在线申请 - 成都贷款公司 | TMT观察网_独特视角观察TMT行业| 宏源科技-房地产售楼系统|线上开盘系统|售楼管理系统|线上开盘软件 | 地图标注-手机导航电子地图如何标注-房地产商场地图标记【DiTuBiaoZhu.net】 | 广州展览设计公司_展台设计搭建_展位设计装修公司-众派展览装饰 广州展览制作工厂—[优简]直营展台制作工厂_展会搭建资质齐全 | 布袋式除尘器|木工除尘器|螺旋输送机|斗式提升机|刮板输送机|除尘器配件-泊头市德佳环保设备 | 杭州用友|用友软件|用友财务软件|用友ERP系统--杭州协友软件官网 | PAS糖原染色-CBA流式多因子-明胶酶谱MMP-上海研谨生物科技有限公司 | 骨龄仪_骨龄检测仪_儿童骨龄测试仪_品牌生产厂家【品源医疗】 | 超声波焊接机_超音波熔接机_超声波塑焊机十大品牌_塑料超声波焊接设备厂家 | 泉州陶瓷pc砖_园林景观砖厂家_石英砖地铺石价格 _福建暴风石英砖 | 交流伺服电机|直流伺服|伺服驱动器|伺服电机-深圳市华科星电气有限公司 | DAIKIN电磁阀-意大利ATOS电磁阀-上海乾拓贸易有限公司 | 探鸣起名网-品牌起名-英文商标起名-公司命名-企业取名包满意 | 轴承振动测量仪电箱-轴承测振动仪器-测试仪厂家-杭州居易电气 | 【灵硕展览集团】展台展会设计_展览会展台搭建_展览展示设计一站式服务公司 | 香蕉筛|直线|等厚|弧形|振动筛|香蕉筛厂家-洛阳隆中重工 | 润滑脂-高温润滑脂-轴承润滑脂-食品级润滑油-索科润滑油脂厂家 | 伺服电机维修、驱动器维修「安川|三菱|松下」伺服维修公司-深圳华创益 | 旗帜网络笔记-免费领取《旗帜网络笔记》电子书 | 桌上式超净工作台-水平送风超净工作台-上海康路仪器设备有限公司 | 手术室净化厂家-成都做医院净化工程的公司-四川华锐-15年特殊科室建设经验 | 变位机,焊接变位机,焊接变位器,小型变位机,小型焊接变位机-济南上弘机电设备有限公司 | 硫化罐_蒸汽硫化罐_大型硫化罐-山东鑫泰鑫智能装备有限公司 | 转子泵_凸轮泵_凸轮转子泵厂家-青岛罗德通用机械设备有限公司 | 地图标注-手机导航电子地图如何标注-房地产商场地图标记【DiTuBiaoZhu.net】 | 无线联网门锁|校园联网门锁|学校智能门锁|公租房智能门锁|保障房管理系统-KEENZY中科易安 |