> 文档中心 > Linux进程控制

Linux进程控制

提示:文章内容较长,请参考目录阅读

这里写目录标题

  • 一、进程创建
    • 1.1 了解fork函数
    • 1.2 fork函数内部完成的工作
    • 1.3 用户空间与内核空间
    • 1.4 写时拷贝
    • 1.5 fork的用法——守护进程
  • 二、进程终止
    • 2.1 进程终止的场景
    • 2.2 进程常见退出方法
    • 2.3 _exit与exit函数
    • 2.4 关于缓冲区
    • 2.5 atexit函数
  • 三、进程等待
    • 3.1 进程等待的意义
    • 3.2 wait
      • 基本功能
      • 僵尸进程
      • wait程序
    • 3.3 waitpid
      • 基本功能
      • 程序
    • 3.4 参数status
  • 四、进程程序替换
    • 4.1 基础知识
    • 4.2 exec函数族
      • exec函数族简介
      • execl
      • execlp
      • execle
      • execv
      • execvp
      • execve

一、进程创建

1.1 了解fork函数

Linux中通过调用fork函数来创建一个新进程,创建出来的新进程为子进程,而原先的进程为父进程。
通过man指令查看fork函数:
在这里插入图片描述
fork函数返回值有两种情况:

  • 创建失败,返回-1
  • 创建成功,给父进程返回一个大于0的数,即子进程的进程号;给子进程返回0

