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