当前位置: 首页 > news >正文

进程、孤儿进程、僵尸进程、fork、wait简介

 进程相关概念

        程序和进程
程序:是指编译好的二进制文件,在磁盘上,占用磁盘空间, 是一个静态的概念.
进程:一个启动的程序, 进程占用的是系统资源,如:物理内存,CPU,终端等,是一个动态的概念

进程状态

        进程基本的状态有5种。分别为初始态,就绪态,运行态,挂起态与终止态。其中初始态为进程准备阶段。

PCB:进程控制块

        进程控制块(Process Control Block,PCB)是操作系统中用于管理和控制进程的重要数据结构。每当操作系统创建一个新进程时,它会为该进程分配一个 PCB,其中包含了与该进程相关的所有信息。Linux内核的进程控制块是task_struct结构体。
       
        vim /usr/src/linux-headers-6.8.0-45/include/linux/sched.h  #6.8.0-45linux版本替换成自己的电脑上的版本   进入这个文件可以查看task_struct结构体的定义。

        task_struct结构体内部成员有很多,我们重点掌握以下部分即可:
进程id。系统中每个进程有唯一的id,在C语言中用pid_t类型表示,其实就是一个非负整数。
进程的状态,有就绪、运行、挂起、停止等状态。
描述虚拟地址空间的信息。
进程切换时需要保存和恢复的一些CPU寄存器。
文件描述符表,包含很多指向file结构体的指针。
用户id和组id。
进程可以使用的资源上限(Resource Limit):使用ulimit -a 命令可以查看

并行和并发

        并发,在一个时间段内, 是在同一个cpu上, 同时运行多个程序。
        如:若将CPU的1S的时间分成100个时间片,每个进程执行完一个时间片必须无条件让出CPU的使用权,这样1S中就可以执行100个进程。高并发可以简单理解为是在有限时间内,系统能够处理尽可能多的进程或请求。
        并行指两个或两个以上的程序在同一时刻发生(需要有多颗cpu,现在我们电脑的cpu都是多核)。就是说,有多个cpu上同时运行着不同程序。

孤儿进程

        孤儿进程的概念:若子进程的父进程已经终止,而子进程还在运行,这个进程就成了孤儿进程。
        为了保证每个进程都有一个父进程,孤儿进程会被init进程(
进程标识符(PID)为1)领养,init进程成为了孤儿进程的养父进程,当孤儿进程退出之后,由init进程完成对孤儿进程的回收。

僵尸进程

        僵尸进程的概念:若子进程终止,父进程还在运行, 但是父进程没有调用wait或waitpid函数完成对子进程的回收,则该子进程就成了僵尸进程。
        
如何解决僵尸进程?
        由于僵尸进程是一个已经终止的进程,所以不能使用kill命令将其杀死

        通过kill其父进程的方法可以消除僵尸进程。

终止掉其父进程后,这个僵尸进程会被init进程领养,由init进程完成对僵尸进程的回收。

fork函数

        函数作用:创建子进程
原型: pid_t fork(void);
使用时需要包含头文件:#include <unistd.h>
函数参数:无
返回值:调用成功:父进程返回子进程的PID,是一个大于0的数;
              子进程返回0。
注意:不是fork函数在一个进程中返回两个值,而是在父子进程各自返回一个值。
fork函数原理图:(PCB在上边有介绍)

子进程创建成功后,代码的执行位置?
        
进程会从 fork() 调用后的下一行代码开始继续执行。这意味着,在 fork() 后面有条件判断或逻辑分支,可以通过检查 fork() 的返回值来确定当前是在父进程还是在子进程中。
如何区分父子进程?
        
通过fork函数的返回值
父子进程的执行顺序?
        
不一定,哪个进程先抢到CPU,哪个进程就先执行。
fork函数代码简单实例:调用fork函数创建子进程,分别打印出子进程pid和父进程pid等。代码里用到了getpid和getppid函数,比较简单,这里不在介绍。

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>int main(void)
{printf("before fork,pid[%d]\n",getpid());pid_t pid = fork();printf("test\n");if(pid<0){perror("fork error");return -1;}else if(pid>0){printf("father:[%d],pid =[%d],fpid=[%d]\n",pid,getpid(),getppid());}else if(pid==0){	printf("child:pid =[%d],fpid=[%d]\n",getpid(),getppid());}printf("after fork,pid:[%d]\n",getpid());return 0;}

