Linux内核学习笔记(3)– 进程的创始与了结

发布时间:2018-12-18  栏目:LINUX  评论:0 Comments

  进程调用 exit() 退出行后,被装置也僵死状态,这时父进程可以通过
wait4()系统调用查询子进程是否截止,之后又举行末的操作,彻底剔除进程所占据的内存资源。
wait4() 系统调用由 linux 内核实现,linux 系统便提供了
wait()、waitpid()、wait3()、wait4()这五只函数,两只函数的参数不同,语义也爆发一线之反差,不过都回到关于截至进程的状态音讯。

无异于、 进程创设:

1、wait() 函数:

  Unix
下之经过成立好特别,与博外操作系统不同,它分点儿步操作来成立与推行过程:
fork() 和 exec() 。首先,fork()通过拷贝当前过程创立一个子过程;然后,exec()函数负责读取可执行文件并拿其载入地址空间初步运行。

  wait() 函数的原型是:

1、fork() :kernel/fork.c

#include <sys/types.h>        // 提供类型 pid_t 的定义
#include <sys/wait.h>

pid_t wait(int *status);

  以Linux系统中,通过调用fork()来创设一个进程。调用 fork()的经过称为大进程,新出的历程称为子进程。在该调用完时,在再次来到点这些相同之位子上,父进程苏醒执行,子进程起首履行。fork()系统调用从根本再次来到两潮:一潮回到大进程,另一样糟糕回到新来的子进程。使用fork()成立新历程的流程如下:

  当进程调用 wait() 时,会停顿目前进程的推行(即死),由 wait()来自动分析是否当前历程的某子进程已经淡出,假诺找到了这么一个早就成为僵尸进程的子进程,wait
就会师收集之子进程的音,并将该根本灭绝后回去;假使没找到这么一个子进程,wait
就会见平昔不通在此地,直到出现僵尸进程。

  1)fork() 调用clone;

  参数 status 保存着子进程退出时的有的状态(包括
task_struct、thread_info及内核栈当)它是一个对 int
类型的指针;即使未以意子进程的停止状态值,只想把这僵尸进程消灭掉(实际上,大多数时段仍旧如此做的),则可将是参数设为
NULL,即:

  2)clone() 调用 do_fork();

pid = wait(NULL);        // 不管子进程的结束状态,直接杀死进程

  3)do_fork() 调用 copy_process() 函数,copy_process() 函数将完成第
4-11 步;

  若是 wait()调用成功,则会再次来到给收集子进程的进程ID;假设给调用进程没有分进程,则调用失败,再次来到-1

  4)调用 dup_task_struct()为新过程创制一个内核栈、thread_info结构和task_struct,那一个价值与当前经过的价值相同;

  接下去用相同截代码来演示一下 wait() 的用法:

 
5)检查并保管新创办是子进程后,当前用户所具备的进程数目没有领先给它们分配的资源的限制;

  1 #include <unistd.h>
  2 #include <stdio.h>
  3 #include <stdlib.h>                                                                    
  4 #include <sys/types.h>
  5 #include <sys/wait.h>
  6 
  7 void main(){
  8     pid_t fpid,rpid;
  9     fpid = fork();
 10     if(fpid < 0){        
 11         perror("error on forking!\n");
 12     }
 13     else if(fpid == 0){
 14         printf("this is a child process! the pid is %d\n",getpid());
 15         sleep(3);
 16     }
 17     else{
 18         rpid = wait(NULL);          // 如果 wait()调用成功,则返回子进程的PID;如果调用失败,则返回 -1
 19         printf("Catch the child process with pid of %d\n",rpid);
 20     }
 21     exit(0);
 22 }    

 
6)清理子进程经过描述符中的片分子(清零或初叶化,如PID),以要得子进程和大进程区别开来;

输出结果如下:

  7)将子进程的状态设置也 TASK_UNINTERRUPTIBLE,保证其不会面投入运作;

图片 1

  8)调用 copy_flags() 以更新 task_struct 的 flags 成员;

   关于 status
