Linux——进程控制(一)进程的创建与退出

news2024/9/21 19:09:16

目录

一、进程创建

1.写时拷贝

2.创建多个进程

二、进程终止

1.main函数的返回值

2.bash中的$? 

3.自定义退出码

4.C语言的错误码

5.错误码与退出码的区别

6.代码异常终止

7.exit函数

8.总结


一、进程创建

在之前,我们学过linux中的非常重要的函数——fork。他可以从已存在进程中创建一个新进程,新进程为子进程,而原进程为父进程

1.写时拷贝

我们知道,fork之后,父子代码共享,经常会出现同一个变量,父子通过操作的不同,这个变量的值也不同,这个时候就会发生写时拷贝。写时拷贝是如何进行的呢?

通过这张图可以看到,fork之后数据段变成了只读, 子进程需要对数据进行写入,就得需要写时拷贝,写时拷贝需要重新申请空间,进行拷贝,再修改页表,这都是操作系统在帮我们处理的,那么操作系统怎么知道你这一份数据需要进行写时拷贝呢?

父进程创建子进程的时候首先将自己的读写权限修改成只读,然后再创建子进程,这些操作用户并不知道,可能对某些数据进行写入,这样在页表处就会进行权限判断,发现用户没有权限,操作系统此时就会介入,操作系统会判断用户的操作

如果该区域本该是可读可写的,是操作系统修改为只读的,因此操作系统会认为用户的操作不算错误,就会触发重新申请内存再拷贝内容的策略机制,这就是写时拷贝。

如果出错,就直接报错,不做额外处理。

写时拷贝完成后,再将对应的内容在页表中修改为可读可写(没有进行写实拷贝的内容依然是只读的)。这样用户就可以正常访问了。

这是一种惰性分离,每次发生写时拷贝都要开辟空间,将写时拷贝的时间越往后延迟,操作系统就有更多的资源


这里还有一个小问题:你要写入的时候写就完事了,为何还要拷贝一份呢?

因为覆盖和修改是不一样的,很多情况,我们只是想要修改内容的某一部分,这样先拷贝再修改会更合适一点。

2.创建多个进程

我们知道fork的常规用法如下两种

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

如果要创建多个进程来帮我们处理,应该怎么做呢?  直接上代码

#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>

#define N 10

typedef void (*callback)();

void Work()
{
    int cnt = 10;
    while(cnt)
    {
        printf("我是一个子进程, pid: %d, ppid :%d, cnt:%d\n",getpid(),getppid(),cnt--);
        sleep(1);
    }
}

void CreateProcess(int n,callback cb)
{
    int i = 0;
    for(;i<n;i++)
    {
        sleep(1);
        pid_t id = fork();
        if(id == 0)
        {
            //child                                                                    
            printf("子进程创建成功: %d\n",i);
            cb();
            exit(0);
        }
    }
}

int main()
{
    CreateProcess(N,Work);
    sleep(100);
    return 0;
}

这代码对于学过fork的我们来讲,并不算难,多了一个函数指针而已,下面是运行代码。

二、进程终止

进程退出的场景如下三种

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

1.main函数的返回值

我们写C语言程序时,main函数一般都会return 0。只要执行到了return语句,证明我们的代码肯定是运行完毕了的,只是结果还不知道是否正确

在多进程环境中,我们创建子进程的目的是完成父进程不方便办的事,那我们怎么知道子进程办得怎么样,虽然我们可以打印出来看看结果,但在有一些情况下不方便或者不能打印出来看看,此时就可以通过return的值来查看的,main函数的返回值,就叫做进程的退出码,0通常表示成功,非0表示失败。父进程可以通过获取子进程退出码(即main函数的返回值)来得知子进程做得咋样。

成功的还好,知道你吧事情办得很好,如果返回非0,代表这个事没办好,我们得知道是因为什么原因失败的,我们可以用不同的数字表示不同的原因。但纯数字能表示出错的原因,但是不便于人阅读,因此有一个函数交 strerror 函数。

如下可以打印出strerror各个数字代表的出错原因

有很多很多原因 

2.bash中的$? 

在bash命令中输入echo $? 可以打印出最近一个子进程执行完毕时的退出码,有点类似于之前我们学习的环境变量,变量名为?,加了$可以打印出变量里的内容。

如下代码中return 10,执行该进程,bash最后获取到的子进程退出码就为10

但是我们继续执行echo $? 后面退出码就会变成0,因为echo也是bash的一个子进程,执行echo语句后,echo语句就是最后一个子进程了,echo又是正常退出的,因此再输入echo $? 得到的值为0。

main函数的退出码是可以被父进程获取的,用来判断子进程的运行结果 

3.自定义退出码

退出码可以使用C语言内置的,也可以自定义,自己对退出码做解释,因为退出码退出多少(也就是return 返回多少是你自己设置的) 

如下就是自定义的退出码,如果你的代码根据用户的操作出现了错误,可以返回响应的值,来知道发生了什么错误。

4.C语言的错误码

