欢迎光临
我们一直在努力

Linux之进程--进程控制

在操作系统中,进程控制(Process Control)是最核心、最基础的系统能力之一。我们运行的每一个程序、每一条命令,本质都是操作系统对进程的创建、调度、等待和销毁的完整生命周期管理。

一、进程控制核心概念

进程是操作系统资源分配和调度的基本单位,每个进程都有唯一的 PID(进程ID),并由 PCB(进程控制块)维护状态、资源、父子关系等信息。

Linux 下所有进程均由 父进程创建子进程,最终形成以 init/systemd 为根的进程树。进程的完整生命周期分为三步:

  • 创建:父进程通过系统调用生成子进程

  • 运行/终止:子进程执行业务逻辑,主动或被动退出

  • 等待回收:父进程获取子进程退出状态,回收资源,避免僵尸进程

  • 二、进程创建

    进程创建在之前的学习我们就了解过一部分

    fork

    一个新的进程就是内核数据结构+代码,数据

    二、进程创建

    进程创建在之前的学习我们就了解过一部分

    fork创建子进程

    一个新的进程就是内核数据结构+代码,数据

    fork 返回值三种情况

  • pid>0:当前是父进程,返回值为子进程 pid;

  • pid==0:当前是子进程;

  • pid<0:fork 失败(进程数达到系统上限、内存不足)

  • 为什么我们要创建进程??

    让子进程和父进程分流,子进程跑父进程的代码
    (1)父子进程分流执行,分担任务

    fork之后,父子进程共享同一份程序代码,但拥有独立进程调度流:

    • fork返回值区分逻辑:父进程返回子进程 pid,子进程返回 0;

    • 父进程负责监听、调度、等待;子进程单独执行业务逻辑,两者互不阻塞。 典型场景:网络服务器,每来一条客户端连接就 fork 一个子进程处理读写。

    完成进程替换
    (2)配合 exec 系列函数,完成进程程序替换

    单纯 fork 只能复制父进程代码,如果想让子进程运行全新可执行程序(比如 shell 执行外部命令),必须搭配exec族函数:

  • fork创建子进程,复刻父进程环境;

  • 子进程内部调用execvp/execve,丢弃原有代码段,加载新程序; 父进程不受影响,继续原有逻辑。 场景:bash 输入ls,shell 先 fork 子进程,子进程 exec 替换为 ls 程序执行。

  • 写时拷贝vs浅拷贝

    浅拷贝

    写时拷贝

    操作系统会将进程fork出来之后的进程他们的数据变成只读

    如果有任意一方要对数据来进行修改,那么os不会让其直接对该数据进行修改,会先将数据拷贝出来一份,然他再备份的那一块再进行改

    三、进程终止

    进程终止就是要释放代码和数据+内核数据结构

    首先先释放代码和数据,然后再释放内核数据结构

    为什么呢??

    因为父进程是要知道子进程结束的的具体信息,可以分为以下三种

    a,b,c都可以利用两个变量来记录这个信息

    int sig

    int exit_code


    再main函数中return的就是进程退出码

    查看进程终止信号

    echo &?

    进程退出码可以根据我们自己自定义也可以是用系统的strerror

    1.查看系统自己的strerror

    系统的退出码对应的信息

    但是我们使用的比较多的还是自定义

    依靠我们自定义的宏

    对于C这种情况我们需要知道什么情况使其发生异常,怎么被kill

    所以退出码对其并没有意义

    选项场景描述是否触发信号对应信号名称 & 信号编号 (Signumber)
    a 代码结束,返回正确 无信号
    b 代码结束,结果不符合预期 无信号
    c 代码未跑完,进程异常结束 有致命信号 1. SIGINT(2):Ctrl+C 中断2. SIGSEGV(11):段错误3. SIGKILL(9):强制杀进程4. SIGFPE(8):除零崩溃

    那么应该要怎么退出呢

    exit vs _exit


    这是我们之前学到的僵尸进程

    子进程结束后,父进程没有对子进程进行回收,子进程会一直处于僵尸状态,当我再命令界面ctrl+c

    会将全部的进程都杀死


    四、进程等待

    Wait

    pid_t wait(int *wstatus);

    wstatus:传出参数,保存子进程退出状态;传 NULL 表示不关心退出码。

    阻塞等待:没有子进程退出,父进程一直等待.直到任意一个子进程结束,而且只能是任意的子进程不能说自己想要的子进程

    执行 pid_t ret = fork(); 后,系统会生成两份独立进程,同一个 fork 返回两个不同值:

  • 父进程:返回 新创建子进程的 PID(正整数)

  • 子进程:返回 0

  • 创建失败(内存不够、进程数上限):父子都返回 -1

  • ret接收了wait子进程的pid

    Waitpid

    #include <sys/wait.h> pid_t waitpid(pid_t pid, int *wstatus, int options);

    参数 1:pid —— 指定要等待的子进程(核心区分点)

    分 4 种取值规则:

  • pid > 0 只等待 PID = pid 这唯一一个子进程,其他子进程退出也不会触发返回。 例:waitpid(child_pid, &st, 0) 只等刚才 fork 出的子进程。

  • pid == 0 等待和当前父进程同进程组的任意子进程。

  • pid == -1 等待所有子进程,等价 wait(&st)。

  • pid < -1 取绝对值 abs(pid) 作为进程组 ID,等待该进程组下所有子进程。

  • 参数 2:wstatus —— 接收子进程退出状态
    • 传 NULL:不关心子进程退出码,丢弃状态;

    • 传 int 变量地址:内核会把子进程退出信息存入该变量,配合宏解析

    看是否正常终止,如果正常终止就就返回0

    非正常终止要看退出码

    参数 3:options —— 控制阻塞行为(位或组合)
  • 0:默认阻塞模式,无特殊功能。

  • WNOHANG:非阻塞 没有子进程退出时,函数立刻返回0,父进程不会卡住,可以继续执行业务逻辑。

  • WUNTRACED 子进程收到 SIGSTOP/SIGTSTP 暂停时,waitpid 也会返回。

  • WCONTINUED 暂停的子进程收到 SIGCONT 恢复运行时,waitpid 也会返回。


  • 看一下下面那个现象

    status对应的不是退出码吗???

    为什么这里显示不是1,而是256

    当然我们也可以尝试用真的退出码

    故意让被除数为0;

    第 8 个信号:8) SIGFPE 含义:浮点运算异常(除零、浮点数溢出等)。

    父进程通过waitpid是如何获得子进程的退出信息的??

    子进程结束后不会立即退出,先是释放代码和数据,但是内核数据结构并没有释放,里面记录退出码和退出信号并且处于Z状态,父进程可以通过waitpid获取子进程的pid和一些信息

    赞(0)
    未经允许不得转载:171主机测评 » Linux之进程--进程控制
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址