进程(上)- 进程创建, 终止,等待的代码实现过程

一 进程创建
1、fork函数

fork函数很重要,函数功能是:从已存在的进程中创建一个新进程,新创建出的进程为子进程,而原进程为父进程。

#include<unistd.h>
pid_t fork(void);
//返回值:子进程中返回0,父进程返回子进程id,出错返回-1
  • 分配新的内存块和内核数据结构给子进程

  • 将父进程部分数据结构内容拷贝至子进程

  • 添加子进程到系统进程列表中

  • fork()返回,开始调度器调度

2、代码实现

这里写图片描述
运行:
这里写图片描述

注意:
打印了一次Before,两次After,原因在于fork之前父进程独立执行,fork之后,父子两个执行流分别执行,fork之后谁先执行由操作系统调度器决定。
fork函数,子进程返回0,父进程返回子进程的pid。

3、fork用法
  • 父进程希望拷贝自己,使得父子进程同时执行不同的代码段,比如:父进程等待客户端请求,子进程处理请求;

  • 一个进程执行不同的程序,例如:子进程从fork返回后,调用exec函数。

fork调用失败原因:
系统中进程太多
内存不够

4、vfork函数

vfork也是用来创建子进程

vfork用于创建一个子进程,而子进程和父进程共享地址空间,也就是共用虚拟地址空间,fork的子进程具有独立的地址空间

vfork保证子进程先运行,父进程挂起,直到子进程结束,父进程才执行,只有调用exec或exit之后父进程才可能被调度运行

ps 相当于任务管理器,从pcb读取信息
ps -aux getpid/getppid

5、代码实现

这里写图片描述
运行结果:
这里写图片描述


二 进程终止
1、进程退出场景
  • 代码运行完毕,结果正确

  • 代码运行完毕,结果不正确

  • 异常退出
    (往往因为收到信号才退出)

2、进程退出的方法
  • 正常终止
    1、从main返回
    2、调用exit
    3、_exit

  • 异常终止
    ctrl +c,信号终止

3、_exit函数
#include<unistd.h>
void _exit(int status);
//参数:status定义了进程终止状态,父进程通过wait来获取该值

虽然status是int类型,但是仅仅低八位被父进程所用,所以_exit(-1)时,在终端执行$?发现返回值为255。

4、exit函数
#incldue<unistd.h>
void exit(int status);

执行exit(n) 相当于return (n),因为调用main的运行函数会将main的返回值当做exit的参数。

三 进程等待
1、为什么要进程等待
  • 子进程退出,父进程不理,就可能造成僵尸进程的问题,进而内存泄漏

  • 进程一旦变成僵尸进程,kill -9也无能为力

  • 父进程派给子进程的任务完成的结果

  • 父进程通过进程等待的方式,回收子进程资源,获取子进程退出信息

2、wait方法等待
#include<sys/types.h>
#incldue<sys/wait.h>
pid_t wait(int *status)
//返回值:
//成功返回被等待的进程PID,失败返回-1。
//父进程有多个子进程,只有返回PID,才能准确知道是哪个子进程。

//参数:
//输出型参数,获取子进程退出状态,不关心可以设置成NULL

进程一旦调用了wait,就立即阻塞自己,由wait自动分析是否当前进程的某个子进程已经退出,如果让它找到了这样一个已经变成僵尸的子进程,wait就会收集这个子进程的信息,并把它彻底销毁后返回;如果没有找到这样一个子进程,wait就会一直阻塞在这里,直到有一个出现为止。

3、waitpid方法
pid_t waitpid(pid_t pid,int *status,int options);

从本质上讲,系统调用waitpid和wait的作用是完全相同的,但waitpid多出了两个可由用户控制的参数pid和options,从而为我们编程提供了另一种更灵活的方式。下面我们就来详细介绍一下这两个参数:

pid:从参数的名字pid和类型pid_t中就可以看出,这里需要的是一个进程ID。但当pid取不同的值时,在这里有不同的意义。

     pid>0时,只等待进程ID等于pid的子进程,不管其它已经有多少子进程运行结束退出了,只要指定的子进程还没有结束,waitpid就会一直等下去。

     pid=-1时,等待任何一个子进程退出,没有任何限制,此时waitpid和wait的作用一模一样。

     pid=0时,等待同一个进程组中的任何子进程,如果子进程已经加入了别的进程组,waitpid不会对它做任何理睬。

     pid<-1时,等待一个指定进程组中的任何子进程,这个进程组的ID等于pid的绝对值。

options:options提供了一些额外的选项来控制waitpid,目前在Linux中只支持WNOHANG和WUNTRACED两个选项,这是两个常数,可以用”|”运算符把它们连接起来使用,比如:

ret=waitpid(-1,NULL,WNOHANG | WUNTRACED);

如果我们不想使用它们,也可以把options设为0,如:

ret=waitpid(-1,NULL,0);

如果使用了WNOHANG参数调用waitpid,即使没有子进程退出,它也会立即返回,不会像wait那样永远等下去。

而WUNTRACED参数,由于涉及到一些跟踪调试方面的知识,加之极少用到,这里就不多费笔墨了,有兴趣的读者可以自行查阅相关材料。

看到这里,聪明的读者可能已经看出端倪了:wait不就是经过包装的waitpid吗?没错,察看<内核源码目录>/include/unistd.h文件349-352行就会发现以下程序段:

static inline pid_t wait(int * wait_stat)

{

    return waitpid(-1,wait_stat,0);

}

返回值和错误

waitpid的返回值比wait稍微复杂一些,一共有3种情况:

      1、当正常返回的时候,waitpid返回收集到的子进程的进程ID;

      2、如果设置了选项WNOHANG,而调用中waitpid发现没有已退出的子进程可收集,则返回0;

      3、如果调用中出错,则返回-1,这时errno会被设置成相应的值以指示错误所在;

当pid所指示的子进程不存在,或此进程存在,但不是调用进程的子进程,waitpid就会出错返回,这时errno被设置为ECHILD。

猜你喜欢

转载自blog.csdn.net/m0_37925202/article/details/79837320
今日推荐