linux 进程的管理、子进程创建、进程资源的回收

来源:互联网 发布:自动归类的软件 编辑:程序博客网 时间:2024/06/06 03:27

今天介绍进程的进程的管理,子进程创建以及进程资源的回收

首先什么是进程?

答:进程是程序的实例。程序是静态的,是存放在硬盘上的,程序运行起来就形成了进程。程序从磁盘到内存里之后就形成了进程。

进程又分为:用户级进程和内核级进程  

我们下面了解用户级进程:

操作系统为了和管理进程,会有一个控制块来记录进程用到了哪些资源。叫做PCB块。

每一个进程都有自己的一个ID叫做PID。

用以下命令可以查看当前的进程

pstree   查看进程树   (init的PID为1)

ps -aux  查看当前的进程表

ps -o pid,ppid,.....   查看当前bash下的进程的信息(信息自己添加如PID PPID)

 

下面介绍一个创建子进程的函数fork()

#include<unistd.h>

pid_t fork (void)

功能:创建一个子进程

参数:无

返回值:成功时,在子进程中返回0,在父进程中返回子进程的PID  (在A进程里创建了B子进程,A就是B的父进程)。

失败时返回- 1  errno被设置

子进程会继承父进程的PCB里的所有资源(他们除了PID不一样其余在你使用fork()函数之后的一切东西都一样);

介绍函数exit()

#include <stdlib.h>

void exit(int status);

功能:使进程正常终止参数:指定退出的值。将status&0377 的数值传递给父进程

返回值:永远都不返回

在进程终止之后,还有一部分资源滞留在系统中,我们需要回收进程所占用的资源

使用wait()函数可以回收进程的资源

#include <sys/types.h>#include <sys/wait.h>pid_t wait(int *status);

功能:阻塞等待子进程的终止,子进程终止的时候回收子进程的资源,立即返回

参数:

status:用来存储子进程状态码的地址,如果为NULL ,不希望保留子进程的退出状态码,可以使用宏来检测退出状态

  WIFEXITED(status):如果子进程正常终止,返回真
  WEXITSTATUS(status):在上个宏返回真的情况下才能被使用.获取子进程的退出状态码的低8位
  WIFSIGNALED(status):如果子进程是被一个信号终止的返回真
  WTERMSIG(status):返回终止子进程的信号的编号,在上个宏为真的情况下使用

返回值:成功返回种植的子进程的PID,失败返回-1


僵尸进程:子进程已经终止但是父进程没有回收他的资源,这时候子进程就是僵尸进程状态

孤儿进程;:父进程结束了,子进程还没结束。这时候子进程就叫做孤儿进程。并且他会过继给INIT 1号进程(INIT当他的父进程)

创建的子进程和父进程默认是同组的。

下面代码演示:

  1 #include<stdio.h>  2 #include<sys/types.h>  3 #include<unistd.h>  4 #include<stdlib.h>  5 #include<sys/wait.h>  6 int main(){  7     int a;  8     pid_t pid;  9     pid=fork(); 10     if(pid==-1){ 11         perror("fork"); 12         return -1; 13     } 14     if(pid==0){//子进程 15         getchar(); 16         exit(7); 17     }else{ 18         wait(&a); 19         printf("回收成功!\n"); 20         if(WIFEXITED(a))printf("son exit status=%d\n",WEXITSTATUS(a));   21         if(WIFSIGNALED(a))printf("kill signal num=%d\n",WTERMSIG(a)); 22     } 23     return 0; 24 }

结果如下:

tarena@ubuntu:~/LIANXI/10.19$ gcc wait_a.ctarena@ubuntu:~/LIANXI/10.19$ ./a.out回收成功!son exit status=7
在执行程序之后,由于在父进程中wait的存在,他会一直等子进程结束并回收其资源。当我们输入任意指令跳过子进程中的getchar时,子程序结束。
第21行代码:如果我们没有去跳过getchar  而是被一个信号中断了程序  如  kill -9  pid(子进程的pid,利用ps -aux 查看,或直接在程序中getpid)时,会打印终止我们程序的信号编号。



下面介绍在进程执行过程中,加载新的映像(可执行文件),来替换掉从父进程那里继承来的映像的函数:

函数: exec(3)系列函数

#include <unistd.h>extern char **environ;  //全局变量 指向了环境变量列表的首地址       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[]);
功能:执行程序

参数:

共同点:都有exec

l:列表形式   const char *arg       和       char *const argv[]   的区别,有l的话需要将数组的每个成员都写在函数形式参数那里。

V:向量形式  只需要写数组的名字即可

都需要在函数的第二个参数后面加上NULL

p:如果有p就是const char *file  (这里只需要填写要执行的可执行程序的名字,但是PATH环境变量里必须能找到这个可执行文件)没有就是const char *path 写的时候需要加上路径(比如./)

e:没有e就是子进程继承父进程的环境变量,有e就是给子进程传递新的环境变量

返回值:执行成功不返回,不成功返回-1


演示一个简单的,代码如下:

首先这个代码输出当前进程的环境变量:

  1 #include<stdio.h>  2 extern char **environ;  3 int main(){   4     int i=0;  5     for(i=0;environ[i]!=NULL;i++){  6         printf("%s\n",environ[i]);  7     }     8     return 0;  9 }   
下面这个代码演示把上面的程序替换为创建的子进程的程序

  1 #include<stdio.h>  2 #include <unistd.h>  3 #include <unistd.h>  4 int main(){  5     pid_t pid=fork();  6     char *const envp[]={"caption=beijing","wo=cool",NULL};  7     if(pid==-1){  8         perror("fork");  9         return -1; 10     }    11     if(pid==0){ 12         execle("./a","a",NULL,envp); 13     }else{ 14         getchar(); 15     }    16         return 0; 17 }

运行结果如下:

tarena@ubuntu:~/LIANXI/10.19$ gcc execl.ctarena@ubuntu:~/LIANXI/10.19$ ./a.outcaption=beijingwo=cooltarena@ubuntu:~/LIANXI/10.19$ 


下面讲一下在当前bash下运行一个程序发生了什么?
首先什么是bash? 

bash也是一个程序,是操作系统和用户交互的一个进程。

写在bash这个大程序里的小程序叫做内部程序

和bash 互相独立的程序叫做外部程序

所以bash也有自己的环境变量

环境变量又分为自定义变量和环境变量,自定义变量只是适用于当前进程,环境变量是子进程可以继承的环境变量。

当我们运行一个程序时,这个程序实在bash中运行的。其实是bash用fork函数创建了一个子进程,子进程复制了bash的映像,然后在子进程的映像中调用了exec系列的函数,将我们写的程序的可执行文件(映像)替换为子进程从父进程那里复制来的映像。然后执行我们写的程序的可执行文件。

fork之后bash下建立了一个子进程,开始这个子进程和bash是共用一个PCB的,当子进程发生改变时,子进程复制了父进程的PCB并调用了exec系列函数,将你要运行的程序的进程的PCB替换掉你从父进程那里复制来的PCB。然后执行你的程序。

以上为个人总结!如有不妥请指教!偷笑




原创粉丝点击