Linux-进程控制(进程创建、进程终止、进程等待)

news2024/11/13 9:05:12

一、进程创建

1.1 fork函数介绍

        在命令行下我们可以通过 ./ + exe文件 来创建一个进程,通过fork函数,我们可以通过代码的形式从一个进程中创建一个进程,新进程为子进程,原进程为父进程,子进程在创建时,会与父进程共享下面的代码与数据,当数据被修改时,会采用写实拷贝的方式保证进程间的独立性。

//头文件
#include<unistd.h>

//函数
pid_t fork()

//返回值
 #子进程中返回0
 #父进程返回子进程id
 #出错返回-1

进程调用fork,当控制转移到内核中的fork代码后,内核做:

  • 分配新的内存块和内核数据结构给子进程
  • 将父进程部分数据结构内容拷贝至子进程
  • 添加子进程到系统进程列表当中
  • fork返回,开始调度器调度

fork函数常规用法:

  • 一个父进程希望复制自己,使父子进程同时执行不同的代码段。例如,父进程等待客户端请  求,生成子进程来处理请求。
  • 一个进程要执行一个不同的程序。例如子进程从fork返回后,调用exec函数

fork函数失败原因:

  • 系统中有太多的进程
  • 实际用户的进程数超过了限制
     
1.2 使用举例
  #include<unistd.h>
  #include<stdio.h>
  #include<stdlib.h>
  int main()
  {
    //父进程
    printf("I am parent process ppid:%d pid:%d\n",getppid(),getpid());
    //创建子进程
    pid_t id=fork();
    //根据id的值分流,使父子进程执行不同的代码
    if(id == -1)
    {
      exit(-1);
    }
    else if(id == 0)
    {
      //子进程
       printf("I am child process ppid:%d pid:%d\n",getppid(),getpid());
    }
    else
    {                                                                                                                                                                                          
      //父进程
       printf("I am parent process ppid:%d pid:%d\n",getppid(),getpid());
    }
    return 0;
  }

二、进程终止

2.1 进程退出场景

  • 代码运行完毕,结果正确
  • 代码运行完毕,结果不正确
  • 代码没有运行完,发现异常,提前终止

 2.2 错误码与信号码

错误码
  • 在main函数中,我们通常会return 0,return -1等等,那这些数字有什么意义吗?
  //strerror可以查看错误码信息
  #include<unistd.h>
  #include<stdio.h>
  #include<stdlib.h>
  #include<string.h>                                                                                                                                                                           
  int main()
  {
    int i=0;
    for(i=0;i<255;i++)
    {
      printf("%d:%s\n",i,strerror(i));
    }
    return 0;
  }

可以看到,不同的数字拥有不同的含义,0表示成功,非0表示错误,当程序执行完后,操作系统会检测到错误,并将错误信息打印出来,反馈给用户,这样就可以通过查看错误码来确定程序终止的情况,例如:

ps:操作系统中存在一个变量,名字就叫 ,它保存了最近一次进程的错误码,可以通过

echo $? 查看

信号码
  • 模拟野指针异常:
  
   int main()  
  {  
    int* p=NULL;
    *p=1;
    retrun 0;                                                                                                                                                                                  
  }  

  • 通过kill -11 给进程发信号

通过上述两个例子可以看出,异常终止也是因为OS给进程发了信号,让进程终止的

可以通过kill -l 查看所有信号

总结:

确认进程终止情况的方法:

1.先判断是否是异常导致

2.是异常通过查看信号码确定异常信息

3.不是异常直接看错误码确定错误信息即可

三、进程等待

3.1 进程等待的必要性
  • 子进程退出后,若父进程不进行管理,就会造成僵尸进程,会导致内存泄露问题
  • 父进程派给子进程的任务完成的如何,我们需要知道。如,子进程运行完成,结果对还是不对,或者是否正常退出
  • 父进程通过进程等待的方式,回收子进程资源,获取子进程退出信息
3.2 进程等待方法
3.2.1 wait
#include<sys/types.h>
#include<sys/wait.h>