参数,相比较复杂,暂时未做啄磨,能够参见这里:https://www.ibm.com/developerworks/cn/linux/kernel/syscall/part3/index.html

  9)调用 alloc_pid() 为新历程分配一个立竿见影之 PID;

 

10)遵照传递给clone() 的参数标志,copy_process()拷贝或共享打开的文本、文件系统音讯、信号处理函数、进程地址空间和命名空间至极;

2、waitpid() 函数:

11)做有了工作并重临一个指向子进程的指针。

  函数原型:

12)回到 do_fork() 函数,如果 copy_process()函数成功重返,新创的子进程将给唤醒并吃那么些投入运作。

#include <sys/types.h>
#include <sys/wait.h>

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

  上边用同一段子简单的代码演示一下 fork() 函数:

   waitpid() 函数的职能跟 wait() 的效用类似,但是,它于 wait()函数多了一定量只参数:

  1 #include <unistd.h>
  2 #include <stdio.h>
  3 
  4 int main(){
  5     pid_t fpid;
  6     int count= 0;
  7     fpid = fork();              // fpid 为fork()的返回值
  8     if(fpid < 0){               // 当fork()的返回值为负值时,表明调用 fork() 出错
  9         printf("error in fork!");
 10     }
 11     else if(fpid  == 0){        // fork() 返回值为0,表明该进程是子进程
 12         printf("this is a child process, the process id is %d\n",getpid());
 13         count++;
 14     }
 15     else{                       // fork() 返回值大于0,表明该进程是父进程,这时返回值其实是子进程的PID
 16         printf("this is a father process, the process id is %d\n",getpid());
 17         count++;
 18     }
 19     printf("计数 %d 次\n",count);
 20     return 0;                                                                          
 21 }

1)参数 pid 为欲等待的子进程的认别码:

输出结果:

  pid < -1 ;等待过程组 ID 为 pid 绝对值的过程组中的任何子进程;

图片 2

  pid = -1 ;等待任何子进程,此时 waitpid() 异常给
wait()。实际上,wait()就是 pid = -1、options = 0
的waitpid(),
 且有:

  可以见到,调用 fork()函数后,原本就暴发一个进程,变成了个别独过程。那有限个经过除了 fpid
的价值不同外几乎完全相同,它们还继续执行接下的主次。由于 fpid
的价值不同,由此会合进不同之判定语句,这为是干吗两单结果暴发不同之处的缘故。另外,可以阅览,父进程的
PID 刚好比子进程的 PID 小1。 fork()  的回到值有以下两种植:

static inline pid_t wait(*status){
    return waitpid(-1,*status,0);  
}

a)在大人进程遭到,fork() 再次回到新成立子进程的 PID;

  pid = 0 ;等待历程组 ID
与当下过程同的任何子进程(也就是是待与一个进程组中的任何子进程);

b)在子进程被,fork() 再次回到0;

  pid > 0 ;等待其他子进程 ID 也 pid
的子进程,只要指定的子进程还未曾终止,waitpid() 就会一向当下去。

c)假诺 fork() 调用出错,则赶回负值

2)参数 options 提供有附加的挑项来控制 waitpid():

 

  WNOHANG;假如没有其余已经完结了底子进程,则就重临,不等待;

 2、exec() :fs/exec.c (源程序
exec.c 实现对二进制可执行文件和 shell 脚本文件的加载与实践)

  WUNTRACED;倘若实进程上暂停实施的情况,则就回到,但截止状态不予理会;

  日常,创设新的长河都是为了这执行新的、不同的次,而继调用
exec() 这组函数就可创建新的地址空间,并拿新的次第载入其中

  也可将立刻点儿单挑选组合起来用,使用 OR
操作。要是非记挂使用这片独选项,也得一直把 options 设为0 ,如下:

  exec() 并无是一个函数,而是一个函数簇,一共包含六单函数,分别吗:
execl、execlp、execle、execv、execvp、execve,定义如下:

