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