tft每日頭條

 > 生活

 > linux 進程用法

linux 進程用法

生活 更新时间:2024-09-27 18:14:48

程序:死的,隻占用磁盤空間

進程:活的,運行起來的程序,占用系統資源(CPU,内存等)

單道程序設計:所有進程一個一個執行,A執行完了才能執行B

多道程序設計:進程相互穿插執行,并行執行

linux 進程用法(Linux進程基礎知識)1

虛拟内存與物理内存映射

MMU:内存管理單元,完成虛拟地址到物理地址映射

linux 進程用法(Linux進程基礎知識)2

linux 進程用法(Linux進程基礎知識)3

不同進程用戶空間内存映射到不同物理内存區域,而内核空間内存映射到同一塊物理内存區域,因為操作系統就一個,在這個物理内存區域包括了不同進程的PCB(結構體)。

在用戶空間分配一個數組(虛拟内存空間中數組地址是連續的),如果數組長度很長,在映射到物理内存上時,其在物理内存上的地址其實是不連續的,但不影響,反正我們平常取得地址都是虛拟内存地址。

MMU還可以修改程序訪問内存級别,普通程序訪問級别為0,操作系統為3,執行系統調用時,MMU修改了程序訪問内存的訪問級别,這個速度比較慢。

PCB進程控制塊

linux 進程用法(Linux進程基礎知識)4

進程狀态

linux 進程用法(Linux進程基礎知識)5

進程挂起的時候會釋放CPU,例如sleep函數會挂起進程。

常用的幾個環境變量

PATH:可執行文件搜索路徑

SHELL:當前使用shell類型

TERM:當前終端類型,在圖形界面終端下它的值通常是xterm,終端類型決定了一些程序的輸出顯示方式,比如圖形界面終端可以顯示漢字,而字符終端一般不行。

LANG:語言和字符編碼方式

HOME:家目錄路徑

linux 進程用法(Linux進程基礎知識)6

fork()

pid_t fork(void); pid_t getpid(void);//獲取進程号 pid_t getppid(void);//獲取父進程号 uid_t getuid(void);//returns the real user ID of the calling process. uid_t geteuid(void);//returns the effective user ID of the calling process. gid_t getgid(void);//returns the real group ID of the calling process. gid_t getegid(void);//returns the effective group ID of the calling process.

成功:父進程返回子進程進程号pid,子進程返回0;

失敗:父進程返回-1,設置errno,不創建子進程;

linux 進程用法(Linux進程基礎知識)7

子進程把父進程的數據複制一份

fork一般判斷方式:

