linux—进程控制

news2024/12/1 0:44:02

进程创建

使用fork函数可以在一个进程中创建一个子进程

fork函数

#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
int main()
{
  printf("begin: 我是一个进程,pid: %d,ppid:%d\n",getpid(),getppid());
  pid_t id = fork();
  if(id == 0)
  {
    //子进程
    printf("我是父进程,pid:%d,ppid:%d\n",getpid(),getppid());
    sleep(1);
  }  
  else if(id >0)
  {
    //父进程
    printf("我是父进程,pid:%d,ppid:%d\n",getpid(),getppid());
    sleep(1);
  }
  else
  {
    //error
  }
  //printf("after fork\n");
  return 0;
}

在这里插入图片描述
为什么这个代码可以同时满足if条件和else if条件?
在这段代码中,fork()调用之后就在原本的process进程中多创建了一个子进程。而在之前的学习中我们知道进程是由PCB+代码和数据,而学了地址空间之后,我们知道所谓的代码和数据其实是进程地址空间结构体+页表,也很好解释了我们的代码为什么可以执行if也可以执行else if的语句,以及这里的id为什么可以一个变量存两个值(fork函数返回值:给父进程返回子进程pid,给子进程返回0)。
是因为在fork过后,我们创建了一个子进程,代码是与父进程共用的,数据在没有修改之前和父进程是共用的,但是如果子进程对数据进行了修改就会在页表重新映射一块内存给子进程存修改后的值,但是虚拟地址(我们C语言%p打印出来的值还是一样的)。这就叫写时拷贝
所以,在父进程代码中的pid变量大于0,所以父进程执行了大于0的条件,在子进程代码中的pid变量等于0,所以子进程执行了等于0的条件。
注意:父子进程,兄弟进程被创建出来,谁先运行,我们是不能确定的,这由调度器决定

fork常规用法

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

fork调用失败的原因

系统中有太多的进程
实际用户的进程数超过了限制

进程终止

对于进程来说,终止只会有三个情况:

  1. 进程正常终止,结果正确
  2. 进程正常终止,结果不正确
  3. 进程异常终止

对于一个进程来说,创建出来就是为了完成工作的,而终止的结果恰恰就是那个工作汇报,给创建进程的那个“人”汇报。

退出码和错误码

为什么main函数总是会返回return 0? 1? 2?, 这个东西给谁了?为什么要返回这个值?
在这里插入图片描述
echo $?表示最近一个程序执行后的退出码
从上面那个例子来说,
我process_stop这个进程返回0给到我们的bash父进程,证明我的main返回的0是个父进程的。而0是退出码,表示进程的运行结果是否正确。0表示正确。
为什么要return 0 ,我们直接用printf打印出来不好吗?但是对于一些用于网络通行或者其他不能打印的场景来说,退出码是辨别进程能否正确执行的标准

错误码:
错误码通常是由系统调用返回的,用于指示系统调用是否成功执行。如果系统调用成功,通常返回 0。如果调用失败,则返回一个负值,这个负值代表一个错误码,可以通过 errno 全局变量获取具体的错误信息。错误码是由操作系统定义的,不同的错误码对应不同的错误情况。
strerror函数:把错误码转换成为字符描述
退出码:
退出码是程序结束时返回给操作系统或父进程的一个值,用于指示程序是正常结束还是遇到了某种错误。退出码通常是一个非负整数,由程序通过 exit() 函数或返回语句返回。退出码通常遵循一定的约定,例如,返回 0 通常表示程序成功执行,而返回非 0 值表示程序执行过程中遇到了问题。
return是一种更常见的退出进程方法。执行return n等同于执行exit(n),因为调用main的运行时函数会将main的返回值当做 exit的参数。
错误码和退出码的区别
用途:错误码用于指示系统调用的状态,而退出码用于指示程序的执行状态。
来源:错误码由系统调用返回,退出码由程序显式返回。

exit函数和 _exit函数

当调用 exit 时,它会执行一些清理工作,包括:

  1. 关闭所有打开的文件描述符(除非它们被设置为保持打开)。
  2. 刷新所有打开的文件流(如 stdout、stderr)并将缓冲区的内容写入文件。
  3. 调用所有注册的退出处理函数(通过 atexit 注册)。
  4. 发送 SIGCHLD 信号给父进程。

_exit 函数提供了一种立即终止进程的方式,不执行任何清理工作。这意味着:

  1. 不关闭文件描述符。
  2. 不刷新文件流。
  3. 不调用退出处理函数。
  4. 不发送 SIGCHLD 信号给父进程。