//等待任一个子进程
pid_t wait(int*status);

#返回值:
成功返回被等待进程pid,失败返回-1。

#参数:
输出型参数,获取子进程退出状态,不关心则可以设置成为NULL
  #include <stdio.h>
  #include <unistd.h>
  #include <string.h>
  #include <stdlib.h>
  #include <sys/types.h>
  #include <sys/wait.h>
  
  void ChildRun()
  {
      int cnt = 5;
      while(cnt)
      {
          printf("I am child process, pid: %d, ppid:%d, cnt: %d\n", getpid(), getppid(), cnt    );
          sleep(1);
          cnt--;
      }
  }
  
  int main()
  {
      printf("I am father, pid: %d, ppid:%d\n", getpid(), getppid());
  
      pid_t id = fork();
      if(id == 0)
      {
          // child
          ChildRun();                                                                       
          printf("child quit ...\n");
          exit(0);
      }
      // fahter
     pid_t rid = wait(NULL);
     if(rid>0)
     {
       printf("wait success pid:%d\n",rid);
     }
     return 0;
  }
3.2.2 waitpid
//头文件: 
#include<sys/type.h>
#include<sys/wait.h>

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

#返回值:

  • 当正常返回的时候waitpid返回收集到的子进程的进程ID;
  • 如果设置了选项WNOHANG,而调用中waitpid发现没有已退出的子进程可收集,则返回0;
  • 如果调用中出错,则返回-1,这时errno会被设置成相应的值以指示错误所在;

#参数:
pid:

  • Pid=-1,等待任一个子进程。与wait等效。
  • Pid>0.等待其进程ID与pid相等的子进程。

status:

  • WIFEXITED(status): 若为正常终止子进程返回的状态,则为真。(查看进程是否是正常退出)
  • WEXITSTATUS(status): 若WIFEXITED非零,提取子进程退出码。(查看进程的退出码)

ps:WIFEXITED 与 WEXITSTATUS是两个宏

options:
WNOHANG(非阻塞等待): 若pid指定的子进程没有结束,则waitpid()函数返回0,不予以等待。若正常结束,则返回该子进程的ID。

  • pid = 0 ,子进程还没有退出,需要下一次再检测
  • pid > 0 ,  等待成功了,子进程退出,并且父进程回收成功
  • pid < 0 ,  等待失败
3.3 获取子进程status

        父进程要获取子进程的退出信息最重要的就是错误码与信号码,而进程PCB中会存在两个整形变量,用于保存错误码与信号码信息,操作系统可以根据wait与waitpid中status ,将子进程的信息传递给父进程

status不能简单的当作整形来看待,可以当作位图来看待,具体细节如下图(只研究status低16比特位):

  • 0-7位表示子进程的信号码(可让 status & 0x7F获取)
  • 8-15位表示子进程的错误码 (可让 (status >>8) & 0xFF获取 )

通过位运算获取错误码:

 
  #include <stdio.h>
  #include <unistd.h>
  #include <string.h>
  #include <stdlib.h>
  #include <sys/types.h>
  #include <sys/wait.h>
  
  void ChildRun()
  {
      int cnt = 5;
      while(cnt)
       {
          printf("I am child process, pid: %d, ppid:%d, cnt: %d\n", getpid(), getppid(), cnt    );
          sleep(1);
          cnt--;
      }
  }
  
  int main()
  {
      printf("I am father, pid: %d, ppid:%d\n", getpid(), getppid());
  
      pid_t id = fork();
      if(id == 0)
      {
          // child
          ChildRun();
          printf("child quit ...\n");
          exit(123);
      }
      sleep(7);
       // fahter
      //pid_t rid = wait(NULL);                                                             
      int status = 0;
      pid_t rid = waitpid(id, &status, 0);
      if(rid > 0)
      {
          printf("wait success, rid: %d\n", rid);
      }
      else
      {
  printf("wait failed !\n");
      }
      sleep(3);
      printf("father quit, status: %d, child quit code : %d, child quit signal: %d\n", statu    s, (status>>8)&0xFF, status & 0x7F);
  }