在学习C语言的时候,我们接触过一个名叫 errno 的全局变量,他会在程序在运行过程中调用某些库函数或者系统接口出错的时候,被自动设置。也是记录最后一次出错的信息。

如下代码,只读的方式打开一个不存在文件,我们看一下erron的变化与出错信息

发现错误码为2,错误信息为没有该文件

5.错误码与退出码的区别

  • 错误码通常是衡量调用库函数或者系统调用接口的调用情况。(系统调用也能更改错误码是因为Linux是用C语言写的,提供了C式接口)
  • 退出码通常是一个进程退出的时候,他的退出结果。

他们两个共同的地方在于当失败的时候,用来衡量函数、进程出错时的详细原因。

如下,让错误码与退出码保持了一致

6.代码异常终止

前面五点主要学习的是进程正常退出的问题,可能会有出错码和退出码,如果一个进程异常终止,那么他的退出码也就没有了意义

比如代码中存在 /0 错误,又比如段错误,栈溢出等等,程序就会崩溃,进程就异常了,就不会继续运行了,本质是操作系统将该进程杀掉了,操作系统会用信号的方式将进程杀掉。

输入 kill -l 可以查看 kill命令的信号 

这里我们一直运行一个进程,然后输入kill -8 + 进程pid,就可以通过浮点数错误的方式终止该进程。输入其他方式杀死,也会有相应的错误报告。 

 因此,查看进程是否出现异常,我们只需看有没有收到信号即可

7.exit函数

C语言退出函数 exit() ,括号内部可以添加数字,这也是退出码的一种。 

exit与return的区别在于

在非main函数中return 并不会终止进程,main函数会终止进程。

在任意函数中exit都会终止进程。

8.总结

查看进程运行完毕,结果是否正确,只需要看退出码即可

查看进程异常终止,只需要查看收到的信号是什么即可。

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

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

相关文章

全网爆火的 MBTI 测试,是隐藏的割韭菜工具?

小伙伴们&#xff0c;谁能想到&#xff0c;作为一名冲浪老手&#xff0c;果子在网上又被骗了。 事情是这样的&#xff0c;前几天&#xff0c;我刷微博&#xff0c;看到一个推荐&#xff0c;大概如下图&#xff0c;是一个 MBTI 人格测试。 MBTI 测试&#xff0c;果子早就做过了…

【前端素材】推荐优质在线电影院商城电商网页Hyper平台模板(附源码)

一、需求分析 1、系统定义 在线电影商城是指一个通过互联网提供电影服务的平台&#xff0c;用户可以在该平台上浏览电影资源、租借或购买电影&#xff0c;以及观看在线影片。 2、功能需求 在线电影商城是指一个通过互联网提供电影服务的平台&#xff0c;用户可以在该平台上…

Windows Docker 部署 Redis

部署 Redis 打开 Docker Desktop&#xff0c;切换到 Linux 内核。然后在 PowerShell 执行下面命令&#xff0c;即可启动一个 redis 服务。这里安装的是 7.2.4 版本&#xff0c;如果需要安装其他或者最新版本&#xff0c;可以到 Docker Hub 中进行查找。 docker run -d --nam…

2.25_模式识别大作业的三种方法

filename sys.argv[1] df pd.read_csv(filename,index_col["ID"]) ax df.plot() ax.set_xlabel("Data_ID") ax.set_ylabel("load_value") plt.show() 这段代码是用来读取一个CSV文件&#xff0c;并将文件中的数据绘制成一个简单的折线图。 在…

lv20 QT 常用控件 2

1 QT GUI 类继承简介 布局管理器 输出控件 输入控件 按钮 容器 2 按钮示例 #ifndef WIDGET_H #define WIDGET_H#include <QWidget> #include <QCheckBox> #include <QLineEdit> #include <QPushButton>class Widget : public QWidget {Q_OBJECTpublic…

【C语言】常见的动态内存管理错误

前言 上一篇介绍了C语言中 动态内存管理函数&#xff0c;本片讲解的是 在我们使用动态内存管理时 常见的错误&#xff0c;一起来看看吧~ 欢迎关注个人主页&#xff1a;逸狼 创造不易&#xff0c;可以点点赞吗~ 如有错误&#xff0c;欢迎指出~ 目录 1.对NULL指针的解引⽤操作 错…

如何高效管理项目:从规划到执行

导语&#xff1a;项目管理是一项复杂而重要的工作&#xff0c;涉及多个方面&#xff0c;包括规划、执行、监控和收尾。本文将介绍如何高效管理项目&#xff0c;从规划到执行&#xff0c;帮助您成功完成项目目标。 一、项目规划 项目规划是项目成功的关键。在规划阶段&#xff0…

图片的处理库Thumbnailator

摘要&#xff1a;最近遇到图片处理的问题&#xff0c;借助了Thumbnailator库&#xff0c;记录下使用步骤如下…… 图片处理&#xff0c;JDK中也提供了对应的工具类&#xff0c;不过处理较麻烦&#xff0c;Thumbnailator 是Google一个 开源Java 图像处理库&#xff0c;用于简化 …