进程等待

在之前进程状态提到过,一个没有被回收资源的子进程退出会让该子进程会变成僵尸进程。僵尸进程的危害特别大,无法用kill -9去杀死他,一直占有内存,导致内存泄露。这时候则需要我们进程等待出来解决这个问题,在父进程调用wait/waitpid,来进行对子进程状态检测和回收。

waitpid函数和wait函数

wait函数一次只能等待一个进程退出

返回值

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

参数

pid_t pid

Pid=-1,等待任一个子进程。与wait等效。
Pid>0.等待传参pid的指定进程。

int* wstatus

是一个输出型参数,用于接收子进程的结束状态,这个wstatus并不是一个简单的一个整形,可以看做为一个位图。如图:
在这里插入图片描述
现在暂时只关心前16位,这前16位中得到前8位是作为退出码。而后8位作为错误码。
WIFEXITED(wstatus): 一个宏函数,传入wstatus,获取该进程的运行是否异常,若为正常终止子进程返回的状态,则为。(查看进程是否是正常退出)
WEXITSTATUS(wstatus): 一个宏函数,传入wstatus,获取该进程的退出结果,若WIFEXITED非零,提取子进程退出码。(查看进程的退出码)

int options

0:阻塞等待,当我们的子进程还没有返回时,我们父进程调用waitpid去等待子进程,这时父进程就会一直等待子进程的返回什么都不做
WNOHANG: 若pid指定的子进程没有结束,则waitpid()函数返回0,不予以等待。若正常结束,则返回该子进程的ID(非阻塞轮询)非阻塞轮询可以让父进程边等待边去做自己事。

进程替换

原理

创建子进程的PCB,进程地址空间和页表,写时拷贝父进程的数据,发生进程替换之后,从磁盘中载入该进程的数据与代码到该进程的物理内存中,再去修改该进程的页表映射(此过程也是写时拷贝)

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>

int main()
{
    pid_t id = fork();
    if(id == 0)
    {
        printf("before: I am a process,pid:%d,ppid:%d\n",getpid(),getppid());
        execl("/usr/bin/ls","ls","-l",NULL);
        printf("after: I am a process,pid:%d,ppid:%d\n",getpid(),getppid());
        exit(0);
    }
    int status = 0;
    pid_t ret = waitpid(-1,&status, 0);
    if(ret > 0)
    {
        printf("wait success, father pid:%d,ret id:%d\n",getpid(),ret);
    }
    return 0;
}

在这里插入图片描述
从输出结果可以观察得出,进程替换只会把a进程的代码数据替换到b进程中,不会创建新进程,pcb、进程地址空间。程序替换之后,execl函数执行后的代码就不是被执行,因为进程的内容已经被替换进来的进程替换掉了。
替换失败怎么办?
在这里插入图片描述
在这里插入图片描述
我把路径改成错误路径,后发现,替换失败就会继续执行后面的代码,替换成功就会执行替换后的代码。
注意:exec* 函数,只有失败返回值,没有成功返回值,我们可以通过给进程退出码,父进程等待接收的方式,来判断到底有没有接收成功。

进程替换接口

所有exec的第一个参数,都是要拿来替换的进程路径
l:像list一样传参(结点类型为const char*)
p:会在默认的环境变量路径中寻找进程
v:像vector一样传参(char const*数组)
e:可以传入环境变量给子进程,该环境变量只有子进程和他的子进程拥有。
注意:l和v传参都要以null结尾
使用实例代码:

int main()
{
    pid_t id = fork();
    if(id == 0)
    {
        printf("before: I am a process,pid:%d,ppid:%d\n",getpid(),getppid());
        char s1[] = "hello=11";
        putenv(s1);
        printf("子进程环境变量:%s\n",getenv("hello"));
        /* 用execl调用ls */
        // execl("/usr/bin/lsa","ls","-l",NULL);

        /* 用execlp调用ls -a +p会去环境变量搜索进程路径*/
        // execlp("ls","ls","-a",NULL);
        
        /* 用execv调用自定义进程*/
        // char *const argv[] = {"otherExe","-a","-l",NULL};
        // execv("./otherExe",argv);
        // printf("after: I am a process,pid:%d,ppid:%d\n",getpid(),getppid());
        // exit(0);

        /* 用execl执行python代码*/
        // execl("/usr/bin/python3","python3","test.py",NULL);
        

        /* 用execle传递环境变量*/
        //自定义环境变量数组
        char *const myenv[] = {
            "MYVAL=11",
            NULL
        };
        execle("./otherExe","otherExe","-a",NULL,myenv);         
    }
    int status = 0;
    pid_t ret = waitpid(-1,&status, 0);
    printf("父进程环境变量:%s\n",getenv("hello"));
    if(ret > 0)
    {
        printf("wait success, father pid:%d,ret id:%d\n",getpid(),ret);
    }
    return 0;
}

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

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

