复习(三)—— 进程管理详解
文章目錄
- 一、進(jìn)程概念
- 1.1、進(jìn)程與程序
- 1.2、進(jìn)程結(jié)構(gòu)
- 1.3、進(jìn)程三種基本狀態(tài)
- 1.4、進(jìn)程三種狀態(tài)間的轉(zhuǎn)換
- 1.5、進(jìn)程標(biāo)識(shí)
- 二、進(jìn)程創(chuàng)建
- 2.1、fork系統(tǒng)調(diào)用
- 2.2、exec族
- 三、exit和_exit
- 3.1、exit和_exit函數(shù)說(shuō)明
- 3.2、exit和_exit函數(shù)語(yǔ)法
- 四、wait和waitpid
- 4.1、wait和waitpid函數(shù)說(shuō)明
- 4.2、wait和waitpid函數(shù)說(shuō)明
- 4.3、使用實(shí)例
- 五、進(jìn)程間通信
- 5.1、pipe管道和FIFO有名管道
- 5.2、信號(hào)
- 5.3、消息隊(duì)列
- 5.4、共享內(nèi)存映射
- 六、守護(hù)進(jìn)程
- 6.1、概念
- 6.2、 模型(守護(hù)進(jìn)程編程步驟)
一、進(jìn)程概念
1.1、進(jìn)程與程序
程序只是一個(gè)普通文件,是一個(gè)機(jī)器代碼指令和數(shù)據(jù)的集合,這些指令和數(shù)據(jù)存儲(chǔ)在磁盤上的一個(gè)可執(zhí)行映象中,所以,程序是一個(gè)靜態(tài)的實(shí)體。簡(jiǎn)單的來(lái)說(shuō),程序是存放在磁盤文件中的可執(zhí)行文件。
程序代表你期望完成某工作的計(jì)劃和步驟,它還浮在紙面上,等待具體實(shí)現(xiàn)。而具體的實(shí)現(xiàn)過(guò)程就是有進(jìn)程來(lái)完成的,進(jìn)程可以理解人為是執(zhí)行中的程序,它除了包含程序中的所有內(nèi)容外,還包含一些額外的數(shù)據(jù)。簡(jiǎn)單的理解就是:進(jìn)程是程序的執(zhí)行實(shí)例。
1.2、進(jìn)程結(jié)構(gòu)
Linux系統(tǒng)是一個(gè)多進(jìn)程的系統(tǒng),進(jìn)程之間具有并行性、互不干擾的特點(diǎn)。
Linux中進(jìn)程包含PCB(進(jìn)程控制塊)、程序以及程序所操縱的數(shù)據(jù)結(jié)構(gòu)集,可分為“代碼段”、“數(shù)據(jù)段”和“堆棧段”。
進(jìn)程是程序的一次執(zhí)行,是運(yùn)行在自己的虛擬地址空間的一個(gè)具有獨(dú)立功能的程序。進(jìn)程是分配和釋放資源的基本單位當(dāng)程序執(zhí)行時(shí),系統(tǒng)創(chuàng)建進(jìn)程,分配內(nèi)存和CPU等資源;進(jìn)程結(jié)束時(shí),系統(tǒng)回收這些資源。進(jìn)程由PCB(進(jìn)程控制塊)來(lái)描述。
1.3、進(jìn)程三種基本狀態(tài)
進(jìn)程在運(yùn)行中不斷地改變其運(yùn)行狀態(tài)。通常,一個(gè)運(yùn)行進(jìn)程必須具有以下三種基本狀態(tài)。
就緒(Ready)狀態(tài)
當(dāng)進(jìn)程已分配到除CPU以外的所有必要的資源,只要獲得處理機(jī)便可立即執(zhí)行,這時(shí)的進(jìn)程狀態(tài)稱為就緒狀態(tài)。
執(zhí)行(Running)狀態(tài)
當(dāng)進(jìn)程已獲得處理機(jī),其程序正在處理機(jī)上執(zhí)行,此時(shí)的進(jìn)程狀態(tài)稱為執(zhí)行狀態(tài)。
阻塞(Blocked)狀態(tài)
正在執(zhí)行的進(jìn)程,由于等待某個(gè)事件發(fā)生而無(wú)法執(zhí)行時(shí),便放棄處理機(jī)而處于阻塞狀態(tài)。引起進(jìn)程阻塞的事件可有多種,例如,等待I/O完成、申請(qǐng)緩沖區(qū)不能滿足、等待信件(信號(hào))等。
1.4、進(jìn)程三種狀態(tài)間的轉(zhuǎn)換
一個(gè)進(jìn)程在運(yùn)行期間,不斷地從一種狀態(tài)轉(zhuǎn)換到另一種狀態(tài),它可以多次處于就緒狀態(tài)和執(zhí)行狀態(tài),也可以多次處于阻塞狀態(tài)。下圖描述了進(jìn)程的三種基本狀態(tài)及其轉(zhuǎn)換。
(1) 就緒→執(zhí)行
處于就緒狀態(tài)的進(jìn)程,當(dāng)進(jìn)程調(diào)度程序?yàn)橹峙淞颂幚頇C(jī)后,該進(jìn)程便由就緒狀態(tài)轉(zhuǎn)變成執(zhí)行狀態(tài)。
(2) 執(zhí)行→就緒
處于執(zhí)行狀態(tài)的進(jìn)程在其執(zhí)行過(guò)程中,因分配給它的一個(gè)時(shí)間片已用完而不得不讓出處理機(jī),于是進(jìn)程從執(zhí)行狀態(tài)轉(zhuǎn)變成就緒狀態(tài)。
(3) 執(zhí)行→阻塞
正在執(zhí)行的進(jìn)程因等待某種事件發(fā)生而無(wú)法繼續(xù)執(zhí)行時(shí),便從執(zhí)行狀態(tài)變成阻塞狀態(tài)。
(4) 阻塞→就緒
處于阻塞狀態(tài)的進(jìn)程,若其等待的事件已經(jīng)發(fā)生,于是進(jìn)程由阻塞狀態(tài)轉(zhuǎn)變?yōu)榫途w狀態(tài)。
1.5、進(jìn)程標(biāo)識(shí)
操作系統(tǒng)會(huì)為每一個(gè)進(jìn)程分配一個(gè)唯一的整型ID,作為進(jìn)程的標(biāo)識(shí)(pid)。進(jìn)程除了自身的ID外,還有父進(jìn)程ID,所有進(jìn)程的祖先進(jìn)程是同一個(gè)進(jìn)程,它叫做init進(jìn)程,ID為 1,init 進(jìn)程是內(nèi)核自舉后的一個(gè)啟動(dòng)進(jìn)程。
獲取進(jìn)程標(biāo)識(shí)相關(guān)函數(shù)
#include <sys/types.h> #include <unistd.h>pid_t getpid(void); 返回:調(diào)用進(jìn)程的進(jìn)程ID pid_t getppid(void); 返回:調(diào)用進(jìn)程的父進(jìn)程ID uid_t getuid(void); 返回:調(diào)用進(jìn)程的實(shí)際用戶ID uid_t geteuid(void); 返回:調(diào)用進(jìn)程的有效用戶ID gid_t getgid(void); 返回:調(diào)用進(jìn)程的實(shí)際組ID gid_t getegid(void); 返回:調(diào)用進(jìn)程的有效組ID二、進(jìn)程創(chuàng)建
2.1、fork系統(tǒng)調(diào)用
#include <unistd.h>pid_t fork(void); 返回值:子進(jìn)程中為0,父進(jìn)程中為子進(jìn)程I D,出錯(cuò)為-1說(shuō)明:
fork函數(shù)用于從已存在進(jìn)程中創(chuàng)建一個(gè)新進(jìn)程。新進(jìn)程稱為子進(jìn)程,而原進(jìn)程稱為父進(jìn)程。
注意:
1、子進(jìn)程復(fù)制父進(jìn)程的0~3g空間和父進(jìn)程內(nèi)核中的PCB,但id號(hào)不同。
2、fork調(diào)用一次返回兩次:
① 父進(jìn)程中返回子進(jìn)程ID
② 子進(jìn)程中返回0
3、讀時(shí)共享,寫時(shí)復(fù)制
4、使用fork函數(shù)得到的子進(jìn)程從父進(jìn)程的繼承了整個(gè)進(jìn)程的地址空間,包括:進(jìn)程上下文、進(jìn)程堆棧、內(nèi)存信息、打開(kāi)的文件描述符、信號(hào)控制設(shè)置、進(jìn)程優(yōu)先級(jí)、進(jìn)程組號(hào)、當(dāng)前工作目錄、根目錄、資源限制、控制終端等。
5、fork系統(tǒng)調(diào)用之后,父子進(jìn)程將交替執(zhí)行。如果父進(jìn)程先退出,子進(jìn)程還沒(méi)退出那么子進(jìn)程的父進(jìn)程將變?yōu)閕nit進(jìn)程。(注:任何一個(gè)進(jìn)程都必須有父進(jìn)程)如果子進(jìn)程先退出,父進(jìn)程還沒(méi)退出,那么子進(jìn)程必須等到父進(jìn)程捕獲到了子進(jìn)程的退出狀態(tài)才真正結(jié)束,否則這個(gè)時(shí)候子進(jìn)程就成為僵進(jìn)程。
子進(jìn)程與父進(jìn)程的區(qū)別:
1、父進(jìn)程設(shè)置的鎖,子進(jìn)程不繼承
2、各自的進(jìn)程ID和父進(jìn)程ID不同
3、子進(jìn)程的未決告警被清除;
4、子進(jìn)程的未決信號(hào)集設(shè)置為空集
示例:
#include <sys/types.h> #include <unistd.h> #include <stdio.h> #include <stdlib.h>int main(void) {pid_t result;/*調(diào)用fork函數(shù),其返回值為result*/result = fork();/*通過(guò)result的值來(lái)判斷fork函數(shù)的返回情況,首先進(jìn)行出錯(cuò)處理*/if(result == -1){perror("fork");exit;}/*返回值為0代表子進(jìn)程*/else if(result == 0){printf("The return value is %d\nIn child process!\nMy PID is %d\n",result,getpid());}else{printf("The return value is %d\nIn father process!\nMy PID is %d\n",result,getpid());}return 0; }運(yùn)行結(jié)果:
The return value is 0 In child process! My PID is 27424 The return value is 27424 In father process! My PID is 274232.2、exec族
用fork創(chuàng)建子進(jìn)程后執(zhí)行的是和父進(jìn)程相同的程序(但有可能執(zhí)行不同的代碼分支),子進(jìn)程往往要調(diào)用一種exec函數(shù)以執(zhí)行另一個(gè)程序。當(dāng)進(jìn)程調(diào)用一種exec函數(shù)時(shí),該進(jìn)程的用戶空間代碼和數(shù)據(jù)完全被新程序替換,從新程序的啟動(dòng)例程開(kāi)始執(zhí)行。調(diào)用exec并不創(chuàng)建新進(jìn)程,所以調(diào)用exec前后該進(jìn)程的id并未改變。
exec 族語(yǔ)法
實(shí)際上,在Linux中并沒(méi)有exec函數(shù),而是有6個(gè)以exec開(kāi)頭的函數(shù)族,它們之間語(yǔ)法有細(xì)微差別,以下列舉了exec函數(shù)的6個(gè)成員函數(shù)語(yǔ)法:
這些函數(shù)如果調(diào)用成功則加載新的程序從啟動(dòng)代碼開(kāi)始執(zhí)行,不再返回,如果調(diào)用出錯(cuò)則返回-1,所以exec函數(shù)只有出錯(cuò)的返回值而沒(méi)有成功的返回值。
記憶這6個(gè)函數(shù)
這六個(gè)exec函數(shù)的參數(shù)很難記憶。函數(shù)名中的字符會(huì)給我們一些幫助:
①字母 p表示可以只給出可執(zhí)行文件名,系統(tǒng)會(huì)自動(dòng)按照PATH環(huán)境變量所指定的路徑尋找可執(zhí)行文件,它與字母e互斥。
②字母l表示該函數(shù)使用一個(gè)參數(shù)列表傳遞參數(shù),它與字母v互斥。
③字母v表示該函數(shù)使用一個(gè)char * argv[ ]傳遞參數(shù)。
④字母e表示該函數(shù)使用char * envp[ ] 傳遞環(huán)境量變,而不使用當(dāng)前環(huán)境。
注意事項(xiàng):
由exec啟動(dòng)的新進(jìn)程繼承了原進(jìn)程的許多東西,已經(jīng)打開(kāi)了的文件描述符在新進(jìn)程里仍將是打開(kāi)的,除非它們的“exec 調(diào)用時(shí)關(guān)閉此文件”標(biāo)志被置了位
exec使用實(shí)例:
//使用文件名的方式來(lái)查找可執(zhí)行文件,同時(shí)使用參數(shù)列表的方式 if(fork()==0){ /*調(diào)用execlp 函數(shù),這里相當(dāng)于調(diào)用了“ps-f”命令*/if (execlp("ps","ps","-ef",NULL)<0){perror("execlp error!");exit(1);} }--------------------------------------- //使用完整的文件目錄來(lái)查找對(duì)應(yīng)的可執(zhí)行文件 if(fork()==0){/*調(diào)用execl 函數(shù),注意這里給出ps程序的完整路徑*/if (execl("/bin/ps","ps","-ef",NULL)<0){perror("execl error!");exit(1);} }--------------------------------------- //將環(huán)境變量添加到新建的子進(jìn)程中去 env:查看當(dāng)前進(jìn)程環(huán)境變量 /*命令參數(shù)列表,必須以NULL結(jié)尾*/ char *envp[]={"PATH=/tmp","USER=sunnq",NULL}; if(fork()==0){/*調(diào)用execle 函數(shù),注意這里也要指出env的完整路徑*/if (execle("/bin/env","env",NULL,envp)<0){perror("execle error!");exit(1);} }--------------------------------------- //通過(guò)構(gòu)造指針數(shù)組的方式來(lái)傳遞參數(shù),注意參數(shù)列表一定要以NULL作為結(jié)尾標(biāo)識(shí)符 char*arg[]={"ls", "-a", NULL}; if(fork()==0){if (execve("/bin/ls",arg,NULL)<0){perror("execve error!");exit(1);} }exec族使用注意點(diǎn):
在使用exec函數(shù)時(shí),最好加上錯(cuò)誤判斷語(yǔ)句。因?yàn)閑xec很容易執(zhí)行失敗,其中最常見(jiàn)的原因有:
①找不到文件路徑,此時(shí)error被設(shè)置為ENOENT;
②數(shù)組argv和envp忘記用NULL結(jié)束,此時(shí)error被設(shè)置為EFALUT;
③沒(méi)有對(duì)應(yīng)可執(zhí)行文件的運(yùn)行權(quán)限,此時(shí)error被設(shè)置為EACCESS;
事實(shí)上,這6個(gè)函數(shù)中真正的系統(tǒng)調(diào)用只有execve,其他5個(gè)都是庫(kù)函數(shù),它們最終都會(huì)調(diào)用execve這個(gè)系統(tǒng)調(diào)用。
三、exit和_exit
3.1、exit和_exit函數(shù)說(shuō)明
exit和_exit用于中止進(jìn)程;當(dāng)程序執(zhí)行到exit或_exit時(shí),進(jìn)程會(huì)無(wú)條件地停止剩下的所有操作,清楚包括PCB 在內(nèi)的各種數(shù)據(jù)結(jié)構(gòu),并終止本進(jìn)程的運(yùn)行。但是,這兩個(gè)函數(shù)還是有區(qū)別的,這連個(gè)函數(shù)的調(diào)用過(guò)程如下圖所示:
由圖可以看出,_exit的作用:直接使進(jìn)程停止運(yùn)行,清除其使用的內(nèi)存空間,并清除其在內(nèi)核中的數(shù)據(jù)結(jié)構(gòu);
exit與_exit函數(shù)不同,exit函數(shù)在調(diào)用exit系統(tǒng)之前要檢查文件打開(kāi)情況,把文件緩沖區(qū)的內(nèi)容寫回文件中去,就是圖中的“清理I/O緩沖”一項(xiàng)
3.2、exit和_exit函數(shù)語(yǔ)法
#include <stdlib.h> //exit #include <unistd.h> //_exitvoid exit(int status) void _exit(int status)參數(shù): status: 0 代表正常結(jié)束;其他數(shù)值表示出現(xiàn)了錯(cuò)誤,進(jìn)程非正常結(jié)束四、wait和waitpid
僵尸進(jìn)程: 子進(jìn)程退出,父進(jìn)程沒(méi)有回收子進(jìn)程資源(PCB),則子進(jìn)程變成僵尸進(jìn)程。
孤兒進(jìn)程: 父進(jìn)程先于子進(jìn)程結(jié)束,則子進(jìn)程成為孤兒進(jìn)程,子進(jìn)程的父進(jìn)程成為1號(hào)。
init進(jìn)程:1號(hào)進(jìn)程,負(fù)責(zé)收留孤兒進(jìn)程,成為他們的父進(jìn)程。
4.1、wait和waitpid函數(shù)說(shuō)明
wait函數(shù)用于使父進(jìn)程(也就是調(diào)用wait的進(jìn)程)阻塞,直到一個(gè)子進(jìn)程結(jié)束或者該進(jìn)程接收到一個(gè)指定信號(hào)為止。如果該父進(jìn)程沒(méi)有子進(jìn)程或者他的子進(jìn)程已經(jīng)結(jié)束,則wait就會(huì)立即返回。
waitpid的作用和wait一樣,但它并不一定要等待第一個(gè)終止的子進(jìn)程,它還有若干選項(xiàng),如可提供一個(gè)非阻塞版本的wait功能,也能支持作業(yè)控制。實(shí)際上wait 函數(shù)只是waitpid 函數(shù)的一個(gè)特例,在Linux內(nèi)部實(shí)現(xiàn)wait函數(shù)時(shí)直接調(diào)用的就是wait函數(shù)。
4.2、wait和waitpid函數(shù)說(shuō)明
#include <sys/types.h> #include <sys/wait.h>pid_t wait(int *status); pid_t waitpid(pid_t pid, int *status, int options);pid是進(jìn)程號(hào) status:<-1 回收指定進(jìn)程組內(nèi)的任意子進(jìn)程-1 回收任意子進(jìn)程0 回收和當(dāng)前waitpid調(diào)用一個(gè)組的所有子進(jìn)程>0 回收指定ID的子進(jìn)程 options:WNOHANG:若由pid指定的子進(jìn)程不立即可用,則waitpid不阻塞,此時(shí)返回值為0WUNTRANCED:若實(shí)現(xiàn)某支持作業(yè)控制,則由pid指定的任一子進(jìn)程狀態(tài)已暫停,且其狀態(tài)自暫停以來(lái)還未報(bào)告過(guò),則返回其狀態(tài)0:同wait,阻塞父進(jìn)程,等待子進(jìn)程退出4.3、使用實(shí)例
#include <unistd.h> #include <stdio.h> #include <stdlib.h> #include <sys/types.h> #include <sys/wait.h>int main(void) {pid_t pc,pr;pc = fork();if (pc <0){printf("Error fork!\n");}/*子進(jìn)程*/else if(pc==0){/*子進(jìn)程暫停3秒*/Sleep(3);/*子進(jìn)程正常退出*/exit(0);}/*父進(jìn)程*/else{/*循環(huán)測(cè)試子進(jìn)程是否退出*/do {/*調(diào)用waitpid,且父進(jìn)程不阻塞*/pr=waitpid(pc,NULL,WNOHANG);/*若子進(jìn)程還未退出,則父進(jìn)程暫停1s*/if(pr==0){printf("The child process has not exited\n");Sleep(1);}} while (pr==0);/*若發(fā)現(xiàn)子進(jìn)程退出,打印出相應(yīng)情況*/if (pr==pc){printf("Get child%d\n",pr);}else{printf("some error occured\n");}}return 0; }執(zhí)行結(jié)果:
The child process has not exited The child process has not exited The child process has not exited Get child32225五、進(jìn)程間通信
5.1、pipe管道和FIFO有名管道
具體內(nèi)容看我博客:管道
5.2、信號(hào)
看我博客:信號(hào)
5.3、消息隊(duì)列
消息隊(duì)列(Message Queue),是分布式系統(tǒng)中重要的組件,其通用的使用場(chǎng)景可以簡(jiǎn)單地描述為:當(dāng)不需要立即獲得結(jié)果,但是并發(fā)量又需要進(jìn)行控制的時(shí)候,差不多就是需要使用消息隊(duì)列的時(shí)候。消息隊(duì)列主要解決了應(yīng)用耦合、異步處理、流量削鋒等問(wèn)題。當(dāng)前使用較多的消息隊(duì)列有RabbitMQ、RocketMQ、ActiveMQ、Kafka、ZeroMQ、MetaMq等,而部分?jǐn)?shù)據(jù)庫(kù)如Redis、Mysql以及phxsql也可實(shí)現(xiàn)消息隊(duì)列的功能。
消息隊(duì)列與管道以及有名管道相比,具有更大的靈話性,首先,它提供有格式字節(jié)流,有利于
減少開(kāi)發(fā)人員的工作量:其次,消息具有類型,在實(shí)際應(yīng)用中,可作為優(yōu)先級(jí)使用。這兩點(diǎn)是管道以及有名管道所不能比的。同樣,消息隊(duì)列可以在幾個(gè)進(jìn)程間復(fù)用,而不管這幾個(gè)進(jìn)程是否具有親緣關(guān)系,這一點(diǎn)與有名管道很相似:但消息隊(duì)列是隨內(nèi)核持續(xù)的,與有名管道(隨進(jìn)程持續(xù))相比,生命力更強(qiáng),應(yīng)用空間更大。
5.4、共享內(nèi)存映射
采用共享內(nèi)存通信的一個(gè)顯而易見(jiàn)的好處是效率高,因?yàn)檫M(jìn)程可以直接讀寫內(nèi)存,而不需要任何數(shù)據(jù)的拷貝。對(duì)于像管道和消息隊(duì)列等通信方式,則需要在內(nèi)核和用戶空間進(jìn)行四次的數(shù)據(jù)拷貝,而共享內(nèi)存則只拷貝兩次數(shù)據(jù)[1]: 一次從輸入文件到共享內(nèi)存區(qū),另一次從共享內(nèi)存區(qū)到輸出文件。
實(shí)際上,進(jìn)程之間在共享內(nèi)存時(shí),并不總是讀寫少量數(shù)據(jù)后就解除映射,有新的通信時(shí),再重新建立共享內(nèi)存區(qū)域。而是保持共享區(qū)域,直到通信完畢為止,這樣,數(shù)據(jù)內(nèi)容直保存在共享內(nèi)存中,并沒(méi)有寫回文件。共享內(nèi)存中的內(nèi)容往往是在解除映射時(shí)才寫回文件的。因此采用共李內(nèi)存的通信方式效率是非常高的。
Linux的2.2.x內(nèi)核支持多種共享內(nèi)存方式,如mmap()系統(tǒng)調(diào)用,Posix 共享內(nèi)存,以及系統(tǒng)V共享內(nèi)存。linux發(fā)行版本如Redhat 8.0支持mmap()系統(tǒng)調(diào)用及系統(tǒng)V共享內(nèi)存,但還沒(méi)實(shí)現(xiàn)Posix共享內(nèi)存。
mmap/munmap
mmap可以把磁盤文件的一部分直接映射到內(nèi)存,這樣文件中的位置直接就有對(duì)應(yīng)的內(nèi)存地址,對(duì)文件的讀寫可以直接用指針來(lái)做而不需要read/write函數(shù)。
#include <sys/mman.h>void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset); int munmap(void *addr, size_t length);參數(shù):addr:為NULL,內(nèi)核會(huì)自己在進(jìn)程地址空間中選擇合適的地址建立映射。不為NULL,則給內(nèi)核一個(gè)提示,應(yīng)該從什么地址開(kāi)始映射,內(nèi)核會(huì)選擇addr之上的某個(gè)合適的地址開(kāi)始映射len:需要映射的那一部分文件的長(zhǎng)度。off:參從文件的什么位置開(kāi)始映射,必須是頁(yè)大小的整數(shù)倍(在32位體系統(tǒng)結(jié)構(gòu)上通常是4K)。filedes:代表該文件的描述符。prot:PROT_EXEC表示映射的這一段可執(zhí)行,例如映射共享庫(kù)PROT_READ表示映射的這一段可讀PROT_WRITE表示映射的這一段可寫PROT_NONE表示映射的這一段不可訪問(wèn)flag:(這里只寫了兩種)MAP_SHARED多個(gè)進(jìn)程對(duì)同一個(gè)文件的映射是共享的,一個(gè)進(jìn)程對(duì)映射的內(nèi)存做了修改,另一個(gè)進(jìn)程也會(huì)看到這種變化。MAP_PRIVATE多個(gè)進(jìn)程對(duì)同一個(gè)文件的映射不是共享的,一個(gè)進(jìn)程對(duì)映射的內(nèi)存做了修改,另一個(gè)進(jìn)程并不會(huì)看到這種變化,也不會(huì)真的寫到文件中去。返回值:成功則返回映射首地址,如果出錯(cuò)則返回常數(shù)MAP_FAILED當(dāng)進(jìn)程終止時(shí),該進(jìn)程的映射內(nèi)存會(huì)自動(dòng)解除,也可以調(diào)用munmap解除映射。munmap成功返回0,出錯(cuò)返回 -1。
示例:
#include <stdlib.h> #include <sys/mman.h> #include <fcntl.h> int main(void) {int *p;int fd = open("file.txt", O_RDWR);if (fd < 0) {perror("open hello");exit(1);}p = mmap(NULL, 6, PROT_WRITE, MAP_SHARED, fd, 0);if (p == MAP_FAILED) {perror("mmap");exit(1);}close(fd);munmap(p, 6);return 0; }注意:
- 用于進(jìn)程間通信時(shí),一般設(shè)計(jì)成結(jié)構(gòu)體,來(lái)傳輸通信的數(shù)據(jù)
- 進(jìn)程間通信的文件,應(yīng)該設(shè)計(jì)成臨時(shí)文件
- 當(dāng)報(bào)總線錯(cuò)誤時(shí),優(yōu)先查看共享文件是否有存儲(chǔ)空間(即文件里是否有數(shù)據(jù))
六、守護(hù)進(jìn)程
6.1、概念
守護(hù)進(jìn)程,也就是通常所說(shuō)的 daemom(精靈) 進(jìn)程,是Linux中的后臺(tái)服務(wù)進(jìn)程,生存期較長(zhǎng)的進(jìn)程,通常獨(dú)立于控制終端并且周期性地執(zhí)行某種任務(wù)或等待處理某些發(fā)生的事件。
守護(hù)進(jìn)程是在后臺(tái)運(yùn)行不受終端控制的進(jìn)程。
守護(hù)進(jìn)程能自動(dòng)轉(zhuǎn)到后臺(tái)并且脫離與終端的聯(lián)系。
Linux系統(tǒng)中一般有很多守護(hù)進(jìn)程在后臺(tái)運(yùn)行,執(zhí)行不同的管理任務(wù)。
6.2、 模型(守護(hù)進(jìn)程編程步驟)
1. 創(chuàng)建子進(jìn)程,父進(jìn)程退出所有工作在子進(jìn)程中進(jìn)行形式上脫離了控制終端 2. 在子進(jìn)程中創(chuàng)建新會(huì)話setsid()函數(shù)使子進(jìn)程完全獨(dú)立出來(lái),脫離控制 3. 改變當(dāng)前目錄為根目錄chdir()函數(shù)防止占用可卸載的文件系統(tǒng)也可以換成其它路徑 4. 重設(shè)文件權(quán)限掩碼umask()函數(shù)防止繼承的文件創(chuàng)建屏蔽字拒絕某些權(quán)限增加守護(hù)進(jìn)程靈活性 5. 關(guān)閉文件描述符繼承的打開(kāi)文件不會(huì)用到,浪費(fèi)系統(tǒng)資源,無(wú)法卸載 6. 開(kāi)始執(zhí)行守護(hù)進(jìn)程核心工作 7. 守護(hù)進(jìn)程退出處理代碼模型
#include <stdlib.h> #include <stdio.h> #include <fcntl.h>void daemonize(void) {pid_t pid;/** 成為一個(gè)新會(huì)話的首進(jìn)程,失去控制終端*/if ((pid = fork()) < 0) {perror("fork");exit(1);} else if (pid != 0) /* parent */exit(0);setsid();/** 改變當(dāng)前工作目錄到/目錄下.*/if (chdir("/") < 0) {perror("chdir");exit(1);}/* 設(shè)置umask為0 */umask(0);/** 重定向0,1,2文件描述符到 /dev/null,因?yàn)橐呀?jīng)失去控制終端,再操作0,1,2沒(méi)有意義.*/close(0);open("/dev/null", O_RDWR);dup2(0, 1);dup2(0, 2); } int main(void) {daemonize();while(1); /* 在此循環(huán)中可以實(shí)現(xiàn)守護(hù)進(jìn)程的核心工作 */ }注意:運(yùn)行這個(gè)程序,它變成一個(gè)守護(hù)進(jìn)程,不再和當(dāng)前終端關(guān)聯(lián)。用ps命令看不到,必須運(yùn)行帶x參數(shù)的ps命令才能看到。另外還可以看到,用戶關(guān)閉終端窗口或注銷也不會(huì)影響守護(hù)進(jìn)程的運(yùn)行。
總結(jié)
以上是生活随笔為你收集整理的复习(三)—— 进程管理详解的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 学习笔记——pygame最常用的15个模
- 下一篇: 学习笔记——itertools模块