waitpid(-1,NULL,WNOHANG | WUNTRACED);     // 没有任何已结束了的子进程或子进程进入暂停执行的状态,则马上返回不等待
waitpid(-1,NULL,0);                // options 设为0,则 waitpid() 会一直等待,直到有进程退出
#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[]);  

3)waitpid() 的归来值,有三种:

  这六个函数的效应实在不比不多,只是接受之参数不同。exec()函数的参数紧要暴发3独片:执行文书部分、命令参数有以及环境变量部分:

a)正常重返时,waitpid() 重回收集及之子进程的PID;

1)执行文书部分:也即是函数中的 path
部分,该有的指出了可执行文件的觅方法。其中
execl、execle、execv、execve的索方法都是动的绝对路径,而
execlp和execvp则可以就叫有文件称展开查找,系统会起环境变量
“$PATH”中检索相应的门道;

b)如果设置了 WNOHANG,而调用 waitpid()时,没有察觉就离的子进程可采,则赶回回0;

2)命令参数有:也尽管是函数中之 file
部分,该片段指出了参数的传递格局同如传送哪些参数。这里,”l”结尾的函数表示用各样列举的方法传送参数;”v”结尾的意味用享有参数全部布局成一个指针数组举办传递,然后拿该数组的首地址当做参数传递给它们,数组中之末段一个指针要求吗
NULL;

c)即便调用出错,则回 -1,这时erron
会被设置为相应的价值为提醒错误所当。(当 pid
所指示的子进程不错在,或这过程是,但切莫是调用进程的子进程, waitpid()就谋面回来出错,这时 erron 被装也 ECHILD)

3)环境变量部分:exec()函数簇使用了系统默认的环境变量,也可以传指定的环境变量。其中 execle
和execve 这简单独函数就好以 envp[] 中指定当前进程所接纳的环境变量。

 

·  当 exec() 执行成功时,exec()函数会取代执行其的进程,此时,exec() 函数没有回去值,进程截止。当 exec()函数执行破产时,将回到战败消息(返回-1),进程继续执行前面的代码。

  1 #include <sys/types.h> 
  2 #include <sys/wait.h>
  3 #include <unistd.h>
  4 #include <stdio.h>
  5 #include <stdlib.h>
  6
  7 void main(){
  8     pid_t fpid,rpid;                          // fpid为fork()的返回值,rpid为waitpid()的返回值
  9     fpid = fork();
 10     if(fpid < 0){
 11         printf("error on forking");
 12     }
 13     else if(fpid == 0){                       // 子进程中 fork() 返回值为0
 14         printf("this is a child process,pid is %d\n",getpid());
 15         sleep(10);                            // 睡眠10s,10s 后子进程退出
 16         exit(0);
 17     }
 18     do{                                  // 父进程中,fork()返回新创建子进程的 PID
 19         rpid = waitpid(fpid,NULL,WNOHANG);    // 等待 PID = fpid 的进程(即子进程)退出,设置了WNOHANG选项,表明当没有发现已退出的子进程时不用等待直接返回,返回值为0;
 20         if(rpid == 0){                        // rpid = 0,说明没有发现已退出的子进程
 21             printf("No child exited\n");
 22             sleep(1);
 23         }
 24     }while(rpid == 0);
 25     if(fpid == rpid)                         // 成功收集了退出的子进程,返回值为被收集子进程的PID
 26         printf("successfully get child process %d\n",rpid);
 27     else
 28         printf("error!\n");
 29 }     

  平常,exec() 会放在 fork()函数的子进程部分,来替代子进程继续执行,exec()执行成功后子进程就会面磨,不过执行破产以来,就必将须要使用 exit()函数来让子进程退出。下面用相同截简单的代码来演示一下 exec()函数簇中的一个函数的用法,另外的参阅:https://www.cnblogs.com/dongguolei/p/8098181.html