相关文章

Java项目实战II基于Java+Spring Boot+MySQL的宠物咖啡馆平台的设计与实现(源码+数据库+文档)

目录 一、前言 二、技术介绍 三、系统实现 四、文档参考 五、核心代码 六、源码获取 全栈码农以及毕业设计实战开发&#xff0c;CSDN平台Java领域新星创作者 一、前言 在快节奏的城市生活中&#xff0c;宠物咖啡馆作为一种结合了休闲与宠物陪伴的新型业态&#xff0c;正…

数据服务-实时同步(sersync)

1. 概述 1.之前我们通过rsync定时任务实现定时备份/同步 2. 对于NFS我们需要进行实时同步 2. Sersync原理 3. 上手指南 环境主机web0110.0.0.7(nfs客户端)nfs0110.0.0.31(rsync客户端) (nfs服务端)backup10.0.0.41(rsync服务端) 3.1 rsync服务端准备 参考: 数据服务-备份服务…

[c++高阶] 继承深度剖析

1.前言 继承 是 面向对象三大特性之一&#xff08;封装、继承、多态&#xff09;&#xff0c;所有的面向对象&#xff08;OO&#xff09;语言都具备这三个基本特征&#xff0c;封装相关概念已经在《类和对象》系列中介绍过了&#xff0c;今天主要学习的是 继承&#xff0c;即如…

RTX4060+ubuntu22.04+cuda11.8.0+cuDNN8.6.0 如何根据显卡型号和系统配置cuda和cuDNN所需的安装环境

文章目录 &#x1f315;电脑原配置&#x1f315;安装cuda和cuDNN前的环境选择&#x1f319;cuDNN与CUDA tookit和nvidia driver的对应关系&#x1f319;cuda版本选择⭐查看自己的nvidia driver版本和最大支持的CUDA版本⭐最小支持版本 &#x1f319;查看11.8.0版本的cuda和ubun…

前端学习——CSS——李白代表作品页面(3)

上传资源的地方&#xff1a;http://download.csdn.net/ 项目要求部分&#xff08;1&#xff09; &#xff1a; 支撑知识点&#xff1a; 1.CSS附加方式——外部样式表&#xff1a; --->链接式外部样式表 语法&#xff1a; 在head标签里边写link单标签&#xff0c;其中再…

认知杂谈99《打工人 警惕 画饼》

内容摘要&#xff1a; 领导心理游戏是指领导者利用甜言蜜语和虚假承诺来操控员工情感&#xff0c;使员工产生依赖和盲目信任的行为。他们常以美好未来的描绘来吸引员工&#xff0c;但这些承诺往往难以实现。 员工之所以容易陷入这种心理游戏&#xff0c;是因为他们渴望得到情感…

Golang | Leetcode Golang题解之第455题分发饼干

题目&#xff1a; 题解&#xff1a; func findContentChildren(g []int, s []int) (ans int) {sort.Ints(g)sort.Ints(s)m, n : len(g), len(s)for i, j : 0, 0; i < m && j < n; i {for j < n && g[i] > s[j] {j}if j < n {ansj}}return }

进阶岛第4关:InternVL 多模态模型部署微调实践

准备InternVL模型 我们使用InternVL2-2B模型。该模型已在share文件夹下挂载好&#xff0c;现在让我们把移动出来。 mkdir -p /root/project/joke/modelcp -r /root/share/new_models/OpenGVLab/InternVL2-2B /root/project/joke/model # 不用ln -s 准备环境 这里我们来手动配…

k8s 中微服务之 MetailLB 搭配 ingress-nginx 实现七层负载

目录 1 MetailLB 搭建 1.1 MetalLB 的作用和原理 1.2 MetalLB功能 1.3 部署 MetalLB 1.3.1 创建deployment控制器和创建一个服务 1.3.2 下载MealLB清单文件 1.3.3 使用 docker 对镜像进行拉取 1.3.4 将镜像上传至私人仓库 1.3.5 将官方仓库地址修改为本地私人地址 1.3.6 运行清…

