linux C总结篇(进程)
說(shuō)些廢話:話說(shuō)從留校以來(lái),linux C已經(jīng)看了大半,然而自己還沒有系統(tǒng)的總結(jié)許多的知識(shí)點(diǎn)。今天想來(lái)真乃是一大“罪狀”啊!!!哈哈哈~~于是乎決定,利用這幾天的時(shí)間對(duì)學(xué)過(guò)的小知識(shí)點(diǎn)系統(tǒng)性的總結(jié)一下。PS:僅作為個(gè)人的參考資料吧!如果對(duì)你有幫助,那真的是瞎貓碰上死耗子了^-_-^
進(jìn)程:一個(gè)程序被加載到內(nèi)存當(dāng)中運(yùn)行,那么在內(nèi)存當(dāng)中的那個(gè)數(shù)據(jù)就是進(jìn)程。
進(jìn)程與程序的區(qū)別:進(jìn)程是運(yùn)行的程序,程序只是存放在硬盤上的可執(zhí)行代碼。
1.獲取進(jìn)程的各種ID (非負(fù)數(shù))
書上說(shuō)是函數(shù)聲明在unistd.h 頭文件中,但我man 之后發(fā)現(xiàn)是在sys/types.h 和 unistd.h之中,說(shuō)明盡信書不如無(wú)書啊!!!!
#include <sys/types.h>#include <unistd.h>pid_t getpid(void); pid_t getppid(void);uid_t getuid(void); //獲得進(jìn)程的實(shí)際用戶IDuid_t geteuid(void); /獲得進(jìn)程的有效用戶IDgid_t getgid(void); //獲得進(jìn)程的實(shí)際組IDgid_t getegid(void); //獲得進(jìn)程的有效組ID實(shí)際用戶:運(yùn)行該程序的用戶
有效用戶:程序以什么用戶身份來(lái)執(zhí)行
小例子:我sudo 執(zhí)行了一個(gè)程序,該進(jìn)程的實(shí)際用戶是我,而有效用戶是root
2.創(chuàng)建進(jìn)程 (fork)
#include <unistd.h>pid_t fork(void);說(shuō)明: 1.調(diào)用成功時(shí)兩個(gè)返回值,父進(jìn)程返回子進(jìn)程ID ,子進(jìn)程返回 0 。失敗返回 -1。這樣就可以來(lái)區(qū)別父子進(jìn)程!
#include<stdio.h> #include<unistd.h> #include<sys/types.h> int main(void) {pid_t pid;int i;pid=fork();switch(pid){case 0:for(i= 0 ;i< 50 ;i++) printf("*****************************子進(jìn)程\n");break;case -1:printf("creat P id failed \n");break;default:for(i= 0 ;i< 50 ;i++) printf("+++++++++++++++++++++++++++++++++++++++父進(jìn)程哦!!\n");break ;}return 0; }執(zhí)行結(jié)果:
說(shuō)明:1.創(chuàng)建子進(jìn)程后,父子進(jìn)程爭(zhēng)奪CPU ,先搶到者先執(zhí)行,另一個(gè)掛起等待 ,所以父子進(jìn)程的執(zhí)行取決于你的操作系統(tǒng)。可能你的執(zhí)行結(jié)果就與我的不一樣哦!!
2.fork 就相當(dāng)于兩條支流,一條流向0 (子進(jìn)程),一條流向大整數(shù)(父進(jìn)程)。
3. 子進(jìn)程的繼承性是“拷貝” ,不是拿來(lái)就用。
3.孤兒進(jìn)程的創(chuàng)建
孤兒進(jìn)程:在其父進(jìn)程執(zhí)行完成或被終止后仍繼續(xù)運(yùn)行的一類進(jìn)程。(會(huì)被init 或者是systemd進(jìn)程收養(yǎng))
4. 守護(hù)進(jìn)程的創(chuàng)建
守護(hù)進(jìn)程(daemon):是一種在后臺(tái)執(zhí)行的電腦程序。此類程序會(huì)以進(jìn)程的形式初始化。守護(hù)進(jìn)程的名稱通常以字母“d”結(jié)尾:例如,syslogd就是指管理系統(tǒng)日志的守護(hù)進(jìn)程。
創(chuàng)建方法:
(1)創(chuàng)建子進(jìn)程,終止父進(jìn)程
由于守護(hù)進(jìn)程是脫離控制終端的,因此首先創(chuàng)建子進(jìn)程,終止父進(jìn)程,使得程序在shell終端里造成一個(gè)已經(jīng)運(yùn)行完畢的假象。之后所有的工作都在子進(jìn)程中完成,而用戶在shell終端里則可以執(zhí)行其他的命令,從而使得程序以僵尸進(jìn)程形式運(yùn)行,在形式上做到了與控制終端的脫離。
(2)在子進(jìn)程中創(chuàng)建新會(huì)話
這個(gè)步驟是創(chuàng)建守護(hù)進(jìn)程中最重要的一步,在這里使用的是系統(tǒng)函數(shù)setsid
setsid函數(shù)用于創(chuàng)建一個(gè)新的會(huì)話,并擔(dān)任該會(huì)話組的組長(zhǎng)。調(diào)用setsid有三個(gè)作用:讓進(jìn)程擺脫原會(huì)話的控制、讓進(jìn)程擺脫原進(jìn)程組的控制和讓進(jìn)程擺脫原控制終端的控制。
在調(diào)用fork函數(shù)時(shí),子進(jìn)程全盤拷貝父進(jìn)程的會(huì)話期(session,是一個(gè)或多個(gè)進(jìn)程組的集合)、進(jìn)程組、控制終端等,雖然父進(jìn)程退出了,但原先的會(huì)話期、進(jìn)程組、控制終端等并沒有改變,因此,那還不是真正意義上使兩者獨(dú)立開來(lái)。setsid函數(shù)能夠使進(jìn)程完全獨(dú)立出來(lái),從而脫離所有其他進(jìn)程的控制。
(4)在子進(jìn)程中創(chuàng)建孫子進(jìn)程 。
因?yàn)閟etsid() 之后 ,默認(rèn)會(huì)是進(jìn)程組組長(zhǎng),而只有進(jìn)程組組長(zhǎng)才能申請(qǐng)一個(gè)控制終端 。那么可以讓它fork后自己再退出,子進(jìn)程做剩下的事。這個(gè)問(wèn)題就迎刃而解了!!
(5)關(guān)閉那些從父進(jìn)程繼承而來(lái)的東西 。
關(guān)閉文件描述符,更改目錄,設(shè)置文件屏蔽字,處理SIGCHLD ,信號(hào) 等等。
#include<stdio.h> #include<stdlib.h> #include<sys/stat.h> #include<time.h> #include<syslog.h> #include<unistd.h> #include<sys/param.h> #include<signal.h> int init_daemon(void) {int pid;int i;signal(SIGTTOU,SIG_IGN); //忽略終端信號(hào)signal(SIGTTIN,SIG_IGN);signal(SIGTSTP,SIG_IGN);signal(SIGHUP,SIG_IGN);pid=fork() ; //第一次forkif(pid > 0)exit(0); //父進(jìn)程退出else if(pid < 0)return -1;/*申請(qǐng)一個(gè)控制終端只能是一個(gè)進(jìn)程組組長(zhǎng),那么可以讓它fork后自己再退出,子進(jìn)程做剩下的事,所以執(zhí)行過(guò)這些步驟后,下一步還應(yīng)該調(diào)用一次fork(),父進(jìn)程退出,子進(jìn)程關(guān)閉繼承于父進(jìn)程打開的文件,修改自己的工作目錄,然后正式成為一個(gè)daemon進(jìn)程。*/pid=fork() ; //第二次forkif(pid > 0)exit(0);else if(pid < 0)return -1;for(i= 0;i< NOFILE ;close(i++));//關(guān)閉文件描述符chdir("/");//更改為根目錄umask(0);//設(shè)置文件屏蔽字為0signal(SIGCHLD,SIG_IGN);//忽略SIGCHLD信號(hào) return 0; } int main(void) {init_daemon();return 0; }(6)當(dāng)進(jìn)程是會(huì)話的進(jìn)程組長(zhǎng)時(shí)setsid()調(diào)用失敗并返回(-1)。setsid()調(diào)用成功后,返回新的會(huì)話的ID,調(diào)用setsid函數(shù)的進(jìn)程成為新的會(huì)話的領(lǐng)頭進(jìn)程,并與其父進(jìn)程的會(huì)話組和進(jìn)程組脫離。由于會(huì)話對(duì)控制終端的獨(dú)占性,進(jìn)程同時(shí)與控制終端脫離。
5.進(jìn)程的退出
正常退出:
- 從main函數(shù)返回
- 調(diào)用exit
- 調(diào)用_exit
異常退出:
- 調(diào)用abort
- 由信號(hào)終止
幾種函數(shù)的比較:
- exit 把控制權(quán)交給系統(tǒng) 。return 把控制權(quán)交給調(diào)用函數(shù)
- exit 在stdlib.h 頭文件中 。 _exit 在unistd.h頭文件中
注:exit()就是退出,傳入的參數(shù)是程序退出時(shí)的狀態(tài)碼,0表示正常退出,其他表示非正常退出,一般都用-1或者1,標(biāo)準(zhǔn)C里有EXIT_SUCCESS和EXIT_FAILURE兩個(gè)宏,用exit(EXIT_SUCCESS);
_exit()函數(shù)的作用最為簡(jiǎn)單:直接使進(jìn)程停止運(yùn)行,清除其使用的內(nèi)存空間,并銷毀其在內(nèi)核中的各種數(shù)據(jù)結(jié)構(gòu);exit() 函數(shù)則在這些基礎(chǔ)上作了一些包裝,在執(zhí)行退出之前加了若干道工序。
exit()函數(shù)與_exit()函數(shù)最大的區(qū)別就在于exit()函數(shù)在調(diào)用exit系統(tǒng)調(diào)用之前要檢查文件的打開情況,把文件緩沖區(qū)中的內(nèi)容寫回文件,就是”清理I/O緩沖”。
兩種情況:
1.子進(jìn)程先于父進(jìn)程退出。
- 如果父進(jìn)程沒有調(diào)用wait 函數(shù)來(lái)等待子進(jìn)程結(jié)束,子進(jìn)程進(jìn)入僵死
- 具體說(shuō)明:
- 線程退出時(shí)會(huì)發(fā)生的那些事:unix提供了一種機(jī)制可以保證只要父進(jìn)程想知道子進(jìn)程結(jié)束時(shí)的狀態(tài)信息, 就可以得到。這種機(jī)制就是: 在每個(gè)進(jìn)程退出的時(shí)候,內(nèi)核釋放該進(jìn)程所有的資源,包括打開的文件,占用的內(nèi)存等。 但是仍然為其保留一定的信息直到父進(jìn)程通過(guò)wait / waitpid來(lái)取時(shí)才釋放
- 僵尸進(jìn)程的那些事: 如果進(jìn)程不調(diào)用wait / waitpid的話, 那么保留的那段信息就不會(huì)釋放,其進(jìn)程號(hào)就會(huì)一直被占用,但是系統(tǒng)所能使用的進(jìn)程號(hào)是有限的,如果大量的產(chǎn)生僵死進(jìn)程,將因?yàn)闆]有可用的進(jìn)程號(hào)而導(dǎo)致系統(tǒng)不能產(chǎn)生新的進(jìn)程. 此即為僵尸進(jìn)程的危害,應(yīng)當(dāng)避免。
2.父進(jìn)程先于子進(jìn)程退出
子進(jìn)程就會(huì)被init 進(jìn)程收養(yǎng),成為孤兒進(jìn)程。
執(zhí)行新程序
新程序的雛形:子進(jìn)程調(diào)用exec 函數(shù)來(lái)執(zhí)行另一個(gè)程序,子進(jìn)程“死亡”。只保留進(jìn)程ID,執(zhí)行另一個(gè)程序
main 函數(shù)原型:main(int argc,char *argv[] ,char *envp[])
exec函數(shù)族包括6個(gè)函數(shù):
include 《 unistd.h 》
int execl(const char *path, const char *arg, …);
int execlp(const char *file, const char *arg, …);
int execle(const char *path, const char *arg, const char *envp[]);
int execv(const char *path, const char *argv[]);
int execve(const char *path, const char *argv[], const char *envp[];
int execvp(const char *file, const char *argv[]);
說(shuō)明:
(以 p 結(jié)尾的兩個(gè)函數(shù))可以只給出文件名,系統(tǒng)就會(huì)自動(dòng)按照環(huán)境變量“$PATH” 所指定的路徑進(jìn)行查找,其他函數(shù)的查找方式都是完整路徑
參數(shù)傳遞方式:exec函數(shù)族的參數(shù)(argv)傳遞有兩種:一種是逐個(gè)列舉的方式,而另一種則是將所有參數(shù)整體構(gòu)造指針數(shù)組傳遞。然而,重點(diǎn)是這些參數(shù)必須以NULL結(jié)束
共同點(diǎn):無(wú)論哪一個(gè)exec函數(shù)都是將可執(zhí)行程序的路徑,命令行參數(shù),環(huán)境變量傳遞給可執(zhí)行程序的main函數(shù) 。
返回值
調(diào)用成功 的 時(shí)候 不會(huì) 返回, 調(diào)用失敗 時(shí) 返回 -1, 并 設(shè)置 errno 為 相應(yīng)的值.
exec 很容易執(zhí)行失敗,其中最常見的原因有:
① 找不到文件或路徑,此時(shí) errno 被設(shè)置為 ENOENT。
② 數(shù)組argv 和envp 忘記用NULL結(jié)束,此時(shí),errno被設(shè)置為 EFAUL。
進(jìn)程等待
#include sys/types.h
#include sys/wait.h
說(shuō)明:
1 .wait 使得父進(jìn)程暫停,等待一個(gè)子進(jìn)程的結(jié)束。返回子進(jìn)程的PID 。
2.參數(shù):status ,所指向的變量存放子進(jìn)程的退出碼
3.waitpid 中pid 的含義:
1.pid > 0 ,等待進(jìn)程ID 等于pid 的子進(jìn)程退出
2.pid == - 1,等待任意 子進(jìn)程
3.waitpid 與wait 的小比較:wait等待第一個(gè)終止的子進(jìn)程,waitpid 指定等待的子進(jìn)程 。
程序1: #include<stdio.h> #include<stdlib.h> #include<unistd.h> #include<sys/types.h> int main(int argc,char *argv[] ,char *environ[]) {int i;int t ;t= atoi(argv[1]);for(i= 0 ;i< t ;i++)printf("-----------------l55555555555555555555555555555555555\n") ;return 0; } 程序2: #include<sys/wait.h> #include<stdio.h> #include<stdlib.h> #include<unistd.h> #include<sys/types.h> int main(int argc,char **argv ,char **environ) {pid_t pid ;int stat_val ;char *a= "50" ;char *b= "10" ;char *str[3] = { a,b, NULL};int n;pid=fork() ;switch(pid){case 0: execve("./test",str,environ) ; exit(0);case -1:printf("ERROR !!\n"); exit(-1);default : printf("11111111111111111111\n"); break;}wait(&n);printf("----------------%d\n",n);exit(0); }編譯語(yǔ)句:gcc -o test execve_1.c ;gcc execve_2.c
;./a.out
執(zhí)行結(jié)果:
說(shuō)明:1.execve(“./test”,str,environ) ; str 就是傳過(guò)去的參數(shù),就是被調(diào)用函數(shù)的argv 數(shù)組,str[0] 就是argv[0] ,str[1]就是argv[1]。
2. 參數(shù)./test ,最好寫為完整路徑 。才不會(huì)容易出錯(cuò)!!
3. 參數(shù)environ ,系統(tǒng)預(yù)定義的全局變量,顯示各個(gè)環(huán)境變量的值。
設(shè)置ID:
setuid :設(shè)置有效用戶ID與實(shí)際用戶ID
stegid:設(shè)置有效用戶組ID與實(shí)際用戶組ID
#include <sys/types.h>#include <unistd.h>int setgid(gid_t gid);int setuid(uid_t uid);說(shuō)明:內(nèi)核檢查一個(gè)進(jìn)程是否具有訪問(wèn)某文件的權(quán)限時(shí),通過(guò)有效用戶ID 來(lái)檢查 ,而不是實(shí)際用戶ID 。
改變進(jìn)程的優(yōu)先級(jí)
#include <sys/time.h>#include <sys/resource.h>int getpriority(int which, int who);//取得程序進(jìn)程執(zhí)行優(yōu)先權(quán))int setpriority(int which, int who, int prio);//設(shè)置進(jìn)程、進(jìn)程組和用戶的進(jìn)程執(zhí)行優(yōu)先權(quán)。
說(shuō)明:(1):getpriority(int which, int who)
參數(shù)which有三種數(shù)值,參數(shù)who則依which值有不同定義:
which who 代表的意義
PRIO_PROCESS who 為進(jìn)程識(shí)別碼
PRIO_PGRP who 為進(jìn)程的組識(shí)別碼
PRIO_USER who 為用戶識(shí)別碼
此函數(shù)返回的數(shù)值介于-20至20 之間,代表進(jìn)程執(zhí)行優(yōu)先權(quán),數(shù)值
越低代表有較高的優(yōu)先次序,執(zhí)行會(huì)較頻繁。
返回值:返回進(jìn)程執(zhí)行優(yōu)先權(quán),如有錯(cuò)誤發(fā)生返回值則為-1且錯(cuò)誤原因存于errno。
(2):setpriority(int which, int who, int prio)
參數(shù)prio介于-20至20之間。代表進(jìn)程執(zhí)行優(yōu)先權(quán),此優(yōu)先權(quán)默認(rèn)是0,而只有超級(jí)用戶(root)允許降低此值。執(zhí)行成功則返回0,如果有錯(cuò)誤發(fā)生返回值則為-1,錯(cuò)誤原因存于errno。
nice 函數(shù):nice函數(shù)改變進(jìn)程優(yōu)先級(jí),也就是改變進(jìn)程執(zhí)行的優(yōu)先順序。
#include <unistd.h>int nice(int inc);小示例:
#include<stdio.h> #include<stdlib.h> #include<unistd.h> #include<sys/types.h> #include<sys/wait.h> #include<sys/time.h> #include<sys/resource.h> int main(void) {pid_t pid ;int n ;int stat_val = 0 ;int old ,new ;pid=fork();switch(pid){case 0:printf("I m child process ,PID = %d ,PPID = %d \n",getpid() ,getppid() );old =getpriority(PRIO_PROCESS,0);printf("old priority is %d \n",old);new =nice(2);printf("new priority is %d \n",new);exit(0) ;case -1:printf("ERROR!!!\n");exit(-1);default :printf("I m parents process ,PID = %d ,PPID = %d \n",getpid() ,getppid() );break ;}wait(&n);exit(0);}執(zhí)行結(jié)果:
OVER !!!!!
轉(zhuǎn)載于:https://www.cnblogs.com/Tattoo-Welkin/p/10335340.html
總結(jié)
以上是生活随笔為你收集整理的linux C总结篇(进程)的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: CentOS报错:Could not r
- 下一篇: linux 搭建testlink的问题总