通过宏获取错误码:

  #include <stdio.h>
  #include <unistd.h>
  #include <string.h>
  #include <stdlib.h>
  #include <sys/types.h>
  #include <sys/wait.h>
  
  void ChildRun()
  {
      int cnt = 5;
      while(cnt)
       { 
        printf("I am child process, pid: %d, ppid:%d, cnt: %d\n", getpid(), getppid(), cnt);
          sleep(1);
          cnt--;
      }
  }
  
  int main()
  {
      printf("I am father, pid: %d, ppid:%d\n", getpid(), getppid());
  
      pid_t id = fork();
      if(id == 0)
      {
          // child
          ChildRun();
          printf("child quit ...\n");
          exit(123);
      }
      sleep(7);
      // fahter
      //pid_t rid = wait(NULL);
      int status = 0;
      pid_t rid = waitpid(id, &status, 0);
      if(rid > 0)
      {                                                                                                                                                                                        
        //判断子进程是否是因为异常退出
        if(WIFEXITED(status))
        {
          printf("wait success, child exit code:%d\n", WEXITSTATUS(status));//获取子进程退出码:123      
        }
}
        else
        {
          printf("child process quit unnormal!\n");
        }
      }
      else
      {
          printf("wait failed !\n");
      }
      sleep(3);
      printf("father quit, status: %d, child quit code : %d, child quit signal: %d\n", status, (status>>8)&0xFF, status & 0x7F);
  }

 

3.4 非阻塞等待 

        上述例子都是子进程正常退出的情况,属于阻塞等待,若子进程陷入死循环永远不退出,那么父进程就会一直待在waitpid函数体中,等待子进程退出,这段时间父进程什么事情也不能做,若给waitpid函数的options参数传入WNOHANG的话,此时等待就会变成非阻塞等待,父进程在等待子进程退出期间也可以做相关操作

  #include <stdio.h>
  #include <unistd.h>
  #include <string.h>
  #include <stdlib.h>
  #include <sys/types.h>
  #include <sys/wait.h>
  
  void DoanotherThing()
  {
    printf("I am doing anotherthing\n");
  }
  
  void ChildRun()
  {
      int cnt = 5;
      while(cnt)
      {
          printf("I am child process, pid: %d, ppid:%d, cnt: %d\n", getpid(), getppid(), cnt);
          sleep(1);
          cnt--;
      }
  }
  
  int main()
  {
      printf("I am father, pid: %d, ppid:%d\n", getpid(), getppid());
  
      pid_t id = fork();
      if(id == 0)
      {
          // child
          ChildRun();
          printf("child quit ...\n");
          exit(123);
      }
      // fahter
      while(1)                                                                                                                                                                                 
      {
        int status = 0;
        pid_t rid = waitpid(id, &status, WNOHANG);
       if(rid==0)
      {
 usleep(10000);
        printf("child is running ,check next time!\n");
        DoanotherThing();
      }
        else if(rid > 0)
      {
        //判断子进程是否是因为异常退出
        if(WIFEXITED(status))
        {
          printf("wait success, child exit code:%d\n", WEXITSTATUS(status));//获取子进程退出码:123      
        }
        else
        {
          printf("child process quit unnormal!\n");
        }
        break;
      }
      else
      {
          printf("wait failed !\n");
          break;
      }
  
     }
  }

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1544727.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

LabVIEW智能降噪系统

LabVIEW智能降噪系统 随着噪声污染问题的日益严重&#xff0c;寻找有效的降噪技术变得尤为关键。介绍了一种基于LabVIEW平台开发的智能降噪系统&#xff0c;该系统能够实时采集环境噪声&#xff0c;并通过先进的信号处理技术实现主动降噪&#xff0c;从而有效改善生活和工作环…

Stable Diffusion 进阶教程 - 二次开发(制作您的文生图应用)