【24最新亲试】ubuntu下载go最新版本

系列综述&#xff1a; &#x1f49e;目的&#xff1a;本系列是个人整理为了工具配置的&#xff0c;整理期间苛求每个知识点&#xff0c;平衡理解简易度与深入程度。 &#x1f970;来源&#xff1a;材料主要源于Ubuntu 升级 golang 版本完美步骤进行的&#xff0c;每个知识点的修…

算法笔记(十一)——优先级队列(堆)

文章目录 最后一块石头的重量数据流中的第 K 大元素前K个高频单词数据流的中位数 优先级队列是一种特殊的队列&#xff0c;元素按照优先级从高到低&#xff08;或从低到高&#xff09;排列&#xff0c;高优先级的元素先出队&#xff0c;可以用 堆来实现 堆是一种二叉树的结构&…

Python 语言学习——应用1.2 数字图像处理(第二节,变换)

目录 1.基础知识 1.图像几何变换概念 2.图像几何变换方式 3.插值运算 4.几何变换步骤 2.各类变换 1.位置变换 2.形状变换 3.代数运算 3.实战演练 1.基础知识 1.图像几何变换概念 在图像处理过程中&#xff0c;为了观测需要&#xff0c;常常需要对 图像进行几何变换&am…

EXCEL_光标百分比

Public Sub InitCells()Dim iSheet As LongFor iSheet Sheets.Count To 1 Step -1Sheets(iSheet).ActivateActiveWindow.Zoom 85ActiveWindow.ScrollRow 1ActiveWindow.ScrollColumn 1Sheets(iSheet).Range("A1").ActivateNext iSheetEnd Sub对日项目中的文档满天…

Python | Leetcode Python题解之第459题重复的子字符串

题目&#xff1a; 题解&#xff1a; class Solution:def repeatedSubstringPattern(self, s: str) -> bool:def kmp(query: str, pattern: str) -> bool:n, m len(query), len(pattern)fail [-1] * mfor i in range(1, m):j fail[i - 1]while j ! -1 and pattern[j …

深度学习中的结构化概率模型 - 结构化概率模型的深度学习方法篇

序言 在深度学习的广阔领域中&#xff0c;结构化概率模型&#xff08; Structured Probabilistic Model \text{Structured Probabilistic Model} Structured Probabilistic Model&#xff09;扮演着至关重要的角色。这类模型利用图论中的图结构来表示概率分布中随机变量之间的…

普渡PUDU MT1:AI赋能,破解大面积场景清洁新挑战

普渡AI智能扫地机器人PUDU MT1:破解大面积场景清洁难题的新利器 在仓储物流、工业车间、交通枢纽、大型商场等大面积场景中,清洁难题一直是管理者们头疼的问题。这些区域面积广阔,清洁任务繁重,传统清洁方式难以胜任。然而,普渡机器人最新推出的AI智能扫地机器人PUDU MT1…

【目标检测】工程机械车辆数据集2690张4类VOC+YOLO格式

数据集格式&#xff1a;Pascal VOC格式YOLO格式(不包含分割路径的txt文件&#xff0c;仅仅包含jpg图片以及对应的VOC格式xml文件和yolo格式txt文件) 图片数量(jpg文件个数)&#xff1a;2694 标注数量(xml文件个数)&#xff1a;2694 标注数量(txt文件个数)&#xff1a;2694 标注…

付费计量系统数据元素(Data elements)

See also Clause 4 for a discussion on general concepts.Data elements are the instruments used to keep record of information on the status of the system and changes over time. 参见条目 4 关于一般概念的讨论。数据元素是仪器使用保留关于系统状态和随时间…

【Kubernetes】常见面试题汇总(四十五)

目录 102.使用 Kubernetes 时可以采取的最佳安全措施是什么&#xff1f; 103.什么是联合集群&#xff1f; 特别说明&#xff1a; 题目 1-68 属于【Kubernetes】的常规概念题&#xff0c;即 “ 汇总&#xff08;一&#xff09;~&#xff08;二十二&#xff09;” 。 题目…

1688商品详情关键词数据-API

要利用 Python 爬虫采集 1688 商品详情数据&#xff0c;需要先了解 1688 网站的页面结构和数据请求方式。一般使用 requests 库请求网站的数据&#xff0c;使用 BeautifulSoup 库解析网页中的数据。 以下是一个简单的 Python 爬虫采集 1688 商品详情数据的示例代码&#xff1a…