Linux下进程创建, 等待, 终止

在Linux下进程是怎样创建的,关于fork函数又是怎样实现系统调用的,我们可以通过本次实验进行进一步的了解。

在Linux下fork函数是非常重要的函数,它从已存在进程中创建一个新进程。新进程为子进程,而原进程为父进程。

#include  <stdio.h>
pid_t fork(void);
返回值:子进程中返回0,父进程返回子进程id,出错返回-1

进程调用fork,当进程转移到内核中的fork代码后,内核做:

分配新的内存块和内核数据结构给子进程

将父进程的部分数据结构内容拷贝到子进程

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

fork返回,开始调度器调度

当一个进程调用fork之后,就有两个二进制代码相同的进程,而且它们都运行到相同的地方,但每个进程将开始自己的旅程

我们来看一段代码:


执行后结果如下:


为什么结果会执行两条命令呢?

我们知道fork函数调用一次会返回两次的,在程序return之前,函数该做的事情已经做完了,意味着子进程已经建立,当前已有两个进程,建立子进程的父进程(当前你写的这个程序)与建立好的进程,既然有两个进程并且两个进程都要返回,因而它就有了两个返回值。

返回成功:父进程返回子进程的pid,子进程返回0;

返回失败:父进程返回-1,子进程没有被创建

fork一般情况下也要进行分流的,子进程做子进程的事,父进程做父进程的事,通常父子进程代码共享,数据各自开辟虚拟地址空间,采用“写时拷贝”的方式各自修改自己的程序。


运行后代码如下


我们可以看到fork函数确实是调用了一次,却返回了两个值:父进程返回子进程的pid,子进程返回值为0,说明子进程创建成功。

fork也有出错,fork出错的两种原因:

1)当前的进程数已经达到了系统规定的上限,这时errno的值被设置为ENOMEM。

2)系统内存不足,这时errno的值被设置成ENOMEM。

“写时拷贝”:父子代码共享,父子再不写入时,数据也是共享的,当任意一方试图写入时,便已写时拷贝的方式各自一份副本。

下图为虚拟地址空间:


当我们在进行写时拷贝之后发现代码的变化



父进程要修改自己的进程,发送请求后,操作系统为它重新开辟空间。

vfork

vfork函数也是用来创建子进程的,而子进程和父进程共享地址空间,fork的子进程具有独立地址空间,vfork保证子进程先运行,在它调用exec或exet之后父进程才可能被调度运行。代码说明如下:


运行代码后的结果如下:


我们可以看到的是在这里当子进程运行结束时,父进程才开始运行。

我们再来看看书上的一些代码:


通过执行完代码我们得出子进程直接改变了父进程的变量值,因为子进程在父进程的地址空间中运行。

进程终止:

进程退出场景

代码执行完毕,结果是正确的。

代码执行完毕,结果不正确。

代码异常终止。

进程常见的退出方法:

正常终止(可以通过echo  $?  查看进程退出码)

1、从main函数中返回。

2、调用exit

3、_exit

异常退出

Ctrl   +  c 信号终止

_exit函数

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

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

exit函数

#include  <unistd.h>
void exit(int status);

exit最后也会调用_exit,但在调用_exit之前,还做了其他工作:

1、执行用户通过atexit或on_exit定义的清理函数。

2、关闭所有打开的流,所有的缓存数据均被写入

3、调用_exit

我们先来看一个例子:

代码运行结果如下所示:


我们再来看下_exit函数


运行结果如下:


由此通过上面的这些代码,我们得出的结果就是在_exit函数在程序中没起作用,直接就退出程序了,而exit在退出的时候会做一些事情。

进程等待

子进程退出,如果父进程不管不顾,子进程会变成僵尸进程,进而会造成内存泄露。如果子进程变为僵尸进程,那就连kill  -9也不能杀死它,因为他已经是个“死人”了。

最后,我们也需要知道,父进程派给子进程的任务完成的如何。

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

进程等待的方法

wait方法

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

返回值:成功返回被等待进程的pid,失败返回-1

参数:输出型参数,获取子进程退出时的信息,不用关 心时,可以设置为NULL。

waitpid

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

返回值:

正常返回时后waitpid收集到的子进程的id,如果设置了选项WNOHANG,而调用中waitpid发现没有已退出的子进程可收集,则返回0.如果调用中出错,则返回-1,这时errno会被设置成相应的值以指示错误所在。

参数:pid

pid等于-1,等待任意一个子进程,与wait等效。

pid>0,等待其进程id与pid相等的子进程。

status:

WIFEXITED(status):若为正常终止子进程返回状态,则为真。(查看进程是否是正常退出)WEXITSTATUS(status):若WIFEXITED非零,提取子进程退出码。(查看进程的退出码)

options:

WNOHANG:若pid指定的子进程没有结束,则waitpid返回0,不予以等待。若正常结束,则返回该子进程的id。如果子进程已经退出,调用wait/waitpid会立即返回,并且释放资源,获得子进程退出信息。如果在任意时刻调用wait/waitpid,子进程存在并且正常运行,则进程可能会阻塞,如果不存在该子进程,则立即出错返回。

获取子进程status

wait和waitpid,都有一个status参数从,该参数是输出型参数,由操作系统填充。如果传递NULL,表示不关心子进程的退出

状态信息。否则,操作系统会根据该参数,将子进程的退出信息反馈给父进程。status不能简单地当做整型来看待,可以当做位图看待,具体如下所示:



在这里进程的创建,等待终止就总结完了。




  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值