目录 1. 引言 2. 基于Rest API 开发 2.1 前置条件 2.2 代码实现 2.3 效果演示 2.4 常见错误 3. 总结 1. 引言 Stable Diffusion作为一种强大的文本到图像生成模型&#xff0c;已经在艺术、设计和创意领域引起了广泛的关注和应用。然而&#xff0c;对于许多开发者来说&#xff…

『Apisix入门篇』从零到一掌握Apache APISIX:架构解析与实战指南

&#x1f4e3;读完这篇文章里你能收获到&#xff1a; &#x1f310; 深入Apache APISIX架构&#xff1a; 从Nginx到OpenResty&#xff0c;再到etcd&#xff0c;一站式掌握云原生API网关的构建精髓&#xff0c;领略其层次化设计的魅力。 &#x1f50c; 核心组件全解析&#xff…

深入了解 Linux 中的 MTD 设备:/dev/mtd* 与 /dev/mtdblock*

目录 前言一、什么是MTD子系统&#xff1f;二、 /dev/mtd* 设备文件用途注意事项 三、/dev/mtdblock* 设备文件用途注意事项 三、这两种设备文件的关系四、关norflash的一些小知识 前言 在嵌入式Linux系统的世界里&#xff0c;非易失性存储技术扮演着至关重要的角色。MTD&#…

4.2 循环语句loop,等差数列求和

汇编语言 1. 循环语句loop loop指令的格式是&#xff1a;loop 标号&#xff0c;CPU执行loop指令的时候&#xff0c;要进行两部操作 cx cx - 1;判断cx中的值&#xff0c;不为0则转至标号处执行程序&#xff0c;如果为0则向下执行 循环使用loop来实现&#xff0c;循环次数存…

基于深度学习的OCR,如何解决图像像素差的问题?

基于深度学习的OCR技术在处理图像像素差的问题时确实面临一定的挑战。图像像素差可能导致OCR系统无法准确识别文本&#xff0c;从而影响其精度和可靠性。尽管已经有一些方法如SRN-Deblur、超分SR和GAN系列被尝试用于解决这个问题&#xff0c;但效果并不理想。然而&#xff0c;这…

上位机图像处理和嵌入式模块部署(qmacvisual拟合圆和拟合椭圆)

【 声明&#xff1a;版权所有&#xff0c;欢迎转载&#xff0c;请勿用于商业用途。 联系信箱&#xff1a;feixiaoxing 163.com】 前面我们学习了拟合直线&#xff0c;今天继续学习下拟合圆和拟合椭圆。其实除了最后一步不同&#xff0c;两者的逻辑是差不多的。一般都是&#xf…

矩阵螺旋输出

问题描述&#xff1a; 所谓螺旋矩阵&#xff0c;顾名思义&#xff0c;就是将矩阵元素以螺旋顺序输出&#xff0c;如图&#xff1a; 解决思路&#xff1a; 由图不难发现&#xff0c;整个螺旋输出过程是一个个左下右上遍历的循环&#xff0c;只是遍历的规模在越变越小&#xff…

2.7、创建列表(List)

概述 列表是一种复杂的容器&#xff0c;当列表项达到一定数量&#xff0c;内容超过屏幕大小时&#xff0c;可以自动提供滚动功能。它适合用于呈现同类数据类型或数据类型集&#xff0c;例如图片和文本。在列表中显示数据集合是许多应用程序中的常见要求&#xff08;如通讯录、…

分享全栈开发医疗小程序 -带源码课件(课件无解压密码),自行速度保存

课程介绍 分享全栈开发医疗小程序 -带源码课件&#xff08;课件无解压密码&#xff09;&#xff0c;自行速度保存&#xff01;看到好多坛友都在求SpringBoot2.X Vue UniAPP&#xff0c;全栈开发医疗小程序 - 带源码课件&#xff0c;我看了一下&#xff0c;要么链接过期&…

SpringBoot集成Solr全文检索

SrpingBoot 集成 Solr 实现全文检索 一、核心路线 使用 Docker 镜像部署 Solr 8.11.3 版本服务使用 ik 分词器用于处理中文分词使用 spring-boot-starter-data-solr 实现增删改查配置用户名密码认证使用 poi 和 pdfbox 组件进行文本内容读取文章最上方有源码和 ik 分词器资源…