结果如下:

  1 #include <unistd.h>
  2 #include <stdio.h>
  3 #include <errno.h>
  4 #include <string.h>
  5 
  6 int main(){
  7     int childpid;
  8     pid_t fpid;
  9     fpid = fork();
 10     if(fpid == 0){                          // 子进程
 11         char *execv_str[] = {"ps","aux",NULL};      // 指令:ps aux 查看系统中所有进程 
 12         if( execv("/usr/bin/ps",execv_str) < 0 ){
 13             perror("error on exec\n");
 14             exit(0);
 15         }
 16     }
 17     else{
 18         wait(&childpid);
 19         printf("execv done\n");
 20     }
 21 }

图片 3

 

  从结果吃可见见,在子进程休眠的10s时里,waitpid()并没有一向等待,而是一贯重临回0,然后开和好的作业(睡眠1s),如此重复了10赖;当子进程退出时,waitpid()收集到离的子进程,并返所收集子进程的PID。

   以那多少个顺序中,使用 fork() 创设了一个子经过,随后登时调用 exec()函数簇中之 execv() 函数,execv()函数执行了相同长指令,显示当前系受有着的过程,结果如下(进程来很多,这里才截了同一有):

 

图片 4

 3、wait3()、wait4() 函数:

图片 5

  函数原型:

  注意看最终两单过程,分别是四叔进程同调用 fork() 后创建的子进程。

#include <sys/tpyes.h>
#include <sys/wait.h>

pid_t wait3(int *status,int options,struct rusage *rusage);
pid_t wait4(pid_t pid,int *status,int options,struct rusage *rusage);

 

   wait3() 和 wait4()函数除了可拿到子进程状态音信外,还可以够拿到子进程的资源采纳信息,这一个信是经参数
rusage 得到的。而 wait3() 与 wait4() 之间的区别是,wait3()等待所有进程,而 wait4() 可以按照 pid 的价值选拔要待的子进程,参数 pid
的含义以及 waitpid() 函数的同样。

次、进程终结

 

  进程被成立后,最后使结。当一个经过终结时,内核必须自由它所占用的资源,并拿当下同一音信告知该大进程。系统经过
exit() 系统调用来拍卖终止与离过程的相干工作,而大多数行事即便是因为
do_exit() 来完成 (kernel/exit.c):

 本文首要参照:

1)将task_struct 中之讲明成员设置为 PF_EXITING;

https://www.ibm.com/developerworks/cn/linux/kernel/syscall/part3/index.html

2)调用 del_timer_sync()删除任一舅决定时器,以保证无定时器在排队,也尚无定时器处理程序在运行;

 

3)调用 exit_mm() 函数放过程占用的
mm_struct,假诺无其余进程使它们(地址空间被共享),就彻底放它们;

 

4)调用 sem__exit() 函数,倘使经过排队齐候 IPC 信号,它尽管去队列;

 

5)调用 exit_files() 和
exit_fs(),以独家递减文件描述符、文件系统数据的援计数,若里面有引用计数的价降到零,则表示从没经过使相应的资源,可以释放掉进程占用的文书描述符、文件系统资源;

 

6)把 task_struct 的 exit_code 成员设置也经过的重返值;

7)调用 exit_notify() 向二伯进程发送信号,并将过程状态设置为
EXIT_ZOMBIE;

8)调用 schedule() 切换至新的经过,继续执行。由于 EXIT_ZOMBIE
状态的过程不会合吃重复调度,所以这是经过所举办的终极一段落代码, do_exit()没有回到值。

  至此,与经过并行关联的享有资源还为放飞掉了,进程不可运行并处在
EXIT_ZOMBIE
退出状态。此时,进程本身所占有的内存还无放,如内核栈、thread_info
结构和 task_struct
结构分外,它有的意思是奔大伯进程提供音讯,当二叔进程收到音信后,或者通告内核那是风马牛不相及的消息后,进程所具有的多余的内存以被假释。父进程可以由此wait4()系统调用查询子进程是否得了,这实际叫进程有了等候特定进程执行了的力量。

  系统经过调用 release_task() 来刑释解教过程描述吻合。

 

留下评论

网站地图xml地图