运行结果如下:

其次,父子进程对某个变量进行修改时需要注意,父进程若修改某个变量的值,子进程中这个变量的值不会改变,试想,如果子进程中某个变量的值能跟随父进程中对应变量的值进行变化,就相当于进行了父子间进程通信!显然,在这份代码中并没有做到。
 

//fork函数测试
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <unistd.h>#include <sys/wait.h>int test = 6;int main()
{//创建子进程pid_t pid = fork();if(pid<0) //fork失败的情况{perror("fork error");return -1;}else if(pid>0)//父进程{printf("parent: [%d], pid=[%d], fpid=[%d]\n", pid, getpid(),getppid());test += 2;printf("parent:[%p]\n", &test);printf("parent: test==[%d]\n",test);//wait(NULL);}else if(pid==0) //子进程{sleep(2); //为了避免父进程还没有执行, 子进程已经结束了printf("[%p]\n", &test);printf("child: pid==[%d], fpid==[%d]\n", getpid(), getppid());printf("child: test==[%d]\n", test);}return 0;
}

执行结果:这里父进程结束后,子进程的fpid是1(init进程的pid为1),这个进程(就是孤儿进程最上边有解释)会被init进程领养,由init进程完成对孤儿进程的回收。但是执行完并没有自动退出终端,如果想让其自动退出终端,把上述代码的wait注释去掉。

wait函数

        pid_t wait(int *_Nullable wstatus);
函数作用:阻塞并等待子进程退出、回收子进程残留资源、获取子进程结束状态(退出原因)。

返回值:
        成功:清理掉的子进程ID;
        失败:-1 (没有子进程
);

wstatus参数:子进程的退出状态 --
传出参数

  • WIFEXITED(wstatus):为非0        → 进程正常结束
  • WEXITSTATUS(wstatus):获取进程退出状态
  • WIFSIGNALED(wstatus):为非0 → 进程异常终止
  • WTERMSIG(status):取得进程终止的信号编号。
    wait函数实例:实现父进程调用wait函数完成对子进程的回收
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <sys/types.h>
    #include <unistd.h>
    #include <sys/wait.h>int main()
    {//创建子进程pid_t pid = fork();if(pid<0) //fork失败的情况{perror("fork error");return -1;}else if(pid>0)//父进程{printf("father: [%d], pid==[%d], fpid==[%d]\n", pid, getpid(),getppid());int wstatus;pid_t wpid = wait(&wstatus);printf("wpid==[%d]\n", wpid);if(WIFEXITED(wstatus)) //正常退出{printf("child normal exit, wstatus==[%d]\n", WEXITSTATUS(wstatus));}else if(WIFSIGNALED(wstatus)) //被信号杀死{printf("child killed by signal, signo==[%d]\n", WTERMSIG(wstatus));}}else if(pid==0) //子进程{printf("child: pid==[%d], fpid==[%d]\n", getpid(), getppid());sleep(10);return 9;}return 0;
    }
    

    结果:


http://www.mrgr.cn/news/62426.html

相关文章:

  • 接口测试 —— Postman 变量了解一下!
  • 【性能】CPU和GPU交互
  • NVR批量管理软件/平台EasyNVR多个NVR同时管理支持视频投放在电视墙上
  • mysql导出数据
  • Labview写CIP协议
  • Mybatis Plus 自定义 SQL
  • linux 网络包接收过程
  • 输出特殊图案,请在c环境中运行
  • 线程池面试点
  • Threejs后期处理Bloom发光效果
  • BERT在预训练阶段,需要如何处理数据集?
  • 多系统萎缩患者需要的维生素小贴士
  • redis 基础知识(三)
  • pcie5.0接口的主板--战未来
  • matlab对于不可逆的线性方程组求解
  • 基于GEE的非线性回归实现树木覆盖率预测
  • Qt 文件目录操作
  • 如何在Linux环境中的Qt项目中使用ActiveMQ-CPP
  • 2024年10款值得一试的加密软件:企业数据加密新选择
  • spring Bean的概念
  • 深度学习-理论知识
  • SQL神器,ChatGPT4o 比拟DBA
  • 【DBeaver】连接带kerberos的hive[Apache|HDP]
  • Manus Metagloves Pro虚拟现实手套
  • wasm 编译使用示例
  • 使用 Elastic、OpenLLMetry 和 OpenTelemetry 跟踪 LangChain 应用程序