pid_t pid; pid = fork(); if(pid==-1) { perror("fork error"); } else if(!pid) { //... } else { //... }

循環創建多個子進程

#include <stdio.h> #include <unistd.h> #include <stdlib.h> #include <errno.h> #include <string.h> /* int main(int argc, char const *argv[]) { for(int i=0;i<5;i ) { if(fork()==0) { printf("--I am the %d child process.\n", i 1); break; } } return 0; } */ int main(int argc, char const *argv[]) { int i = 0; for(i=0;i<5;i ) { if(fork()==0) { //printf("--I am the %d child process.\n", i 1); break; } sleep(1);//讓後面打打印依序打印 } if(i==5) { printf("--I am the parent process.\n"); } else { printf("--I am the %d child process.\n", i 1); } return 0; }

linux 進程用法(Linux進程基礎知識)8

父子進程共享哪些内容?

linux 進程用法(Linux進程基礎知識)9

子進程把父進程的數據複制一份,父子進程間遵循讀時共享寫時複制原則。

fork之後先執行父進程還是子進程是不确定的,取決于内核的調度算法。

gdb調試

使用gdb調試時,gdb隻能跟蹤一個進程,可以在fork函數調用之前,通過指令設置gdb調試跟蹤父進程還是子進程,默認跟蹤父進程。

set follow-fork-mode child:設置gdb在fork之後跟蹤子進程

se follow-fork-mode parent:設置gdb在fork之後跟蹤父進程

注意:一定要在fork函數之前設置才有效。

linux 進程用法(Linux進程基礎知識)10

不管跟蹤父進程還是子進程,程序運行結果都是一樣的。隻是單步調試的時候,走的語句不一樣。

exec函數族

fork創建子進程後執行的是和父進程相同的程序,子進程往往要調用一種exec函數去執行另一個程序,當進程調用一種exec函數時,該進程的用戶空間代碼和數據完全被新程序替換,從新程序開始處開始執行,調用exec并不創建新進程,exec前後進程的id不變

将當前進程的.text,.data替換成要加載的程序的.text,.data。

int execl(const char *path, const char *arg, .../* (char *) NULL */); int execlp(const char *file, const char *arg, .../* (char *) NULL */); int execle(const char *path, const char *arg, .../*, (char *) NULL, char * const envp[] */); int execv(const char *path, char *const argv[]); int execvp(const char *file, char *const argv[]); int execvpe(const char *file, char *const argv[],char *const envp[]); int execve(const char *filename, char *const argv[], char *const envp[]);

l:表示參數以列表方式提供

v:表示參數以數組方式提供

p:表示在環境變量path路徑下查找可執行文件。

e:使用環境變量數組,不實用進程原有的環境變量,設置新加載程序的環境變量

事實上,隻有execve是真正的系統調用,其他的幾個函數最終都是調用它,execve在man第二節,其他在第三節。

linux 進程用法(Linux進程基礎知識)11

參數:

path:完整可執行程序路徑 文件名

file:在環境變量path路徑下查找可執行程序文件,隻指定文件名即可

arg:傳遞給新進程的參數,必須以NULL結尾,其中arg[0]一般存程序名,

返回值:通常情況下,exec函數不會返回,調用成功,跳轉到新的程序入口處;錯誤時,返回-1,并設置errno。

execlp:通常用來調用系統程序,如ls、data、cp、cat等。其實,execlp也會在當前目錄下查找可執行程序。

#include <stdio.h> #include <unistd.h> #include <stdlib.h> #include <errno.h> #include <string.h> int main(int argc, char const *argv[]) { pid_t pid; pid = fork(); if(pid==-1) { perror("fork error"); exit(1); } else if(!pid) { execlp("ls","ls","-l","-a","-h",NULL); //execlp("ls","ls","-alh",NULL);//這裡這樣寫效果一樣 perror("execlp error");//execlp出錯才返回執行 exit(1); } else { sleep(1); printf("--parent process---\n"); } return 0; }

linux 進程用法(Linux進程基礎知識)12

execl:一般用于執行自己的程序

#include <stdio.h> #include <unistd.h> #include <stdlib.h> #include <errno.h> #include <string.h> int main(int argc, char const *argv[]) { pid_t pid; pid = fork(); if(pid==-1) { perror("fork error"); exit(1); } else if(!pid) { execl("./printf_hello","./printf_hello",NULL);//main函數命令行參數 //execlp("./printf_hello","./printf_hello",NULL);//這裡也可以execlp,它也會查找當前路徑 perror("execl error");//execl出錯才返回執行 exit(1); } else { sleep(1); printf("--parent process---\n"); } return 0; } #include <stdio.h> int main(int argc, char const *argv[]) { printf("hello world\n"); return 0; }

linux 進程用法(Linux進程基礎知識)13

execvp:

char *arg[] = {"ls","-a","-l","-h",NULL};

execvp("ls",arg);

孤兒進程:

父進程先于子進程結束,子進程變為孤兒進程,systemd進程會成為孤兒進程的父進程

ps ajx

#include <stdio.h> #include <unistd.h> #include <stdlib.h> #include <errno.h> #include <string.h> int main(int argc, char const *argv[]) { pid_t pid; pid = fork(); if(pid==-1) { perror("fork error"); exit(1); } else if(!pid) { while(1) { printf("--child process--pid=%d--ppid=%d--\n",getpid(),getppid()); sleep(1); } } else { printf("--parent processs--pid=%d--\n",getpid()); sleep(9); } return 0; }

linux 進程用法(Linux進程基礎知識)14

linux 進程用法(Linux進程基礎知識)15

linux 進程用法(Linux進程基礎知識)16

kill -9 殺掉子進程

僵屍進程:

進程終止,父進程尚未回收。子進程殘留資源(PCB)存放于内核中,變成僵屍(Zombie)進程。

僵屍進程是不能用kill命令清除掉的,因為kill命令是用來終止進程的,而僵屍進程已經終止,可以kill掉僵屍進程父進程,來讓系統進行回收。

#include <stdio.h> #include <unistd.h> #include <stdlib.h> #include <errno.h> #include <string.h> int main(int argc, char const *argv[]) { pid_t pid; pid = fork(); if(pid==-1) { perror("fork error"); exit(1); } else if(!pid) { printf("--child process--pid=%d--ppid=%d--\n",getpid(),getppid()); sleep(9); } else { while(1) { printf("--parent processs--pid=%d--\n",getpid()); sleep(1); } } return 0; }

linux 進程用法(Linux進程基礎知識)17

子進程結束前:

linux 進程用法(Linux進程基礎知識)18

子進程結束後:

linux 進程用法(Linux進程基礎知識)19

defunct:死者

wait函數

阻塞回收任意一個子進程

一個進程在終止時會關閉掉所有的文件描述符,釋放在用戶空間分配的内存,但它的PCB還保留着,内核在其中保存了一些信息;如果是正常終止,則保存着退出狀态,如果是異常終止,則保存着導緻該進程終止的信号是哪個。這個進程的父進程可以調用wait或者waitpid獲取這些信息,然後徹底清除掉這個進程。我們知道一個進程的退出狀态可以在shell中用特殊變量$?查看,是因為shell是它的父進程,當它終止時,shell調用wait或者waitpid得到它的退出狀态,同時徹底清除掉這個進程。

父進程調用wait回收子進程終止信息,wait函數包括三個功能:

1. 阻塞等待子進程退出

2. 回收子進程殘留資源

3. 獲取子進程結束狀态(退出原因,正常退出->退出值,異常終止->終止信号)

pid_t wait(int *stat_loc);

stat_loc:傳出參數,保存子進程的退出狀态

成功:返回回收的子進程的pid,

失敗:返回-1

判斷進程退出狀态的幾個宏:

WIFEXITED(stat_val)//判斷進程是否正常終止 WEXITSTATUS(stat_val)//取退出值 WIFSIGNALED(stat_val)//判斷進行是否被信号終止 WTERMSIG(stat_val)//取信号值 WIFSTOPPED(stat_val) WSTOPSIG(stat_val) WIFCONTINUED(stat_val)

#include <stdio.h> #include <unistd.h> #include <stdlib.h> #include <errno.h> #include <string.h> #include <sys/wait.h> int main(int argc, char const *argv[]) { pid_t pid; pid_t wpid; int status; pid = fork(); if(pid==-1) { perror("fork error"); exit(1); } else if(!pid) { printf("--child process--pid=%d--ppid=%d--\n",getpid(),getppid()); sleep(10); return 100;//查看退出狀态是否為100 } else { int wpid = wait(&status); if(wpid==-1) { perror("wait error"); exit(1); } printf("--wpid=%d----\n",wpid); if(WIFEXITED(status))//為真,說明子進程正常終止 { printf("--child process exit with %d\n--",WEXITSTATUS(status)); } if(WIFSIGNALED(status))//為真,說明子進程是被信号終止 { printf("--child process killed with %d\n--",WTERMSIG(status)); } } return 0; }

正常退出:

linux 進程用法(Linux進程基礎知識)20

使用信号殺死:

linux 進程用法(Linux進程基礎知識)21

linux 進程用法(Linux進程基礎知識)22

當父進程不關心子進程的退出狀态時,wpid=wait(NULL);

waitpid函數

pid_t waitpid(pid_t pid, int *stat_loc, int options);

返回值:

>0:表示成功回收的子進程pid

0:函數調用時,options設置為WNOHANG,并且,沒有子進程結束。WNOHANG--wait no hang。hang:挂起

-1:失敗,設置errno。

參數:

options:設置為WNOHANG時,指定回收方式為非阻塞。默認為0,阻塞。

WNOHANG return immediately if no child has exited.

pid:

>0:回收指定id的子進程

-1:回收任意子進程(相當于wait)

0:回收和當前調用watipid在一個組的所有子進程

<-1:回收指定進程組内的任意子進程

stat_loc:子進程退出狀态,和wait函數中參數含義一樣。

waitpid(-1,NULL,0);等價于wait(NULL);

waitpid示例:

#include <stdio.h> #include <unistd.h> #include <stdlib.h> #include <errno.h> #include <string.h> #include <sys/wait.h> int main(int argc, char const *argv[]) { int i = 0; int pid,pid_waitfor,wpid; for(i=0;i<5;i ) { pid = fork(); if(!pid) { break; } if(i==2) { pid_waitfor = pid;//指定回收第三個子進程,父進程中保存第三個子進程pid } //sleep(1); } if(i==5) { wpid = waitpid(pid_waitfor,NULL,0);//阻塞等待回收第三個子進程 //wpid = waitpid(-1,NULL,WNOHANG);//不阻塞回收任意一個子進程,沒有結束的子進程,返回值為0。 //wpid = waitpid(-1,NULL,0);//阻塞回收任意一個子進程。 if(wpid==-1) { perror("wait error"); exit(1); } printf("--parent process--waitfor %d--\n",wpid); } else { sleep(1); printf("--child process--pid=%d--\n", getpid()); } return 0; }

linux 進程用法(Linux進程基礎知識)23

wait、waitpid函數一次隻能回收一個子進程,要回收多個子進行需要循環。

循環回收多個子進程

#include <stdio.h> #include <unistd.h> #include <stdlib.h> #include <errno.h> #include <string.h> #include <sys/wait.h> int main(int argc, char const *argv[]) { int i = 0; int pid,wpid; for(i=0;i<5;i ) { pid = fork(); if(!pid) { break; } } if(i==5) { /* while((wpid=waitpid(-1,NULL,0))!=-1)//注意最後返回-1 { printf("--wait child--%d\n",wpid); sleep(1); } */ while((wpid=waitpid(-1,NULL,WNOHANG))!=-1) { if(wpid>0) { printf("--wait child--%d\n",wpid); } else if(wpid==0) { sleep(1); } } } else { sleep(1); printf("--child process--pid=%d--\n", getpid()); } return 0; }

linux 進程用法(Linux進程基礎知識)24

發現waitpid沒有子進程可回收後返回-1。

#include <stdio.h> #include <unistd.h> #include <stdlib.h> #include <errno.h> #include <string.h> #include <sys/wait.h> int main(int argc, char const *argv[]) { int wpid; //wpid=waitpid(-1,NULL,0); wpid=waitpid(-1,NULL,WNOHANG); //兩種結果一樣,不管有沒有設置阻塞,沒有子進程可回收後返回-1 if(wpid==-1) { printf("沒有子進程,返回-1\n"); } if(wpid==0) { printf("沒有子進程,返回0\n"); } return 0; }

linux 進程用法(Linux進程基礎知識)25

,

更多精彩资讯请关注tft每日頭條,我们将持续为您更新最新资讯!

查看全部

相关生活资讯推荐

热门生活资讯推荐

网友关注

Copyright 2023-2024 - www.tftnews.com All Rights Reserved