LinuxC高级编程——进程
LinuxC高級編程——進程
 
宗旨:技術(shù)的學習是有限的,分享的精神是無限的。
 
 
? ? ? ? 每個進程在內(nèi)核中都有一個進程控制塊( PCB)來維護進程相關(guān)的信息, Linux內(nèi)核的 進程控制塊是task_struct結(jié)構(gòu)體。PCB包含的信息:
(1)進程id。系統(tǒng)中每個進程有唯一的id,在C語言中用pid_t類型表示,其實就是一個非負整 數(shù)。
(2)進程的狀態(tài),有運行、掛起、停止、僵尸等狀態(tài)。
(3)進程切換時需要保存和恢復的一些CPU寄存器。 描述虛擬地址空間的信息。
(4)描述控制終端的信息。
(5)當前工作目錄(Current Working Directory) 。
 (6)umask掩碼。 
(7)文件描述符表,包含很多指向file結(jié)構(gòu)體的指針。
(8)和信號相關(guān)的信息。 用戶id和組id。
(9)控制終端、Session和進程組。
(10)進程可以使用的資源上限(Resource Limit)
一、環(huán)境變量
? ? ? ? libc中定義的全局變量environ指向環(huán)境變量表, environ沒有包含在任何頭文件中,所以在使用時要用extern聲明。
#include<stdio.h>int main(void) {extern char **environ;int i;for(i = 0; environ[i] != NULL; i++){printf("%s\n", environ[i]);}return 0; }<span style="font-family: Arial, Helvetica, sans-serif; background-color: rgb(255, 255, 255);">?</span>比較重要的環(huán)境變量:
PATH:可執(zhí)行文件的搜索路徑。
SHELL:當前SHELL,通常是/bin/bash。
TERM:當前終端類型。在圖形界面下通常是xterm。
LANG:語言和locale,決定了字符編碼以及時間、貨幣等信息的顯示格式。
HOME:當前用戶主目錄的路徑,很多程序需要在主目錄下保存配置文件,使得每個用戶在運行該程 序時都有自己的一套配置。
?
二、fork()系統(tǒng)調(diào)用
? ? ? ? fork的作用是根據(jù)一個現(xiàn)有的進程復制出一個新 進程,原來的進程稱為父進程(Parent Process) ,新進程稱為子進程(Child Process)。系統(tǒng)中 同時運行著很多進程,這些進程都是從最初只有一個進程開始一個一個復制出來的。在Shell下輸入 命令可以運行一個程序,是因為Shell進程在讀取用戶輸入的命令之后會調(diào)用fork復制出一個新 的Shell進程,然后新的Shell進程調(diào)用exec執(zhí)行新的程序。
例如:在Shell提示符下輸入命令ls,首先fork創(chuàng)建子進程,這時父進程仍在執(zhí)行/bin/bash程序,然后子進程調(diào)用exec執(zhí)行新的程序/bin/ls
除了子進程和父進程的進程ID不同,其他資源一模一樣。
——創(chuàng)建子進程
(1)函數(shù)原型:
#include<sys/types.h> #include <unistd.h> pid_t fork(void);(2)參數(shù)——無
(3)返回值
? ? ? ? fork調(diào)用失敗則返回-1,返回進程pid,pid大于0:父進程;pid等于0:子進程。執(zhí)行順序不定,根據(jù)內(nèi)核的調(diào)度算法。
? ? ? ? 特點:調(diào)用一次,返回兩次。
setfollow-fork-mode child命令設置gdb在fork之后跟蹤子進程( set follow-fork-mode
 parent則是跟蹤父進程),然后用run命令,看到的現(xiàn)象是父進程一直在運行