力扣:35. 搜索插入位置

力扣&#xff1a;35. 搜索插入位置 描述 给定一个排序数组和一个目标值&#xff0c;在数组中找到目标值&#xff0c;并返回其索引。如果目标值不存在于数组中&#xff0c;返回它将会被按顺序插入的位置。 请必须使用时间复杂度为 O(log n) 的算法。 示例 1: 输入: nums [1,…

MySQL5.7.44版本压缩包在Win11系统快速安装

一.背景 主要还是为了公司的带徒弟任务。我自己也喜欢MySQL的绿色版本。 1.软件版本说明 MySQL版本&#xff1a;5.7.44 压缩包版本&#xff0c;相当于绿色版。当然&#xff0c;你也可以使用window系统的Installer版本去安装。 操作系统&#xff1a;Win11家庭版 二.MySQL软…

MYSQL--JDBC*

一.介绍: 1.JDBC是一种用于执行SQL于语句的JAVA API,JDBC是一种使用JAVA访问数据库的执行规范标准,能够为不同的数据库提供统一的访问!由一组使用JAVA语言编写的接口以及类组成的 2.JDBC核心的类以及相关的接口主要有: DriverManager 注册驱动 Connection 使用…

期货开户坚持固定的盈利模式

1、超级操盘手比的往往不是技术&#xff0c;而是素质。成功的交易者有着一种与众不同的品质&#xff0c;他们拥有正确的思维方式&#xff0c;严谨的交易态度&#xff0c;强烈的自信心、果敢&#xff0c;和面对失败永不言败的精神&#xff0c;即使在系统最困难的时候&#xff0c…

C++ //练习 10.14 编写一个lambda,接受两个int,返回它们的和。

C Primer&#xff08;第5版&#xff09; 练习 10.14 练习 10.14 编写一个lambda&#xff0c;接受两个int&#xff0c;返回它们的和。 环境&#xff1a;Linux Ubuntu&#xff08;云服务器&#xff09; 工具&#xff1a;vim 代码块 /***************************************…

5G 网络建设【华为OD机试-JAVAPythonC++JS】

题目描述 现需要在某城市进行5G网络建设&#xff0c;已经选取N个地点设置5G基站&#xff0c;编号固定为1到N&#xff0c;接下来需要各个基站之间使用光纤进行连接以确保基站能互联互通&#xff0c;不同基站之间架设光纤的成本各不相同&#xff0c;且有些节点之间已经存在光纤相…

使用MyBatisPlus实现向数据库中存储List类型的数据

使用MyBatisPlus实现向数据库中存储List类型的数据 问题描述 建表时&#xff0c;表中的这五个字段为json类型 但是在入库的时候既不能写入数据&#xff0c;也不能查询出数据。 解决方案&#xff1a; 1.首先明确&#xff0c;数据存入的时候是经过了数据类型转化&#xff0c…

Nacos配置

目录 启动nacos 项目步骤 Nacos服务分级存储模型​编辑 服务跨域集群调用问题 NacosRule负载均衡 服务实例的权重设置 环境隔离-namespace Nacos环境隔离 Nacos和Eureak对比 临时实例和非临时实例 Ncaos与Eureka的共同点 Nacos与Eureka的区别 Nacos配置管理 统一配…

Linux 系统安装/卸载 Nginx教程

优质博文&#xff1a;IT-BLOG-CN 一、安装Nginx 【1】首先通过Nginx官网确定需要安装的版本&#xff0c;如果Linux联网则直接在Linux服务上使用wget命令将Nginx安装包下载到/usr/local/目录下&#xff1a; [rootxxx local]# wget -c http://nginx.org/download/nginx-1.22.1.…

electron+vue3全家桶+vite项目搭建【28】封装窗口工具类【2】窗口组,维护窗口关系

文章目录 引入实现效果思路主进程模块渲染进程模块测试效果 引入 demo项目地址 窗口工具类系列文章&#xff1a; 封装窗口工具类【1】雏形 我们思考一下窗口间的关系&#xff0c;窗口创建和销毁的一些动作&#xff0c;例如父子窗口&#xff0c;窗口组合等等&#xff0c;还有…

[HackMyVM]靶场 Adria

kali:192.168.56.104 主机发现 arp-scan -l 靶机:192.168.56.108 端口扫描 nmap -p- 192.168.56.108 开启了 22 80 139 445端口 进入web 编辑 /etc/hosts&#xff0c;把192.168.56.108 adria.hmv添加进去重新访问 里面没什么有用的东西&#xff0c;注册需要邮箱&#xff0c;…

LLM+RAG: 关于知识问答优化的思考总结

本文分享实践中对LLMRAG实现知识问答系统的相关调研和思考。 LLM的知识库问答有3种实现路径&#xff1a;RAG 或 微调&#xff0c;或两者结合。而RAG和微调都各有利弊&#xff0c;比如说&#xff1a; - RAG&#xff1a; 低成本易部署&#xff0c;适用于知识会更新的场景&#…