Linux环境编程学习笔记--进程基础

1、fork函数

fork函数用来创建一个子进程,他的函数原型如下:

pid_t fork(void);

通过返回值类型,可以知道当前进程是父进程还是子进程:
pid_t>0:父进程,pid_t为子进程的进程id。
=0:子进程(可通过调用getpid()和getppid()得到自己和父进程的id)
=-1:创建失败

2、进程空间

每个进程都有自己的4GB虚拟地址空间,由虚拟地址到真使物理地址通过映射机制进行。进程空间结构如下(由自下向上)
在这里插入图片描述
fork函数就是把自己的进程地址空间复制一份(不是100%复制,除了少数几个地方)。

实际上进程空间被分割为用户空间和内核空间。对于32 位 linux 来说,从 0-3GB 的空间是用户空间,从 3GB - 4GB 是内核空间。对于一个进程来说,是绝对无法读写内核空间的。
最重要的一点,或者精确一点,进程的用户空间是隔离的,而内核空间是共享的。看起来有点像下面的图
在这里插入图片描述
进程打开的文件都会存储在进程的PCB(位于内核空间中)中的struct file *flip[NR_OPEN] (文件表)中,fork一个子进程后,它们会共享文件表(因为它位于内核空间),f_count会+1.
在这里插入图片描述除了打开的文件外,父进程的很多其他性质也会被子进程共享,比如各种 ID 号、当前工作目录、根目录、资源的限制、信号屏蔽字、进程环境、文件打开执行时关闭标志、共享存储段。

3、写时复制

子进程与父进程虽然都有自己的进程空间,但是其中相同的部分在物理地址中只有一份,两个进程读时实际上时同一个东西,只有某个进程要对这个东西进行修改(写)时,才会在物理地址上复制出相同的一份。

4、exec函数

exec系列函数的作用就是把本进程空间的代码和数据换成指定的数据,然后从新的程序入口点开始执行。
exec后面跟不同的字母,代表不同的参数。函数名中的 v 代表 vector,表示参数是数组。p 代表 path,表示如果你指定的文件不包含路径,就从 path 环境变量指定的路径中搜索文件。那么函数名中的字母 l 表示 list,表示函数参数是可变参数;字母 e 表示函数需要传递环境变量参数。

以 list 可变参数传参
    execl
    execlp
    execle
以 vector 数组传参
    execv
    execvp
    execve

其实,exec系列函数只有execve才是真正的系统调用接口,其它5个都是表中c函数库中的函数,它们最终都调用了execve函数。
ecec函数与fork一起使用,可以使两个进程分别进行独立工作。

5、wait函数

5.1 僵尸进程

僵尸进程是当子进程比父进程先结束,而父进程又没有回收子进程,释放子进程占用的资源,此时子进程将成为一个僵尸进程。如果父进程先退出 ,子进程被init(pid为1的进程,是内核启动的第一个用户级进程)接管,子进程退出后init会回收其占用的相关资源。

5.2wait函数

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

// 参数保存子进程退出通知码,返回 -1 表示没有子进程或者错误。否则返回子进程的进程 id 号。
pid_t wait(int *status);

参数status用来保存被收集进程退出时的一些状态,它是一个指向int类型的指针。但如果我们对这个子进程是如何死掉毫不在意,只想把这个僵尸进程消灭掉,(事实上绝大多数情况下,我们都会这样想),我们就可以设定这个参数为NULL,就像下面这样:

pid = wait(NULL);

如果成功,wait会返回被收集的子进程的进程ID,如果调用进程没有子进程,调用就会失败,此时wait返回-1,同时errno被置为ECHILD

5.3 waitpid函数

根据options返回指定状态的进程号,如果进程退出,则进行清除操作。
函数原型:

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

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

pid>0时,只等待进程ID等于pid的子进程,不管其它已经有多少子进程运行结束退出了,只要指定的子进程还没有结束,waitpid就会一直等下去。
pid=-1时,等待任何一个子进程退出,没有任何限制,此时waitpid和wait的作用一模一样。
pid=0时,等待同一个进程组中的任何子进程,如果子进程已经加入了别的进程组,waitpid不会对它做任何理睬。
pid<-1时,等待一个指定进程组中的任何子进程,这个进程组的ID等于pid的绝对值。
*调用 wait(&status) 函数实际上就相当于调用 waitpid(-1, &status, 0)*

status
status 描述的是子进程的退出状态,它包括了不止一种信息。可以通过宏来判断 status 到底属于哪种信息。

首先,status 分成下面四类:

1 进程正常退出
2 进程被信号终止
3 进程被暂停执行
4 进程被恢复执行

这四种情况,由 4 个宏函数判断到底是哪一种。分别是 WIFEXITED(status)、WIFSIGNALED(status)、WIFSTOPPED(status)和WIFCONTINUED(status)。这些都能从宏函数的名字里看到(exited, signaled, stopped, continued)。这种实现技巧实际上归因于 status 不同比特位代表了不同含义。
options
options 的三个可组合选项如下(可以理解成开关组合):

WNOHANG (设置非阻塞,即使子进程全部正常运行,waitpid 也会立即返回 0,而不会像wait一直阻塞)
WUNTRACED (可获取子进程暂停状态,也就是可获取 stopped 状态)
WCONTINUED (可获取子进程恢复执行的状态,也就是可获取 continued 状态)

你可以任意使用位或 ∣∣ 运算符自由组合他们。如果 options 置空(这三个开关都不打开),也就是 0,这意味着 waitpid 函数是阻塞的(在有子进程正常运行的情况下)。
waitpid 能否接收 stopped 和 continued 状态,取决于 WUNTRACED 和 WCONTINUED 开关是否打开。
如果你的 WUNTRACED 开关未打开,waitpid 函数是不会理会子进程停止状态的。同理,如果 WCONTINUED 开关未打开,waitpid 也不会理会子进程恢复执行的状态。后面的实验,读者可以自行验证,代码也会提供。
返回值
waitpid 的返回值通常也会有 > 0, = 0, 以及 = -1 这几种情况。

=−1,意味着没有子进程或者其它错误。
=0,这只有在打开了 WNOHANG 的情况下才会可能出现。如果子进程都是正常运行没有发生状态改变,它就会返回 0.
大于0,只要有任意一个子进程状态发生改变,比如停止,终止,恢复执行,waitpid 就会返回该子进程的 pid。

参与评论 您还未登录,请先 登录 后发表或查看评论

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

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
©️2022 CSDN 皮肤主题:大白 设计师:CSDN官方博客 返回首页
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值