pe启动盘破解windows密码wins电脑登录密码修改重置

目录 1.进入电脑BIOS&#xff0c;设置电脑第一启动项为U盘启动2.进入微pe系统3.然后点击界面最左下方的Windows图标4.点击windows密码选择对应用户名称修改&#xff1b; 1.进入电脑BIOS&#xff0c;设置电脑第一启动项为U盘启动 把u盘插到要清除密码的电脑&#xff0c;然后开机…

面试八股——Redis——分布式锁——Redisson

1.看门狗机制 注意看门狗机制&#xff1a;redisson会监听持有锁的线程&#xff0c;并每隔一段时间(releaseTime/3&#xff0c;默认releaseTime为30s)&#xff0c;如果线程还未释放锁的话&#xff0c;会给锁做一次续期。 2. 主从一致性 实际开发中我们会搭建多台redis服务器&a…

【数据结构】非线性结构——二叉树

文章目录 前言1.树型结构1.1树的概念1.2树的特性1.3树的一些性质1.4树的一些表示形式1.5树的应用2.二叉树 2.1 概念2.2 两种特殊的二叉树2.3 二叉树的性质2.4 二叉树的存储2.5 二叉树的基本操作 前言 前面我们都是学的线性结构的数据结构&#xff0c;接下来我们就需要来学习非…

安防监控视频汇聚平台EasyCVR在银河麒麟V10系统中的启动异常及解决方法

安防监控视频平台EasyCVR具备较强的兼容性&#xff0c;它可以支持国标GB28181、RTSP/Onvif、RTMP&#xff0c;以及厂家的私有协议与SDK&#xff0c;如&#xff1a;海康ehome、海康sdk、大华sdk、宇视sdk、华为sdk、萤石云sdk、乐橙sdk等。平台兼容性强&#xff0c;支持Windows系…

密码学及其应用1 —— 密码学概述

1 密码学的基本概念 1.1 网络安全的定义 网络安全是网络领域的一个专业领域&#xff0c;它涵盖了在基础计算机网络基础设施中所采取的措施、网络管理员为保护网络及网络可访问资源免受未授权访问而采纳的政策&#xff0c;以及对其有效性&#xff08;或无效性&#xff09;的持续…

WSL2 设置桥接模式

文章目录 一、前言二、准备阶段三、环境配置3.1 Type-V管理器环境配置3.2 新增.wslconfig 文件 四、遇到的问题以及解决方案 一、前言 ​ 使用 wsl 的过程中&#xff0c;会出现 WSL 的IP地址 找不到&#xff0c;或者无法和计算机通讯&#xff0c;搞 嵌入式 的话&#xff0c;还…

基于nodejs+vue智能菜谱推荐系统python-flask-django-php

本文拟采用nodejs技术和express 搭建系统框架&#xff0c;后台使用MySQL数据库进行信息管理&#xff0c;设计开发的智能菜谱推荐系统。通过调研和分析&#xff0c;系统拥有管理员和用户两个角色&#xff0c;主要具备登录注册、个人信息修改、对用户管理、类型管理、菜谱信息管理…

力扣算题【第二期】

文章目录 1.反转链表1.1 算法题目1.2 算法思路1.3 代码实现 2.回文链表2.1 算法题目2.2 算法思路2.3 代码实现 1.反转链表 1.1 算法题目 给你单链表的头节点 head &#xff0c;请你反转链表&#xff0c;并返回反转后的链表。 1.2 算法思路 1.设置工作指针p,来遍历链表。 2.采…

常用中间件redis,kafka及其测试方法

常用消息中间件及其测试方法 一、中间件的使用场景引入中间件的目的一般有两个&#xff1a;1、提升性能常用的中间件&#xff1a;1) 高速缓存&#xff1a;redis2) 全文检索&#xff1a;ES3) 存日志&#xff1a;ELK架构4) 流量削峰&#xff1a;kafka 2、提升可用性产品架构中高可…