[Linux系统编程]进程,父子进程,进程操作函数

一.程序,进程,虚拟内存和物理内存的映射关系

程序:不占用内存,未运行,只是二进制可执行文件,相当于剧本
进程:运行起来的程序,占用内存,相当于演出

虚拟内存与物理内存的映射
各个进程有自己的虚拟内存空间,分为用户区和内核区,要运行进程,必须要将数据加载到内存中,这个映射的关系由CPU中的MMU来执行,对于用户区的数据,MMU将其映射到不同的内存区域,各自独立;而对于内核区的数据,例如PCB,MMU会将其映射至同一块内存空间。
那么MMU是怎么划分内存,实现不同区域的映射的?
MMU会将内存分级,0级的内存空间对应内核区,会将内核区的数据映射在这里,而3级对于用户区。

在这里插入图片描述

二.PCB进程控制块相关

做PCB这种数据结构会记录进程的信息,用来管理进程
比较重要的有:
进程ID (PID)
进程状态(就绪 挂起 执行 终止)
进程的当前工作目录
文件描述符表(当前进程打开了哪些文件)
信号相关信息
用户ID 组ID

在这里插入图片描述

三.环境变量

环境变量是用来搭建进程运行环境的,环境变量保存在进程地址空间内部的用户区与内核区交接的位置
常用的环境变量如
PATH —指定可执行文件的搜索路径 默认是 bin目录下,因为bin目录存放着系统的很多可执行文件,系统命令(ls,date等)就依靠这个环境变量搜寻路径。
SHELL —当前的命令解析
HOME —用户家目录

在这里插入图片描述

四.父子进程

1.fork函数
函数原型
pid_t fork()
返回一个pid_t类型(整数)的值,fork会创建一个新的进程,作为当前进程的子进程。
成功时 : 对于父进程,pid_t返回子进程的pid 对于子进程,返回0
失败时: 对于父进程返回-1,不会有子进程被创建。
fork后的子进程:
对于fork后的子进程,它只会执行fork函数后的代码逻辑。
当子进程被创建时,它会继承父进程的全局变量,.data,.text,栈,堆,环境变量等数据。子进程遵循读时共享,写时复制的原则。
父子进程不同的是pid ppid 进程运行时间 fork返回值等数据。
在这里插入图片描述
父子进程完全共享的:
它们共享文件描述符
共享mmap映射区
在这里插入图片描述
当使用gdb调试一个可执行程序时,可以使用以下指令来指定跟随父或子进程的代码逻辑

在这里插入图片描述

2.getpid /getppid

分别代表获取当前进程的pid 和获取父进程的pid

3.exec函数族
作用:不再执行当前的代码逻辑,将代码逻辑替换为另一个文件的,但本身的pid不变,换核不换壳。

当fork函数创建子进程后,子进程所运行的完全是父进程的代码逻辑,子进程要调用一种 exec函数执行另一个程序。当进程调用一种exec函数时,该进程的用户空间代码和数据完全被新程序替换,从新程序的启动例程开始执行。

exec有六种不同的函数,其中最重要的是execl 和 execlp 函数,这六种函数只有execve是系统调用,其余的都是库函数,底层调用了execve
在这里插入图片描述
execl:
主要用来执行自己写的可执行文件,第一个参数为可执行文件的路径,接下来的参数是可变的,代表选项。
末尾以NULL作为守卫。
在这里插入图片描述

execlp:
p代表着PATH环境变量,execlp就是依据环境变量,寻找可执行文件路径,那么默认PATH是bin目录,这代表着我们可以用execlp来执行系统命令
在这里插入图片描述

4.孤儿进程,僵尸进程
孤儿进程:
父进程先于子进程结束,则子进程成为孤儿进程,然后系统会将子进程的父进程设置为init进程,子进程结束时由init进程来回收该孤儿进程。

处理办法:可以直接kill 孤儿进程

僵尸进程:
子进程终止,父进程尚未回收的时间内,子进程残留资源(PCB)存放于内核中,变成僵尸(Zombie)进程。

比如,父进程sleep中,而子进程早已结束,这段时间内子进程未被回收,成为僵尸进程。

处理办法:僵尸进程无法被kill处理,但是可以通过kill 父进程,实现子进程的回收。

5.wait / waitpid
wait函数----------------------------
用于回收子进程
函数作用1:阻塞等待子进程退出(当子进程未结束时,父进程会阻塞等待子进程结束,然后再回收子进程)
函数作用2:清理子进程残留在内核的 pcb 资源
函数作用3:通过传出参数,得到子进程结束状态(通过status返回)

函数原型:

pid_t pid = wait (int *status)

返回值pid: 成功,返回被回收的子进程的pid 失败,返回-1
传出参数status:它代表子进程的结束状态,可以使用若干操作系统提供的宏函数对其解析,如下

①使用WIFEXITED判断子进程是否正常结束,若正常结束,使用WEXITSTATUS得到子进程返回值:
WIFEXITED(status)–》为真–》调用WEXITSTATUS(status)–》得到 子进程退出值。

②使用WIFSIGNALED判断子进程是否被信号异常终止,使用WTERISIG获取子进程异常编号:
WIFSIGNALED(status)–》为真–》调用 WTERISIG(status)–》 得到 导致子进程异常终止的信号编号。

waitpid函数----------------------
与wait不同的是,waitpid通过传入一个指定pid进行子进程的回收,而通过传入WNOHANG作为第三个参数,可以使用非阻塞等待的方式进行回收(即不等待子进程结束)。

传入一个pid,指定某一个进程进行回收。
pid t waitpid(pid_t pid, int *status, int options)

参数:
pid:指定回收的子进程pid
status:(传出)回收进程的状态。
*options:WNOHANG 指定回收方式为,非阻塞。

返回值:

大于0:表示成功回收的子进程的pid
0:函数调用时,参3指定了WNOHANG,并且,没有子进程结束。(父进程先于子进程结束)
-1:失败。errno

wait和waitpid函数每次只能回收一个子进程,如果一个进程有多个子进程,那么wait会随机删除一个子进程。而waitpid通过传入pid进行回收,而如果pid传入值为-1,那么waitpid函数也会随机选择一个子进程回收。

waitpid(-1,*status,0) 相当于 wait(*status)
waitpid对比wait有更强大的两个功能,一个是可以传入子进程pid,指定回收,第二是可以通过WNOHANG来使用不阻塞的方式回收子进程。

回收多个子进程
若一个父进程有多个子进程,可以使用while循环,根据waitpid/wait函数的不同返回值,来达到回收全部子进程的目的。
在父进程的逻辑中,使用while循环,使用wait或waitpid随机收回子进程的方法,在返回值为-1时跳出循环。
若使用阻塞的方式回收,那么返回值会有两种结果:
1.回收的子进程pid
2.-1 ,表示error,即没有子进程可回收

若使用非阻塞的方式回收,那么返回值会有三种结果:
1.回收的子进程pid
2.-1 ,表示error,即没有子进程可回收
3. 0 ,表示子进程未结束,还不能回收,此时可以手动使父进程sleep,等待进程结束。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值