Linux 進(jìn)程管理與程序開發(fā)
進(jìn)程是Linux事務(wù)管理的基本單元椎瘟,所有的進(jìn)程均擁有自己獨(dú)立的處理環(huán)境和系統(tǒng)資源,進(jìn)程的環(huán)境有當(dāng)前系統(tǒng)狀態(tài)及其父進(jìn)程信息決定和組成愈涩。
- 進(jìn)程環(huán)境及進(jìn)程屬性
- 進(jìn)程管理及控制
- LINUX 特殊進(jìn)程
7.1進(jìn)程環(huán)境及環(huán)境
7.1.1進(jìn)程資源
進(jìn)程是Linux系統(tǒng)下用戶層管理事物的基本單元望抽,每個(gè)進(jìn)程都有自己獨(dú)立的運(yùn)行空間。
為了更好的管理Linux所訪問的資源履婉,系統(tǒng)在內(nèi)核頭文件include/linux/sched.h中定義了結(jié)構(gòu)體 struct task_struct 來(lái)管理每個(gè)進(jìn)程的資源煤篙。
7.1.2 進(jìn)程狀態(tài)
雖然Linux操作系統(tǒng)是一個(gè)多用戶多任務(wù)的操作系統(tǒng),但對(duì)于單CPU系統(tǒng)來(lái)說毁腿,在某一個(gè)時(shí)刻辑奈,只能有一個(gè)進(jìn)程處于運(yùn)行狀態(tài),其他進(jìn)程都處于其他狀態(tài)已烤,等待系統(tǒng)資源鸠窗,各任務(wù)根據(jù)調(diào)度算法在這些狀態(tài)之間不停地切換。但由于CPU處理速率較快胯究,使用戶感覺每個(gè)進(jìn)程都完全獨(dú)立擁有整個(gè)系統(tǒng)塌鸯。
用戶級(jí)進(jìn)程擁有以下幾種狀態(tài):
- 就緒
- 運(yùn)行狀態(tài)
- 等待狀態(tài)(可以被中斷)
- 等待狀態(tài)(不可以被中斷)
- 停止?fàn)顟B(tài)
- 僵死狀態(tài)
7.1.3 進(jìn)程基本屬性
與進(jìn)程相關(guān)的屬性包括進(jìn)程號(hào)(PID)、父進(jìn)程號(hào)(PPID)唐片、進(jìn)程組號(hào)(PGID)丙猬。
1:進(jìn)程號(hào)(PID)
進(jìn)程號(hào)是系統(tǒng)維護(hù)的唯一標(biāo)識(shí)一個(gè)進(jìn)程的正數(shù)涨颜,進(jìn)程號(hào)是無(wú)法再用戶層修改的。在Linux系統(tǒng)中茧球,系統(tǒng)的第一個(gè)用戶進(jìn)程為init進(jìn)程庭瑰,它的PID為1,其他的進(jìn)程PID以此增加抢埋。
getpid函數(shù) 獲取當(dāng)前進(jìn)程的PID 該函數(shù)在unistdd.h文件中聲明
執(zhí)行失敗 -1 ,錯(cuò)誤原因存儲(chǔ)于errno中,pid_t類型其實(shí)就是int類型
2:父進(jìn)程號(hào)(PPID)
任何進(jìn)程(除init進(jìn)程)都是由另一進(jìn)程創(chuàng)建弹灭,該進(jìn)程稱為被創(chuàng)建進(jìn)程的父進(jìn)程,被創(chuàng)建的進(jìn)程稱為子進(jìn)程揪垄,父進(jìn)程號(hào)無(wú)法在用戶層修改穷吮,父進(jìn)程的進(jìn)程號(hào)(PID)即為子進(jìn)程的父進(jìn)程號(hào)(PPID),用戶可以通過調(diào)用getppid函數(shù)來(lái)獲取當(dāng)前進(jìn)程的父進(jìn)程號(hào)(PPID)饥努,其函數(shù)定義在/unistd.h文件中捡鱼。
3: 進(jìn)程組號(hào)(PGID)
在Linux系統(tǒng)中,每個(gè)用戶都擁有用戶號(hào)(UID)和用戶組號(hào)(GUID);和用戶管理一樣酷愧,進(jìn)程也擁有自己的進(jìn)程號(hào)(PID)和進(jìn)程組號(hào)(PGID)驾诈。進(jìn)程組是一個(gè)或者多個(gè)進(jìn)程的集合。
它們與同一作業(yè)相關(guān)聯(lián)溶浴,可以接受來(lái)自同一終端的各種信號(hào)乍迄,每個(gè)進(jìn)程組都有唯一的進(jìn)程組號(hào),進(jìn)程組號(hào)可以在用戶層修改士败。
用戶可以通過調(diào)用 getpgid()函數(shù)來(lái)獲得指定的進(jìn)程的進(jìn)程組號(hào)(PGID)闯两。其函數(shù)定義在 unistd.h文件中
如果執(zhí)行失敗則返回 -1 ,錯(cuò)誤原因存儲(chǔ)在errno中谅将。
每個(gè)進(jìn)程組都可以有一個(gè)組長(zhǎng)進(jìn)程漾狼,組長(zhǎng)進(jìn)程的進(jìn)程組號(hào)等于其他進(jìn)程號(hào)。但組長(zhǎng)進(jìn)程可以先退出戏自,即只要在某個(gè)進(jìn)程中有一個(gè)進(jìn)程存在邦投,則該進(jìn)程組就存在伤锚,與其組長(zhǎng)進(jìn)程是否終止無(wú)關(guān)擅笔。進(jìn)程組的最后一個(gè)進(jìn)程可以終止,或者裝一道另一個(gè)進(jìn)程組屯援。
加入一個(gè)現(xiàn)有的組或者一個(gè)新的進(jìn)程組的系統(tǒng)調(diào)用函數(shù)setpgid(),其聲明如下:
int setpgid(pid_t pid猛们,pid_t pgid );
4:會(huì)話
會(huì)話(session)是一個(gè)或多個(gè)進(jìn)程組的集合。系統(tǒng)調(diào)用函數(shù)getsid用來(lái)獲取某個(gè)進(jìn)程的會(huì)話號(hào)SID
函數(shù):extern _pid_t getsid(_pid_t _pid)
某進(jìn)程的會(huì)話SID是可以修改的狞洋,函數(shù)setsid()用來(lái)創(chuàng)建新的會(huì)話弯淘。
函數(shù):extern _pid_t setsid(void);
如果調(diào)用進(jìn)程已經(jīng)是一個(gè)進(jìn)程組的組長(zhǎng),則此函數(shù)返回錯(cuò)誤吉懊。為了杜絕這種情況的發(fā)生庐橙,通常先調(diào)用fork創(chuàng)建子進(jìn)程假勿,然后使其父進(jìn)程終止,而子進(jìn)程則繼續(xù)态鳖,在子進(jìn)程中調(diào)用此函數(shù)转培。
如果調(diào)用此函數(shù)的進(jìn)程不是一個(gè)進(jìn)程組的組長(zhǎng),則此函數(shù)會(huì)創(chuàng)建一個(gè)新會(huì)話:
- 該進(jìn)程會(huì)變成新會(huì)話首進(jìn)程(seesion leader)浆竭,會(huì)話首進(jìn)程是創(chuàng)建該會(huì)話的進(jìn)程浸须。
- 該進(jìn)程成為一個(gè)新進(jìn)程組的組長(zhǎng)進(jìn)程。新進(jìn)程組PGID 是該調(diào)用進(jìn)程的PID
- 該進(jìn)程沒有控制終端邦泄,如果在調(diào)用setsid之前該進(jìn)程就有一個(gè)控制終端删窒,那么這種聯(lián)系也會(huì)被中斷。
5: 控制終端
會(huì)話和進(jìn)程組有以下一些特點(diǎn):
- 一個(gè)會(huì)話可以有一個(gè)控制終端顺囊,建立與控制終端連接的會(huì)話首進(jìn)程被稱為控制進(jìn)程肌索。
- 一個(gè)會(huì)話中的幾個(gè)進(jìn)程組可被分成一個(gè)前臺(tái)進(jìn)程組合幾個(gè)后臺(tái)進(jìn)程組,如果一個(gè)會(huì)話有一個(gè)控制端包蓝,則它有一個(gè)前臺(tái)進(jìn)程組驶社。
- 無(wú)論何時(shí)鍵入終端的終端鍵(DELETE或Ctrl+C),都會(huì)將中斷信號(hào)發(fā)送給前臺(tái)進(jìn)程組的所有進(jìn)程测萎;無(wú)論何時(shí)鍵入終端的退出鍵(Ctrl+\)亡电,都會(huì)將退出信號(hào)發(fā)送給前臺(tái)進(jìn)程組的所有進(jìn)程。
7.1.4 進(jìn)程用戶屬性
Linux 是權(quán)限有嚴(yán)格控制的操作系統(tǒng)硅瞧,某個(gè)進(jìn)程擁有的真實(shí)用戶號(hào)(RUID)份乒、真實(shí)用戶組號(hào)(RGID)、有效用戶號(hào)(EUID)腕唧、有效用戶組號(hào)(EGID)信息或辖。
在說道進(jìn)程用戶時(shí),需要將文件的擁有者與擁有者組加以區(qū)別枣接。
1:進(jìn)程真實(shí)用戶號(hào)(RUID)
對(duì)于進(jìn)程而言颂暇,創(chuàng)建該進(jìn)程的用戶UID(執(zhí)行此程序的用戶)為此程序真實(shí)用戶號(hào)RUID.可以通過getuid 函數(shù)來(lái)獲取當(dāng)前進(jìn)程的真實(shí)用戶號(hào)(RUID)
函數(shù): extern _uid_t getuid(void)
此函數(shù)無(wú)參數(shù),如果執(zhí)行成功將返回當(dāng)前進(jìn)程的UID但惶;如果執(zhí)行失敗則返回-1耳鸯,錯(cuò)誤原因存儲(chǔ)在errno中。
2:進(jìn)程有效用戶號(hào)(EUID)
EUID 主要用于權(quán)限檢查膀曾。
3:進(jìn)程用戶組號(hào)(GID)
創(chuàng)建進(jìn)程的用戶所在的組號(hào)為該進(jìn)程的進(jìn)程用戶組號(hào)(GID)县爬。可以通過調(diào)用getgid()函數(shù)來(lái)獲得當(dāng)前進(jìn)程的真實(shí)用戶組號(hào)添谊。
函數(shù):extern _uid_t getgid(void)
此函數(shù)無(wú)參數(shù)财喳,如果執(zhí)行成功將返回當(dāng)前進(jìn)程的GID;如果執(zhí)行失敗則返回-1 ,錯(cuò)誤原因存儲(chǔ)在errno中。
4:有效進(jìn)程用戶組號(hào)(EGID)
一般情況下耳高,EGID和GID 相同扎瓶,但是,當(dāng)某可執(zhí)行文件設(shè)置了setgid位泌枪,那么任何用戶(包括root用戶)運(yùn)行此程序時(shí)栗弟,其有效用戶組號(hào)EGID該為文件的擁有者所在組,其原來(lái)EUID類似工闺。
函數(shù):extern _uid_t getegid(void)
此函數(shù)無(wú)參數(shù)乍赫,如果執(zhí)行成功將返回當(dāng)前進(jìn)程的EGID;如果執(zhí)行失敗則返回-1 ,錯(cuò)誤原因存儲(chǔ)在errno中陆蟆。
7.2 進(jìn)程管理及控制
在開發(fā)應(yīng)用程序時(shí)雷厂,程序猿需要有效管理進(jìn)程。常見的進(jìn)程管理方式包括
- 創(chuàng)建進(jìn)程
- 獲取進(jìn)程信息
- 設(shè)置進(jìn)程屬性
- 執(zhí)行進(jìn)程
- 退出進(jìn)程
- 跟蹤進(jìn)程
7.2.1 創(chuàng)建進(jìn)程
1:fork函數(shù)介紹
在Linux環(huán)境下叠殷,創(chuàng)建進(jìn)程的主要方法是調(diào)用fork()函數(shù)改鲫。Linux下所有的進(jìn)程都有init(PID為1)直接或間接創(chuàng)建。
函數(shù):extern _pid_t frok(void);
此函數(shù)沒有參數(shù) 返回值如下:
- 如果執(zhí)行成功林束,在父進(jìn)程中將返回子進(jìn)程(新創(chuàng)建的進(jìn)程)的PID像棘,類型為pid_t,在子進(jìn)程將返回0,以區(qū)別父子進(jìn)程壶冒。
- 如果執(zhí)行失敗缕题,則在父進(jìn)程中返回-1,錯(cuò)誤原因存儲(chǔ)在errno中。
fork函數(shù)調(diào)用成功后胖腾,其子進(jìn)程會(huì)復(fù)制父進(jìn)程的幾乎所有信息(除PID等信息)烟零,主發(fā)復(fù)制父親進(jìn)程的代碼段、數(shù)據(jù)段咸作、BSS锨阿、堆、棧记罚、打開文件描述符墅诡。另外,子進(jìn)程從父進(jìn)程繼承下列屬性:實(shí)際用戶/組號(hào)桐智、有效用戶/組號(hào)末早、環(huán)境變量、堆文件的執(zhí)行時(shí)關(guān)閉標(biāo)志酵使,信號(hào)處理方式設(shè)置荐吉、信號(hào)掩碼焙糟、當(dāng)前工作目錄口渔、根目錄、文件模式創(chuàng)建掩碼穿撮、文件大小設(shè)置等缺脉。
2:創(chuàng)建子進(jìn)程應(yīng)用示例
子進(jìn)程從創(chuàng)建后和父進(jìn)程同時(shí)執(zhí)行痪欲,競(jìng)爭(zhēng)系統(tǒng)資源,子進(jìn)程在執(zhí)行位置為fork返回位置攻礼。
/*
============================================================================
Name : frok_example.c
Author : file_demo
Version :
Copyright : Your copyright notice
Description : Hello World in C, Ansi-style
============================================================================
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
int main(void) {
pid_t pid;
if((pid == fork())==-1){
printf("fork error");
}
printf("bye!\n");
return EXIT_SUCCESS;
}
以上可以看出业踢,fork函數(shù)后代碼在子進(jìn)程中也被執(zhí)行。實(shí)際上礁扮,其他代碼也在子進(jìn)程代碼段中知举,只是子進(jìn)程執(zhí)行位置為fork返回位置,其之前的代碼無(wú)法執(zhí)行罷了太伊。
/*
* fork_example2.c
*
* Created on: Mar 4, 2016
* Author: james
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
int main(){
pid_t pid;
pid = fork();
if(pid == -1){
printf("fork process error\n");
}else if(pid == 0){
printf("fork process ok\n");
}else {
printf("main process\n");
}
return EXIT_SUCCESS;
}
3:對(duì)打開文件的處理
fork函數(shù)創(chuàng)建子進(jìn)程后雇锡,子進(jìn)程將復(fù)制父進(jìn)程的數(shù)據(jù)段、BBS段僚焦、代碼段锰提、堆空間、椃急空間立肘、文件描述符,而對(duì)于文件描述符關(guān)聯(lián)的內(nèi)核文件表項(xiàng)名扛,則采用共享的方式谅年。
案例如下:
/*
* fork_example3.c
*
* Created on: Mar 4, 2016
* Author: james
*/
include stdio.h
include stdlib.h
include unistd.h
include sys/types.h
include string.h
include fcntl.h
include sys/stat.h
int main(){
pid_t pid;
int fd;
int i = 1;
int status ;
char *ch1 = "hello";
char *ch2 = "world";
char *ch3 = "IN";
if((fd = open("text.txt",O_RDWR|O_CREAT,0644)) == -1){
perror("parent open");
exit(EXIT_FAILURE);
}
if(write(fd,ch1,strlen(ch1) == -1)){
perror("parent write");
exit(EXIT_FAILURE);
}
if(( pid = fork() )== -1){
perror("fork");
exit(EXIT_FAILURE);
}else if (pid == 0){
i = 2;
printf("in child\n");
printf("i = %d\n",i);
if(write(fd,ch2,strlen(ch2))== -1)
perror("child write error\n");
return 0;
}else{
sleep(1);
printf("in parent\n");
printf("i = %d",i);
if(write(fd,ch3,strlen(ch3)) == -1)
perror("parent,write");
wait(&status);
return 0;
}
}
案例說明:
在程序?qū)ξ募枋龇僮髦校高M(jìn)程手續(xù)打開創(chuàng)建文件test.txt 文件肮韧,接著向該文件中寫入ch1(hello)內(nèi)容踢故,然后父進(jìn)行等待1秒讓子進(jìn)程完成寫ch2(world),操作惹苗,1秒后殿较,父進(jìn)程再次寫入數(shù)據(jù)ch3(IN)。由文件test.txt內(nèi)容可以看出父子進(jìn)程共同對(duì)一個(gè)文件操作桩蓉,且寫入數(shù)據(jù)不交叉覆蓋淋纲,說明父子進(jìn)程共享文件偏移。
對(duì)變量i院究,在子進(jìn)程中進(jìn)程了第2次賦值(i= 2)洽瞬,其結(jié)果為2,而父進(jìn)程中i的值不變业汰,顯然伙窃,父子進(jìn)程各子擁有這一變量的副本,互相不影響样漆。
4:結(jié)合vfork測(cè)試全局?jǐn)?shù)據(jù)段與BSS段使用策略
vfork()函數(shù)創(chuàng)建新進(jìn)程時(shí)無(wú)需完全復(fù)制父進(jìn)程的地址空間为障,如果派生的進(jìn)程只執(zhí)行exec()函數(shù),則使用fork()從父進(jìn)程復(fù)制到子進(jìn)程的數(shù)據(jù)空間將不被使用。這樣效率非常低鳍怨,從而使得vfork非常有用呻右。根據(jù)父進(jìn)程數(shù)據(jù)空間的大小,vfork比f(wàn)ork可以很大程度上提高性能鞋喇。cfork只在需要的時(shí)候復(fù)制声滥,而一般采用與父進(jìn)程共享所有的資源的方式處理。
其函數(shù):extern _pid_t vfork(void);
vfork()在子進(jìn)程環(huán)境中返回0侦香,在父進(jìn)程環(huán)境中返回子進(jìn)程的進(jìn)程號(hào)落塑。
在執(zhí)行過程中,fork()和vfork()函數(shù)有一定的區(qū)別罐韩,fork函數(shù)是拷貝一個(gè)父進(jìn)程的副本芜赌,從而擁有自己獨(dú)立的代碼段、數(shù)據(jù)段伴逸、以及堆棽颍空間,即成為一個(gè)獨(dú)立的實(shí)體错蝴。而vfork是共享父進(jìn)程的代碼以及數(shù)據(jù)段洲愤。
案例示例:
/*
* fork_vfork_example4.c
*
* Created on: Mar 4, 2016
* Author: james
*/
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
#include <error.h>
int glob = 6;
int main(){
int var ;
pid_t pid;
var = 88;
printf("in beginning:\tglob = %d \tvar = %d\n",glob,var);
if((pid = vfork())<0){
perror("vfork");
exit(EXIT_FAILURE);
}else if(pid == 0 ){
//printf("in child ,modify the var:glob++ var++\n");
glob++;
var++;
printf("in child,printf the var == %d, glob == %d\n",var,glob);
_exit(0);
}else{
printf("in parent,printf the var == %d, glob == %d\n",var,glob);
return 0;
}
}
5:在子函數(shù)調(diào)用vfork創(chuàng)建子進(jìn)程。
案例示例:
/*
* fork_return_example4.c
*
* Created on: Mar 4, 2016
* Author: james
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
void test(){
int i =1;
pid_t pid;
pid = vfork();
if(pid == -1){
perror("vfork error");
exit(EXIT_FAILURE);
}else if (pid == 0){
printf("pid == 0 getpid = %d,getppid = %d\n",getpid(),getppid());
_exit(0);
}else{
printf("pid == other getpid = %d,getppid = %d\n",getpid(),getppid());
}
}
void fun(){
char buf[100];
printf("buf len = %d\n",sizeof(buf));
int i ;
for(i = 0;i<100;i++){
buf[i] = 0;
printf("fun pid getpid = %d,getppid ==%d\n",getpid(),getppid());
}
}
int main(){
pid_t pid;
test();
fun();
return EXIT_SUCCESS;
}
7.2.2 在進(jìn)程中運(yùn)行新代碼
1:函數(shù)功能介紹及應(yīng)用
在fork函數(shù)創(chuàng)建子進(jìn)程后顷锰,如果希望在當(dāng)前子進(jìn)程中運(yùn)行新程序柬赐,則可以調(diào)用exec系列函數(shù)。當(dāng)進(jìn)程調(diào)用exec系列函數(shù)中的任意一個(gè)時(shí)官紫,該進(jìn)程代碼段肛宋,數(shù)據(jù)內(nèi)容完全由新成效替代。因?yàn)檎{(diào)用exec并不是創(chuàng)建新進(jìn)程束世,所以前后的進(jìn)程號(hào)等相關(guān)信息并不發(fā)生變化酝陈。exec,只是用新程序替換了當(dāng)前進(jìn)程的正文毁涉、數(shù)據(jù)沉帮、堆和棧段。
execl 函數(shù):
extern int execl(_const char *_path,_const char *_arg ,...);
execl() 用來(lái)執(zhí)行參數(shù)path字符所代表的文件路徑(絕對(duì)路徑)贫堰,第二個(gè)參數(shù)代表執(zhí)行文件時(shí)傳遞的argv,最后一個(gè)參數(shù)必須是空指針穆壕。
案例示例:
/*
============================================================================
Name : exec_example.c
Author : file_demo
Version :
Copyright : Your copyright notice
Description : Hello World in C, Ansi-style
============================================================================
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
int main(void) {
pid_t pid;
if((pid = fork())<0 ){
perror("fork error\n");
}else if (pid == 0){
//
int exec_resp = execl("/bin/ls","ls","-l","/home",(char)0);
printf("exec_resp = %d\n",exec_resp);
_exit(0);
}else {
//
printf("");
}
return EXIT_SUCCESS;
}
execle函數(shù)聲明:
extern int execle(_const char *_path,_const char *_arg,...)
execle 用來(lái)執(zhí)行參數(shù)path 字符串所代表的文件路徑(絕對(duì)路徑),第二個(gè)參數(shù)代表執(zhí)行文件時(shí)傳遞的是argv其屏,最后一個(gè)參數(shù)必須指向一個(gè)新的環(huán)境變量數(shù)組喇勋,此數(shù)組即成為新執(zhí)行程序的環(huán)境變量。
/*
* execle_example.c
*
* Created on: Mar 4, 2016
* Author: james
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main( int argc, char *argv[],char *env[]){
execle("/bin/ls","ls","-l","/home",(char *)0,env);
return EXIT_SUCCESS;
}
execlp()函數(shù)聲明
extern int execlp(_const char *_file,_const char *_arg,...)
execlp()會(huì)從PATH環(huán)境變量所指的目錄中查找符合參數(shù)file的文件名偎行,找到后執(zhí)行該文件川背,第二個(gè)參數(shù)代表執(zhí)行文件時(shí)傳遞的argv[0],最后一個(gè)參數(shù)必須用空指針NULL贰拿。
案例示例:
/*
* execlp_example.c
*
* Created on: Mar 5, 2016
* Author: james
*/
include <unistd.h>
include <stdio.h>
include <stdlib.h>
int main(int argc,char *argv[]){
execlp("ls","ls","-l","/home",(char)0);
return EXIT_SUCCESS;
}
execvp() 函數(shù)聲明
extern int execvp(_const char *_file ,char *_const _argv[]);
除以上函數(shù)外,system()以新進(jìn)程方式運(yùn)行一個(gè)程序渗常。然后結(jié)束。system()函數(shù)用來(lái)創(chuàng)建新進(jìn)程汗盘,并再次進(jìn)程中運(yùn)行新進(jìn)程皱碘,知道新進(jìn)程結(jié)束后,才繼續(xù)運(yùn)行父進(jìn)程隐孽,子進(jìn)程結(jié)束后癌椿,會(huì)返回退出狀態(tài),(如wait一樣)菱阵。
其函數(shù)定義如下:
extern int system (_const char *_command);
案例示例:
/*
* system_example.c
*
* Created on: Mar 5, 2016
* Author: james
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>
int main(){
int status;
status = system("pwd");
if(!WIFEXITED(status)){
printf("abnormal exit\n");
}else{
printf("the exit status is %d\n",status);
}
return 0;
}
2:執(zhí)行新代碼對(duì)打開文件的處理
案例示例:
include <unistd.h>
include <stdio.h>
include <stdlib.h>
int main(int argc, char *argv[]){
int i;
int fd;
char *ptr = "hello world\n";
fd = atoi (argv[1]);
i = write(fd,ptr,strlen(ptr));
if(i<=0){
perror("write\n");
}
return EXIT_SUCCESS;
}
/*
* fcntl_example.c
*
* Created on: Mar 5, 2016
* Author: james
*/
include <stdio.h>
include <stdlib.h>
include <unistd.h>
include <sys/types.h>
include <fcntl.h>
include <string.h>
int main(){
int fd ,status;
pid_t pid;
fd = open("text.txt",O_RDWR|O_CREAT,0644);
if(fd == -1){
perror("open error");
exit(EXIT_FAILURE);
}
printf("befor child process write\n");
system("cat text.txt");
pid = fork();
if(pid == -1){
perror("fork error");
exit(EXIT_FAILURE);
}else if(pid == 0){
char buf[128];
sprintf(buf,"%d",fd);
execl("./newcode","newcode","buf",(char)0);
}else{
wait(&status);
printf("after child_preccess write\n");
system("cat text.txt");
}
return EXIT_SUCCESS;
}
7.2.3 等待進(jìn)程結(jié)束
進(jìn)程與進(jìn)程間要進(jìn)程信息的狀態(tài)的傳遞與交互踢俄,必須使用進(jìn)程間通信機(jī)制,Linux使用函數(shù)wait()和waitpid()在父子進(jìn)程間提供了簡(jiǎn)單的父子進(jìn)程間同步機(jī)制晴及。
1:wait 等待子進(jìn)程結(jié)束
調(diào)用 wait() 函數(shù)的父進(jìn)程將等待該進(jìn)程的任意一個(gè)子進(jìn)程結(jié)束后才繼續(xù)執(zhí)行都办,如果有多個(gè)子進(jìn)程,只需要等待其中的進(jìn)程虑稼。其函數(shù)定義在/use/include/sys/wait.h文件中琳钉。
函數(shù)聲明為:extern _pid_t wait (_WAIT_STATUS _stat_loc);//wait 函數(shù)
如果等到任意一個(gè)子進(jìn)程結(jié)束,將返回當(dāng)前結(jié)束的子進(jìn)程的PID蛛倦,同時(shí)將子進(jìn)程退出時(shí)的狀態(tài)存儲(chǔ)到“——stat_loc”變量中歌懒。如果執(zhí)行失敗則返回-1,錯(cuò)誤原因存儲(chǔ)在 errno 中溯壶。
案例示例:
/*
* wait_example.c
*
* Created on: Mar 5, 2016
* Author: james
*/
#include <stdio.h>
#include <stdlib.h>
#include <sys/wait.h>
int main(int argc, char *argv[]){
pid_t pid_one ,pid_wait;
int status;
if((pid_one = fork()) == -1){
perror("fork error");
}else if(pid_one == 0){
printf("my pid is %d\n",getpid());
sleep(1);
exit(EXIT_SUCCESS);
}else{
pid_wait = wait(&status);
if(WIFEXITED(status)){
printf("wait on pid:%d return value is;%4x\n",pid_wait,WEXITSTATUS(status));
}else if(WIFSIGNALED(status)){
//printf("wait on pid :%d return value is:%4x\n",pid_wait,WIFSIGNALED(status));
}
}
return EXIT_SUCCESS;
}
2:waitpid() 等待子進(jìn)程結(jié)束
用戶可以使用waitpid()函數(shù)來(lái)等待指定子進(jìn)程(指定PID的子進(jìn)程)結(jié)束及皂。
其函數(shù)定義/usr/include/sys/Wait.h
extern _pid_t waitpid (_pid_t _pid,int *_stat_loc,int _options);
其中,
第一個(gè)參數(shù)為進(jìn)程PID值且改,該值的設(shè)置范圍如下:
- PID > 0 ,表示等待進(jìn)程PID 為該P(yáng)ID 值的進(jìn)程結(jié)束验烧。
- PID =-1 ,表示等待任意進(jìn)程結(jié)束
- PID = 0 ,表示等待與當(dāng)前進(jìn)程的進(jìn)程組PGID一致的進(jìn)程結(jié)束
- PID <-1 ,表示等待進(jìn)程組PGID是此值的絕對(duì)值的進(jìn)程結(jié)束
第二個(gè)參數(shù)為調(diào)用它的函數(shù)中某個(gè)變量地址,如果執(zhí)行成功又跛,則用來(lái)存儲(chǔ)結(jié)束進(jìn)程的結(jié)束狀態(tài)噪窘。
第三個(gè)參數(shù)為等待選項(xiàng),可以設(shè)置為0效扫,亦可為WNOHANG和WUNTRACED 定義如下:
define WNOHANG 1
define WUNTRACED 2
如果OPTIONS 設(shè)置為WNOHANG倔监,而此時(shí)沒有子進(jìn)程退出,將返回0菌仁,而返回子進(jìn)程的PID浩习,并獲取子進(jìn)程的狀態(tài)于參數(shù)STAT_LOC中。
waitpid 與 wait 的使用方法類似济丘,因此可以將wait示例中的wait語(yǔ)句換成以下代碼:
pid_wait= waitpid(pid_one,&status,0);
7.2.4 退出進(jìn)程
在LINUX 下谱秽,可以通過以下方式結(jié)束進(jìn)程
- 向exit或_exit發(fā)布一個(gè)調(diào)用
- 在main函數(shù)中執(zhí)行return
- 隱含的離開main函數(shù)
LINUX常用的退出進(jìn)程函數(shù)有exit()洽蛀、_exit()和on_exit()
1:abort 中止進(jìn)程
abort 函數(shù)用來(lái)中止進(jìn)程。函數(shù)聲明
extern void abort (void)
2:atexit 退出進(jìn)程
注冊(cè)一個(gè)函數(shù)在exit退出時(shí)調(diào)用疟赊。函數(shù)聲明
extern int atexit(void(* _func)(void))
3:on_exit正常退出進(jìn)程
正常結(jié)束當(dāng)前調(diào)用函數(shù)郊供。其函數(shù)聲明
extern int on_exit(void(*_func)(int status,void *arg)void *arg);
on_exit()用來(lái)設(shè)置一個(gè)程序正常結(jié)束前調(diào)用的函數(shù),當(dāng)程序通過調(diào)用exit()或者從main中返回時(shí)近哟,參數(shù)func所指定的函數(shù)先被調(diào)用驮审,然后才真正由exit()結(jié)束程序,參數(shù)arg指針會(huì)傳給func函數(shù)疯淫。
如果執(zhí)行成功則返回0,否則返回-1,錯(cuò)誤原因存儲(chǔ)在errno中肌似。
案例示例:
/*
============================================================================
Name : exit_example.c
Author : file_demo
Version :
Copyright : Your copyright notice
Description : Hello World in C, Ansi-style
============================================================================
*/
#include <stdio.h>
#include <stdlib.h>
void test_exit(int status ,void *arg){
printf("before exit()\n");
printf("exit %d\n",status);
printf("arg = %s\n",(char *)arg);
}
int main(void) {
char *str = "test";
on_exit(test_exit,(void *)str);
exit(4321);
return EXIT_SUCCESS;
}
4:exit退出進(jìn)程
函數(shù)exit用于退出進(jìn)程。其函數(shù)聲明如下:
extern void exit(int _status);
exit()用來(lái)正常結(jié)束當(dāng)前進(jìn)程的執(zhí)行笛臣,并把參數(shù)status返回給父進(jìn)程鲸拥,而進(jìn)程所有的緩沖區(qū)數(shù)數(shù)據(jù)會(huì)自動(dòng)寫回并關(guān)閉文件。
如果執(zhí)行成功沒有返回值沛申,否則返回-1 ,失敗原因存儲(chǔ)在errno中惊暴。
5:exit 與 return 區(qū)別
C 語(yǔ)言關(guān)鍵字與函數(shù)益咬,exit()在main函數(shù)退出時(shí)有相似之處,但兩者本質(zhì)的區(qū)別:
- return退出當(dāng)前函數(shù)主體,exit()函數(shù)退出當(dāng)前進(jìn)程全封,因此桃犬,在main函數(shù)里面return(0)和exit(0)完成一樣的功能攒暇。
- return僅僅從子函數(shù)中返回乎莉,而子進(jìn)程用exit()退出肥橙,調(diào)用exit()時(shí)要調(diào)用一段終止處理程序,然后關(guān)閉所有的IO流
6:_exit 不調(diào)用任何注冊(cè)函數(shù)退出進(jìn)程味榛,其函數(shù)聲明如下:
extern void _exit(int _status)
_exit()用來(lái)正常結(jié)束當(dāng)前進(jìn)程的執(zhí)行椭坚,把參數(shù)status返回給父進(jìn)程,并關(guān)閉文件搏色。此函數(shù)調(diào)用后不會(huì)返回善茎,而傳遞SIGHLD信號(hào)給父進(jìn)程,父進(jìn)程可以通過wait函數(shù)取得獲得子進(jìn)程的結(jié)束狀態(tài)频轿,exit()不會(huì)處理標(biāo)準(zhǔn)的IO緩沖區(qū)垂涯,如果要更新需要調(diào)用exit().
7.2.5 修改進(jìn)程用戶相關(guān)信息
1:access 核實(shí)用戶權(quán)限
此函數(shù)用來(lái)檢查真實(shí)用戶號(hào)(UID)或真實(shí)用戶組號(hào)(GID)是否擁有對(duì)文件的相應(yīng)訪問權(quán)限烁焙。
函數(shù)定義:extern int access (_const char *_name ,int type);
此函數(shù)的第一餓參數(shù)為欲訪問的文件(需要包含路徑),第二參數(shù)為相應(yīng)的訪問權(quán)限耕赘,文件權(quán)限定義如下:
- define R_OK 4
- define W_OK 2
- define X_OK 1
- define F_OK 0
如果文件具有測(cè)試權(quán)限骄蝇,此函數(shù)將返回0,否則返回-1,其錯(cuò)誤狀態(tài)主要有以下幾個(gè):
- EACCESS :不具有指定的訪問權(quán)限
- ENOENT :文件不存在
- EROFS :在只讀文件系統(tǒng)要求寫權(quán)限操骡。
案例示例:
/*
* access_example.c
*
* Created on: Mar 5, 2016
* Author: james
*/
include < stdio.h >
include < stdlib.h >
include < unistd.h >
int main(){
int i ;
if(i == access("/etc/exports",X_OK) == -1){// 檢查是否有執(zhí)行的檢驗(yàn)
perror("access");
exit(EXIT_FAILURE);
}else{
printf("the user have write permission\n");
}
return 0;
}
2:設(shè)置進(jìn)程真實(shí)用戶RUID
如果要顯示修改此值九火,可以調(diào)用setuid函數(shù)。
函數(shù)聲明
extern int setuid(_uid_t _uid);
此函數(shù)有一個(gè)參數(shù)册招,即設(shè)置進(jìn)程真實(shí)用戶號(hào)(RUID):
- 如果當(dāng)前用戶是超級(jí)用戶岔激,則將設(shè)置真實(shí)用戶號(hào)(UID)、有效用戶號(hào)EUID為指定ID是掰,并返回0 以標(biāo)識(shí)成功虑鼎。
- 如果當(dāng)前用戶是普通用戶,且設(shè)置UID值為自己的UID冀惭,則可以修改成功震叙,否則無(wú)權(quán)修改掀鹅,函數(shù)江返回-1
案例示例:
/*
* setuid_exp.c
*
* Created on: Mar 5, 2016
* Author: james
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main(){
int uid,euid,suid;
getresuid(&uid,&euid,&suid);
printf("uid = %d,euid = %d,suid = %d\n",uid,euid,suid);
printf("after setuid(501)\n");
setuid(501); //執(zhí)行此程序 普通用戶無(wú)法修改
uid = 1; euid = -1; suid = -1;
getresuid(&uid,&euid,&suid);
printf("uid == %d,euid = %d,suid = %d\n",uid,euid,suid);
return 0;
}
3 設(shè)置進(jìn)程有效用戶EUID
由前面可知散休,某個(gè)進(jìn)程的EUID首先由該可執(zhí)行文件的權(quán)限決定,如果該可執(zhí)行文件設(shè)置setuid位乐尊,則EUID為執(zhí)行此進(jìn)程的用戶戚丸;如果該可執(zhí)行文件設(shè)置setuid位,則EUID為該可執(zhí)行文件的擁有者扔嵌。
函數(shù)setuid 用來(lái)設(shè)置有效用戶號(hào)(EUID)
函數(shù)聲明
extern int seteuid(_uid_t uid);
- 如果是超級(jí)用戶限府,將設(shè)置有效用戶號(hào)(EUID)為指定ID。如果調(diào)用成功痢缎,該函數(shù)將返回0胁勺;如果調(diào)用失敗,將返回-1 并有以下錯(cuò)誤代碼設(shè)置独旷。
- 如果是普通用戶署穗,可以設(shè)置EUID為自己的ID,如果想設(shè)置為其他用戶則不予更改嵌洼,將返回失敗案疲,此外,還可以調(diào)用setegid來(lái)設(shè)置音效egid
setegid 函數(shù)可以用來(lái)設(shè)置有效組ID麻养,原理與seteuid類型褐啡,函數(shù)聲明如下:
extern int setegid(__gid_t ,_gid)
案例示例:
/*
* setuid_exp.c
*
* Created on: Mar 5, 2016
* Author: james
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main(){
int uid,euid,suid;
getresuid(&uid,&euid,&suid);
printf("uid = %d,euid = %d,suid = %d\n",uid,euid,suid);
printf("after setuid(501)\n");
setuid(501);
setreuid(0,500);
uid = 1; euid = -1; suid = -1;
getresuid(&uid,&euid,&suid);
printf("uid == %d,euid = %d,suid = %d\n",uid,euid,suid);
return 0;
}
7.2.6 進(jìn)程調(diào)度管理函數(shù)
每個(gè)進(jìn)程都受調(diào)度策略和優(yōu)先級(jí)的控制。這些參數(shù)可以通過應(yīng)用程序顯式執(zhí)行 sched_setscheduler()或通過sched_setparam()函數(shù)來(lái)指定(這些函數(shù)可以按進(jìn)程級(jí)別為多進(jìn)程更改調(diào)度參數(shù))鳖昌。
優(yōu)先級(jí)范圍與每種策略相關(guān)备畦。每種策略的優(yōu)先級(jí)范圍可以(但不需要)與其他策略的優(yōu)先級(jí)范圍重疊低飒。選擇要運(yùn)行的進(jìn)程時(shí),將選擇位于最高優(yōu)先級(jí)非空進(jìn)程列表首位的進(jìn)程懂盐,然后逸嘀,該進(jìn)程會(huì)從其他進(jìn)程列表中刪除。
在常見操作系統(tǒng)中允粤,調(diào)度算法主要有以下原則:
- FIFO 先入先出原則:首先請(qǐng)求服務(wù)的對(duì)象首先得到CPU的處理崭倘;
- 最短作業(yè)優(yōu)先原則:需要最小系統(tǒng)時(shí)間的服務(wù)首先得到處理;
- 最高優(yōu)先級(jí)優(yōu)先原則:優(yōu)先級(jí)最高的服務(wù)首先得到處理类垫;
- 時(shí)間輪片原則司光;每個(gè)任務(wù)分配一個(gè)系統(tǒng)時(shí)間片,輪流執(zhí)行悉患;
1: 設(shè)置調(diào)度策略
設(shè)置調(diào)度策略sched_setscheduler 函數(shù)聲明:
extern int sched_setscheduler (_pid_t __Pid,int policy,_const struct sched_param *__param);
sched_setscheduler()函數(shù)將pid所指定進(jìn)程的調(diào)度策略和調(diào)度參數(shù)分別設(shè)置為param指向的sched_param結(jié)構(gòu)中的policy及其參數(shù)残家。param結(jié)構(gòu)中的sched_priority成員的值可以為任何整數(shù),該整數(shù)位于policy所指定調(diào)度策略的優(yōu)先級(jí)范圍內(nèi)(含邊界值)售躁。policy參數(shù)的可能值在頭文件<sched.h>中定義坞淮。
- 如果存在pid所描述的進(jìn)程,將會(huì)為進(jìn)程ID等pid的進(jìn)程設(shè)置調(diào)度策略和調(diào)度參數(shù)陪捷。
- 如果pid為零回窘,將會(huì)為調(diào)用進(jìn)程設(shè)置調(diào)用策略和調(diào)度參數(shù)。
- 如果進(jìn)程pid含多個(gè)進(jìn)程或輕量進(jìn)程(即該進(jìn)程時(shí)多進(jìn)程的)市袖,此函數(shù)將會(huì)影響進(jìn)程中各個(gè)內(nèi)核可調(diào)用實(shí)體的策略和優(yōu)先級(jí)啡直,具體取決于調(diào)度爭(zhēng)用范圍。
對(duì)于使用系統(tǒng)爭(zhēng)用范圍創(chuàng)建的進(jìn)程苍碟,此系統(tǒng)調(diào)用將不會(huì)對(duì)進(jìn)程或基礎(chǔ)內(nèi)核調(diào)度實(shí)體的調(diào)度產(chǎn)生影響酒觅。
對(duì)于使用進(jìn)程爭(zhēng)用范圍的進(jìn)程,進(jìn)程的調(diào)度參數(shù)不會(huì)受到影響微峰。但是進(jìn)程爭(zhēng)用范圍進(jìn)程的基礎(chǔ)內(nèi)核調(diào)度實(shí)體會(huì)將其調(diào)度參數(shù)改為參數(shù)中指定的值舷丹,進(jìn)程爭(zhēng)用范圍進(jìn)程使用的內(nèi)核調(diào)度實(shí)體(在此調(diào)用完成之后創(chuàng)建)從進(jìn)程繼承調(diào)度策略和相關(guān)調(diào)度參數(shù)。
返回的優(yōu)先級(jí)為目標(biāo)進(jìn)程的舊優(yōu)先級(jí)蜓肆,但是如果使用某些其他接口更改各個(gè)進(jìn)程或輕量進(jìn)程的優(yōu)先級(jí)颜凯,各個(gè)進(jìn)程或輕量進(jìn)程可能具有其他值。
更改其他進(jìn)程的調(diào)度參數(shù)需要有相應(yīng)的特權(quán)症杏。調(diào)用進(jìn)程必須具有相應(yīng)的特權(quán)装获,或者是具有PRIV_STSCHED權(quán)限的組的成員,才能夠調(diào)用sched_setscheduler();
/*
============================================================================
Name : sched_setscheduler.c
Author : sched_setscheduler
Version :
Copyright : Your copyright notice
Description : Hello World in C, Ansi-style
============================================================================
*/
#include <stdio.h>
#include <stdlib.h>
#include <sched.h>
int main(void) {
struct sched_param param;
int maxpri;
maxpri = sched_get_priority_max(SCHED_FIFO);// 獲取最大值
if(maxpri == -1){
perror("sched_get_priority_max() failed");
exit(1);
}
param.sched_priority = maxpri;
if(sched_setscheduler(getpid(),SCHED_FIFO,¶m) == -1){//設(shè)置優(yōu)先級(jí)
perror("sched_setscheduler() failed");
exit(1);
}
return EXIT_SUCCESS;
}
2:獲取調(diào)度策略
獲取調(diào)度策略sched_getscheduler ()函數(shù)聲明如下:
extern int sched_getscheduler(_pid_t __pid)
sched_getscheduler()函數(shù)返回pid所指定進(jìn)程的調(diào)度策略厉颤。
- 如果存在pid所描述的進(jìn)程穴豫,將返回進(jìn)程ID 等pid的進(jìn)程的調(diào)度策略。
- 如果pid為零,將返回調(diào)用進(jìn)程的調(diào)度策略
- 如果pid包含多個(gè)進(jìn)程或輕量進(jìn)程精肃,此函數(shù)只返回進(jìn)程調(diào)度策略和優(yōu)先級(jí)秤涩。目標(biāo)進(jìn)程中的各個(gè)進(jìn)程或輕量進(jìn)程具有其自身的調(diào)度策略和優(yōu)先級(jí),它們可能與進(jìn)度的調(diào)度策略和優(yōu)先級(jí)不同司抱。
3:設(shè)置調(diào)度參數(shù)
可以使用sched_getparam 設(shè)置調(diào)度參數(shù)筐眷,其函數(shù)聲明如下:
extern int sched_getparam(__pid_t __pid,struct sched_param *_param);
sched_setparam函數(shù)將pid指定進(jìn)程的調(diào)度參數(shù)設(shè)置為param指向的sched_param結(jié)構(gòu)指定的值。
sched_param結(jié)構(gòu)定義如下:
struct sched_param{
int sched_priority;
}
param 結(jié)構(gòu)中的sched_priority成員的值可以為任何整數(shù)习柠,該整數(shù)位于pid指定進(jìn)程當(dāng)前調(diào)度策略的優(yōu)先級(jí)范圍內(nèi)(含邊界值)匀谣,該優(yōu)先級(jí)的數(shù)值越大表示優(yōu)先級(jí)越高(越強(qiáng))。
如果存在pid所描述的進(jìn)程资溃,并且調(diào)用進(jìn)程具有權(quán)限武翎,將為進(jìn)程ID等于pid 的進(jìn)程設(shè)置調(diào)度參數(shù)。如果pid為零溶锭,將為調(diào)用進(jìn)程設(shè)置調(diào)度參數(shù)宝恶。
如果進(jìn)程的pid包含多個(gè)進(jìn)程或輕量進(jìn)程鞠评,此函數(shù)將會(huì)影響進(jìn)程中各個(gè)內(nèi)核可調(diào)度實(shí)體的策略和優(yōu)先級(jí)惰爬,具體取決于調(diào)度爭(zhēng)用范圍郭厌。
- 對(duì)使用系統(tǒng)爭(zhēng)用范圍創(chuàng)建的進(jìn)程哨免,此系統(tǒng)調(diào)用不會(huì)對(duì)進(jìn)程或基礎(chǔ)內(nèi)核調(diào)度實(shí)體的調(diào)度產(chǎn)生影響。
- 對(duì)于使用進(jìn)程爭(zhēng)用范圍的進(jìn)程耀石,進(jìn)程的調(diào)度參數(shù)不會(huì)受到影響忘蟹。但是進(jìn)程爭(zhēng)用范圍進(jìn)程的基礎(chǔ)內(nèi)核調(diào)度實(shí)體會(huì)將其調(diào)度參數(shù)改為參數(shù)中指定的值朴恳。進(jìn)程爭(zhēng)用范圍進(jìn)程使用的內(nèi)核調(diào)度實(shí)體(在此調(diào)用完成之后創(chuàng)建)從進(jìn)程繼承調(diào)度策略和相關(guān)調(diào)度參數(shù)欺栗。
- 對(duì)于單進(jìn)程的進(jìn)程毫痕,其調(diào)度參數(shù)也將會(huì)更改征峦。
只有超級(jí)用戶才能更改其他進(jìn)程的調(diào)度參數(shù)迟几。調(diào)用進(jìn)程必須具有相應(yīng)的特權(quán)。
4:讀取調(diào)度參數(shù)
可以使用sched_getparam 設(shè)置調(diào)度參數(shù):
extern int sched_setparam(_pid_tm_const struct sched_param *_param);
sched_getparam()函數(shù)返回進(jìn)程的調(diào)度參數(shù)(該進(jìn)程由param指向sched_param 結(jié)構(gòu)中pid指定)栏笆。
5:放棄進(jìn)程
extern int sched_yield(void)
sched_yield()函數(shù)強(qiáng)制調(diào)用進(jìn)程放棄處理器类腮,直到調(diào)用進(jìn)程再次位于進(jìn)程列表的首位。該函數(shù)不帶參數(shù)蛉加。
6:獲取優(yōu)先級(jí)值的范圍
sched_get_priority_max()和 sched_get_priority_main()函數(shù)分別為algorithm指定的調(diào)度策略返回的相應(yīng)最大值和最小值蚜枢。
7:獲取進(jìn)程的執(zhí)行時(shí)間限制
8:獲取進(jìn)程優(yōu)先級(jí)
9:設(shè)置進(jìn)程優(yōu)先級(jí)
7.3Linux特殊進(jìn)程
7.3.1守候進(jìn)程及其創(chuàng)建過進(jìn)程
1:守候進(jìn)程的特點(diǎn)
守護(hù)進(jìn)程(Daemon)是在后臺(tái)進(jìn)程的一種特殊進(jìn)程,脫離于終端针饥。
一般情況下,守護(hù)進(jìn)程可以通過以下方式啟動(dòng):
- 在系統(tǒng)啟動(dòng)時(shí)由腳本啟動(dòng)厂抽,這些啟動(dòng)腳本通常放在/etc/rc.d目錄下
- 利用inetd超級(jí)服務(wù)器啟動(dòng),如telnet等丁眼。
- 由cron定時(shí)啟動(dòng)以及在終端nohup啟動(dòng)進(jìn)程也是守護(hù)進(jìn)程筷凤。
2:守護(hù)進(jìn)程編程要點(diǎn)
下面是編寫守護(hù)進(jìn)程的基本過程:
- (1)屏蔽一些有關(guān)控制端的信號(hào),這是為了防止在守護(hù)進(jìn)程沒有正常運(yùn)行起來(lái)前,控制終端受到干擾退出或掛起:
- (2)在后臺(tái)運(yùn)行藐守。 這是為了避免掛起控制終端將其放入后臺(tái)執(zhí)行挪丢。方法是在進(jìn)程中調(diào)用fork使其fork使父進(jìn)程終止,讓其子進(jìn)程中后臺(tái)執(zhí)行卢厂。
- (3)脫離控制終端和進(jìn)程組乾蓬,應(yīng)為進(jìn)程屬于一個(gè)進(jìn)程組,進(jìn)程組號(hào)(PGID)就是進(jìn)程組長(zhǎng)的進(jìn)程號(hào)(PID)慎恒。同進(jìn)程組中進(jìn)程共享一個(gè)控制終端任内,這個(gè)控制終端通常是創(chuàng)建進(jìn)程的shell登陸終端。而控制終端和進(jìn)程組通常是父進(jìn)程繼承下來(lái)的融柬,需要擺脫它們族奢,使之不受它們的影響。因此需要調(diào)用setsid使子進(jìn)程成為新的會(huì)話組長(zhǎng)
案例示例:
setsid調(diào)用成功后丹鸿,調(diào)用進(jìn)程會(huì)成為新的會(huì)話組長(zhǎng)和新的進(jìn)程組長(zhǎng)越走,并于原來(lái)的登錄會(huì)話和進(jìn)程組脫離,由于會(huì)話過程對(duì)控制終端的獨(dú)占性靠欢,進(jìn)程同時(shí)與控制終端脫離廊敌。 - (4)禁止進(jìn)程重新打開控制終端。
- (5)關(guān)閉打開的文件描述符门怪。
- (6)改變當(dāng)前工作目錄骡澈。
- (7)重設(shè)文件創(chuàng)建淹模。
- (8)處理SIGGHLD信號(hào)(子進(jìn)程退出信號(hào))掷空。但對(duì)于某些進(jìn)程肋殴,特別是服務(wù)器進(jìn)程往往在請(qǐng)求到來(lái)時(shí)生成子進(jìn)程處理請(qǐng)求。如果父進(jìn)程不等待子進(jìn)程結(jié)束坦弟,子進(jìn)程將成為僵尸進(jìn)程(zombie)從而占用系統(tǒng)資源护锤。
如果父進(jìn)程等待子進(jìn)程結(jié)束,將增加父進(jìn)程的負(fù)擔(dān)酿傍,影響服務(wù)器進(jìn)程的并發(fā)性能烙懦。在Linux下可以簡(jiǎn)單地將SIGCHLD信號(hào)的操作設(shè)為SIG_IGN來(lái)解決這一問題。
示例: signal(SIGCHLD,SIG_IGN)// signal函數(shù)的使用
這樣赤炒,內(nèi)核在子進(jìn)程結(jié)束時(shí)不會(huì)產(chǎn)生僵死進(jìn)程氯析。
7.3.2日志信息及其管理
1:日志信息基本概念
為了告訴系統(tǒng)管理員守護(hù)進(jìn)程的運(yùn)行情況,守護(hù)進(jìn)程需要輸出特定信息莺褒,而守護(hù)進(jìn)程又不能把信息輸出到某個(gè)終端掩缓,因此,守護(hù)一般采用日志信息的方式輸出遵岩。
2:建立與日志守候進(jìn)程聯(lián)系
在進(jìn)程中你辣,調(diào)用函數(shù)openlog()將與日志守護(hù)進(jìn)程建立聯(lián)系,也就是說,如果需要寫日志信息,一般情況下绢记,都需要顯示調(diào)用此函數(shù)扁达,告訴日志守護(hù)進(jìn)程當(dāng)前進(jìn)程將寫日志;
extern void openlog(__const char *_ident,int_option,int _facility)
第一個(gè)參數(shù):要向每個(gè)消息加入字符串蠢熄,一般可設(shè)置為當(dāng)前進(jìn)程名
第二個(gè)參數(shù):用來(lái)描述一打開選項(xiàng)
第三個(gè)參數(shù):消息的類型跪解,決定將消息寫入到那個(gè)日志文件中。
如果在關(guān)閉與日志守護(hù)進(jìn)程的聯(lián)系签孔,可以調(diào)用colselog函數(shù)
extern void closelog(void)
3: 寫日志信息
syslog()將產(chǎn)生一條日志信息叉讥,然后由日志守護(hù)進(jìn)程將其發(fā)布到個(gè)日志文件中,該函數(shù)聲明:
extern void syslog(int _pri ,__const char *_fmt,...);
第一個(gè)參數(shù)決定日志類別.
第二個(gè)參數(shù)為日志輸出格式
7.3.3守候進(jìn)程應(yīng)用示例
/*
============================================================================
Name : Daemon_exp.c
Author : james
Version :
Copyright : Your copyright notice
Description : Hello World in C, Ansi-style
============================================================================
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#include <fcntl.h>
#include <sys/syslog.h>
#include <sys/param.h>
#include <sys/types.h>
#include <sys/stat.h>
int init_daemon(const char *pname,int facility){
int pid;
int i;
signal(SIGTTOU,SIG_IGN);//處理可能的終端信號(hào)
signal(SIGTTIN,SIG_IGN);
signal(SIGTSTP,SIG_IGN);
signal(SIGHUP,SIG_IGN);
if(pid == fork()){// 創(chuàng)建子進(jìn)程饥追,父進(jìn)程退出
exit(EXIT_SUCCESS);
}else if(pid < 0){
perror("fork");
exit(EXIT_FAILURE);
}
setsid();// 設(shè)置新會(huì)話組長(zhǎng)图仓,新進(jìn)程組長(zhǎng),脫離終端
if(pid == fork()){// 創(chuàng)建新進(jìn)程但绕,子進(jìn)程不能再申請(qǐng)終端
exit(EXIT_SUCCESS);
}else if(pid < 0){
perror("fork");
exit(EXIT_FAILURE);
}
for(i = 0;i < NOFILE;++i)// 關(guān)閉父進(jìn)程的文件描述符
close(i);
open("/dev/null",O_RDONLY);// 對(duì)標(biāo)準(zhǔn)輸入輸入全部重定向到/dev/null
open("/dev/null",O_RDWR);// 因?yàn)橄惹瓣P(guān)閉了所有的文件描述符救崔,新開的值為0,1捏顺,2
open("/dev/null",O_RDWR);
chdir("/tmp");// 修改主目錄
umask(0);//重新設(shè)置文件掩碼
signal(SIGCHLD,SIG_IGN);// 處理子進(jìn)程退出
openlog(pname,LOG_PID,facility);//與守候進(jìn)程簡(jiǎn)歷聯(lián)系六孵,加上進(jìn)程號(hào),文件名
return 1;
}
int main(int argc,char *argv[]) {
FILE *fd;
time_t ticks;
init_daemon(argv[0],LOG_KERN);
while(1){
sleep(1);
ticks = time(NULL);
syslog(LOG_INFO,"%s",asctime(localtime(&ticks)));
}
return EXIT_SUCCESS;
}
7.3.4:孤兒進(jìn)程與僵死進(jìn)程
孤兒進(jìn)程:因父進(jìn)程先退出而導(dǎo)致的一個(gè)子進(jìn)程被init進(jìn)程收養(yǎng)的為孤兒進(jìn)程幅骄。
/*
============================================================================
Name : orphan_p.c
Author : james
Version :
Copyright : Your copyright notice
Description : Hello World in C, Ansi-style
============================================================================
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main() {
pid_t pid;
if((pid = fork()) == -1)
perror("fork");
else if(pid == 0){
while(1){
printf("pid = %d,ppid = %d\n",getpid(),getppid());
sleep(2);
printf("pid = %d,ppid = %d\n",getpid(),getppid());
}
}
else
exit(0);
}
僵死進(jìn)程:而已經(jīng)退出但還沒有回收資源的進(jìn)程為僵死進(jìn)程劫窒。
案例示例:
/*
* dead_p.c
*
* Created on: Mar 7, 2016
* Author: james
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main(){
pid_t pid ;
if((pid = fork()) == -1){
perror("fork");
exit(EXIT_FAILURE);
}else if(pid == 0){
printf("child_pid pid = %d\n",getpid());
exit(0);
}
sleep(3);
system("ps");
exit(0);
}