三、exec函數(shù)族
? ? ? ? 用fork()創(chuàng)建子進程后執(zhí)行的是個父進程一樣的程序,子進程往往要調(diào)用一種exec函數(shù)以執(zhí)行另一個程序。exec不創(chuàng)建新進程,所以進程id不變。
1、exec函數(shù)族詳解
(1)、函數(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, ..., char *const envp[]); int execv(const char *path, char *const argv[]); int execvp(const char *file, char *const argv[]); int execve(const char *path, char *const argv[], char *const envp[]);(2)、參數(shù)——可變參數(shù)
(3)、返回值
? ? ? ?這些函數(shù)如果調(diào)用成功則加載新的程序從啟動代碼開始執(zhí)行,不再返回,如果調(diào)用出錯則返回-1,所以exec函數(shù)只有出錯的返回值而沒有成功的返回值。?
2、記憶這些函數(shù)的規(guī)則
(1)不帶字母p(表示path)的exec函數(shù)第一個參數(shù)必須是程序的相對路徑或絕對路徑。
(2)對于帶字母p的函數(shù):如果參數(shù)中包含/,則將其視為路徑名。否則視為不帶路徑的程序名,在PATH環(huán)境變量的目錄列表中搜索這個程序。
(3)帶有字母l(表示list)的exec函數(shù)要求將新程序的每個命令行參數(shù)都當作一個參數(shù)傳給它,命令行參數(shù)的個數(shù)是可變的,因此函數(shù)原型中有...,...中的最后一個可變參數(shù)應該是NULL, 起sentinel的作用。
(4)對于帶有字母v(表示vector)的函數(shù),則應該先構(gòu)造一個指向各參數(shù)的指針數(shù)組,然后將該數(shù)組的首地址當作參數(shù)傳給它,數(shù)組中的最后一個指針也應該NULL,就像main函數(shù)的argv參數(shù)或者環(huán)境變量表一樣。
(5)對于以e(表示environment)結(jié)尾的exec函數(shù),可以把一份新的環(huán)境變量表傳給它,其他exec函數(shù) 仍使用當前的環(huán)境變量表執(zhí)行新程序。
只有execve是真正的系統(tǒng)調(diào)用,其它五個函數(shù)最終都調(diào)用execve。通過man可以驗證,man 2 execve,其余的都是man 3。
char*const ps_argv[] = {"ps", "-o", "pid,ppid,pgrp,session,tpgid,comm", NULL}; char*const ps_envp[] = {"PATH=/bin:/usr/bin", "TERM=console", NULL}; execl("/bin/ps", "ps", "-o", "pid,ppid,pgrp,session,tpgid,comm", NULL); execv("/bin/ps", ps_argv); execle("/bin/ps", "ps", "-o", "pid,ppid,pgrp,session,tpgid,comm", NULL, ps_envp); execve("/bin/ps", ps_argv, ps_envp); execlp("ps", "ps", "-o", "pid,ppid,pgrp,session,tpgid,comm", NULL); execvp("ps", ps_argv);<span style="font-family: Arial, Helvetica, sans-serif; background-color: rgb(255, 255, 255);">?</span>四、wait和waitpid函數(shù)
? ? ? ? 僵尸( Zombie)進程:一個進程已經(jīng)終止,但是它的父進程尚未調(diào)用wait或waitpid對它進行清理,這時的進程狀態(tài)稱為僵尸進程。
(1)函數(shù)原型
#include<sys/types.h> #include <sys/wait.h> pid_t wait(int *status); pid_t waitpid(pid_t pid, int *status, int options);(2)參數(shù)
(3)返回值
? ? ? ? 若調(diào)用成功則返回清理掉的子進程id,若調(diào)用出錯則返回-1。父進程調(diào)用wait或waitpid時可能會: 阻塞(如果它的所有子進程都還在運行)。 帶子進程的終止信息立即返回(如果一個子進程已終止,正等待父進程讀取其終止信息)。 出錯立即返回(如果它沒有任何子進程)。
? ? ? ? 區(qū)別:如果父進程的所有子進程都還在運行,調(diào)用wait將使父進程阻塞,而調(diào)用waitpid時如果 在options參數(shù)中指定WNOHANG可以使父進程不阻塞而立即返回0。wait等待第一個終止的子進程,而waitpid可以通過pid參數(shù)指定等待哪一個子進程。
? ? ? ? 調(diào)用:pid = wait(NULL); // 如果成功,wait會返回被收集的子進程的進程ID,如果調(diào)用進程沒有子進程,調(diào)用就會失敗,此時wait返回-1,同時errno被置為ECHILD。?
#include<sys/types.h> #include<sys/wait.h> #include<unistd.h> #include<stdio.h> #include<stdlib.h> int main(void) {pid_t pid;pid = fork();if (pid < 0){perror("fork failed");exit(1);}if (pid == 0){int i;for (i = 3; i > 0; i--){printf("This is the child\n");sleep(1);}exit(3);}else{int stat_val;waitpid(pid, &stat_val, 0);if (WIFEXITED(stat_val)){printf("Child exited with code%d\n", WEXITSTATUS(stat_val));}else if (WIFSIGNALED(stat_val)){printf("Child terminated abnormally,signal %d\n", WTERMSIG(stat_val));}}return 0; } 創(chuàng)作挑戰(zhàn)賽新人創(chuàng)作獎勵來咯,堅持創(chuàng)作打卡瓜分現(xiàn)金大獎總結(jié)
以上是生活随笔為你收集整理的LinuxC高级编程——进程的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
 
                            
                        - 上一篇: C++识别二维码
- 下一篇: Python批量下载中国大学MOOC课件