1.2 fork函数内部完成的工作

  • 分配新的内存块和内核数据结构给子进程
  • 将父进程的部分数据结构拷贝至子进程,即拷贝父进程PCB,修改其中的为子进程PID
  • 将子进程添加到系统进程列表(双向链表)
  • fork返回,开始调度器调度(操作系统开始调度

1.3 用户空间与内核空间

  • 内核空间:Linux操作系统和驱动程序都运行在内核空间,所以包括fork在内的系统调用都在内核空间运行
  • 用户空间:一般应用程序都运行在用户空间,包括我们写的程序

程序在运行中如果调用了系统函数,就会切换到内核空间,执行完系统函数的程序后,再返回用户空间。

1.4 写时拷贝

使用fork创建子进程后,父子进程代码共享,子进程的PCB和页表都是拷贝父进程的,同一变量的物理地址和虚拟地址的映射关系时一样的,操作系统并没有给子进程单独开辟物理空间。当父子进程任意一方要进行写入时,便会拷贝数据,此时父子进程通过页表映射到不同的物理地址。
如图所示:

1.5 fork的用法——守护进程

  • 守护进程,守护进程是运行在后台的一种特殊进程,脱离于终端,可以避免被任何终端所产生的信息所打断。父进程创建子进程,通过进程程序替换让子进程执行真正的业务。
    通过ps axj查看当前运行着的守护进程:
    在这里插入图片描述

二、进程终止

2.1 进程终止的场景

  • 代码运行完毕,结果正确
  • 代码运行完毕,结果不正确
  • 代码异常终止

2.2 进程常见退出方法

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

  • main函数返回
  • 调用exit
  • 调用_exit

异常终止

  • 程序崩溃(解引用空指针,内存越界等)
  • Ctrl c,终止程序

2.3 _exit与exit函数

使用方法
_exit终止当前进程
在这里插入图片描述

exit函数
在这里插入图片描述
exit函数的作用也是终止当前进程,并且函数最终会调用_exit函数,但是在调用_exit之前,还做了其他工作:

  • 执行用户通过atexit或者on_exit定义的清理函数
  • 关闭所有打开的流,冲刷缓冲区

区别
exit与_exit的区别如图所示:
在这里插入图片描述
程序演示
用两个程序展示exit与_exit函数的区别
在这里插入图片描述
在这个程序中,由于调用了_exit函数,缓冲区的数据没有被冲刷就退出了进程(注意"hello world"后面没有换行符,因为换行符也会冲刷缓冲区),所以运行该程序后没有打印结果:
Linux进程控制

将程序稍作修改,调用exit函数:
在这里插入图片描述
结果正常打印:
Linux进程控制
因为exit函数在终止进程之前会冲刷缓冲区,所以打印输出了缓冲区中的字符串"hello world!"

2.4 关于缓冲区

  • 缓冲区的作用:
    缓冲区由C标准库建立,目的是减少IO次数,因为IO操作比较耗费时间,所以定义了缓冲区,出发刷新缓冲区的条件之后,缓冲区的内容才会继续IO操作(打印输出、将内容写入文件、从文件中读取等)。
  • 刷新缓冲区的方式:exit、main函数中的return、fflush、"\n"等
  • 缓冲方式:
    全缓冲:缓冲区写满才进行IO
    行缓冲:在输入和输出中遇到"\n"时进行IO操作
    不缓冲:无缓冲区,标准IO库不对字符进行缓冲存储

2.5 atexit函数

atexit函数,注册一个回调函数,在进程终止的时候进行调用(注册回调函数的函数)
在这里插入图片描述

程序验证:
在这里插入图片描述

三、进程等待

3.1 进程等待的意义

僵尸进程
由于子进程退出时,父进程没有回收子进程的退出信息,可能会造成“僵尸进程”的问题,造成内存泄漏。而进程等待时处理僵尸进程最合理的解决方案。

3.2 wait

基本功能

在这里插入图片描述

wait时操作系统提供的函数,包含在头文件中

  • 作用:等待退出的子进程,回收子进程退出状态信息
  • 函数特性:谁调用谁等待,直到等到子进程退出,发起阻塞之后,需等待函数完成功能才返回。
  • 返回值:调用成功返回被等待进程pid,失败则返回-1
  • 参数:status是一个输出型参数,在父进程中定义,获取子进程的退出状态

僵尸进程

在学习使用wait前,先回顾僵尸进程,如果子进程先于父进程退出,父进程没有回收子进程的退出状态信息,子进程便成为了僵尸进程,如图:
在这里插入图片描述
运行结果:
Linux进程控制
通过ps aux查看进程状态信息:
在这里插入图片描述

wait程序

程序1
在这个程序中,子进程仍然先于父进程退出,但是由于父进程中调用了wait函数进行进程等待,回收了子进程的退出状态信息,所以子进程没有成为僵尸进程,如图:
在这里插入图片描述
子进程已经正常退出:
Linux进程控制
程序2
在这里插入图片描述
运行结果:
在这里插入图片描述

在子进程运行的30秒中,通过pstack指令查看其父进程正在执行的代码:
在这里插入图片描述

通过上面的程序我们为你可以看出,当父进程调用wait函数时,如果子进程没有退出,则父进程会一直等待,直至等到子进程退出为止,这种属性称为阻塞。

3.3 waitpid

基本功能

在这里插入图片描述

  • 函数功能:进行进程等待
  • 返回值:正常调用时返回收集到的子进程的pid;
    设置options为"WNOHANG"时,调用中若没有已退出的子进程可收集,则返回0;
    调用出错,则返回-1;
  • 参数:
    pid:-1,表示等待任一子进程;>0,等待进程号与pid值相等j的进程,如:设置pid为10000,则表示只等待进程号为10000的进程。
    status:子进程的退出信息,与wait相同。
    options:可设置为"WNOHANG",表示若子进程未结束,则不再等待,函数返回0,若子进程正常结束,返回子进程的pid。

程序

在这里插入图片描述
程序运行后,通过ps aux查看进程状态:
在这里插入图片描述

3.4 参数status

含义
wait和waitpid函数都有参数status,该参数是一个整型,输出型参数。在父进程中定义,将地址传递给子进程,在子进程中可以更改其值,设置退出状态。
status是一个整型,内部不同的位具有不同的含义:
在这里插入图片描述
正常终止:退出状态会被设置位退出码
异常终止:终止信号和core dump标志位被设置
根据不同不同比特位所表示的含义,可以通过按位与的方式获得各个信号的值(与’1’进行按位与)。
终止信号:status&0x7F
core dump:(status>>7)&0x1
退出状态:(status>>9)&0xFF
程序

根据(status&0x7f)的值是否为0判断子进程是否为正常终止,为0则表明终止信号未被设置,正常终止,否则便是异常终止

  • 正常终止
    程序
    在这里插入图片描述
    运行结果
    Linux进程控制
  • 异常终止
    程序
    在这里插入图片描述
    运行结果
    Linux进程控制

四、进程程序替换

4.1 基础知识

进程替换的原因
父进程创建出来的子进程和父进程拥有相同的代码,因此想要执行不同的程序,就需要让子进程调用程序替换的接口,执行其他程序。
替换原理
调用exec函数进行进程替换并没有创建新的进程,所以进程号没有改变,替换以后,从新程序最开始处执行。并完成两项工作:替换进程的代码和数据段,更新堆栈。

4.2 exec函数族

exec函数族简介

库函数
在这里插入图片描述
系统函数
在这里插入图片描述
exec函数族中,execve是系统提供的函数,其余均为库函数。
命名规则

  • l(list) :表示参数采用列表,第一个参数是可执行程序本身,各个参数之间用","隔开,并以NULL结尾
  • v(vector):参数采用数组(字符指针数组)
  • p(path):代表自动搜索环境变量PATH
  • e(env):表示自己维护环境变量

execl

execl参数传路径和命令行参数
在这里插入图片描述

execlp

execlp会自动搜索PATH环境变量,第一个参数传文件名即可
在这里插入图片描述

execle

参数采用列表,需自己维护环境变量
在这里插入图片描述

execv

参数采用字符指针数组,需定义指针数组保存命令行参数
在这里插入图片描述

execvp

execvp函数参数用数组保存,且会自动搜索PATH环境变量,所以可以不用传递路径
在这里插入图片描述

execve

execve是操作系统提供的函数,其他exec函数都是由C标准库提供,通过调用execve实现,execve参数保存在数组,且需要维护环境变量
在这里插入图片描述