【Linux进程控制】进程创建|终止

news2024/11/24 3:18:40

目录

一、进程创建

fork函数

写时拷贝

二、进程终止

想明白:终止是在做什么?

进程退出场景

常见信号码及其含义

进程退出的常见方法

正常终止与异常终止

exit与_exit的区别


一、进程创建

fork函数

在Linux中fork函数是非常重要的函数,它从已存在进程中创建一个新进程。新进程为子进程,原进程为父进程,其中返回值:子进程中返回0,父进程返回子进程id,出错返回-1;

测试 

#include <stdio.h>
#include <assert.h>
#include <unistd.h>
#include <sys/types.h> 
int main()
{
        printf("before fork, pid = %d\n", getpid());
        pid_t id = fork();
        assert(id != -1);//进程创建失败
        (void)id;

        printf("after fork, pid = %d, fork return %d\n", getpid(), id);
        return 0;
}

上面代码执行路径如下图所示

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

❍ 分配新的内存块和内核数据结构给子进程

❍ 将父进程部分数据结构内容拷贝至子进程

❍ 添加子进程到系统进程列表中

❍ fork返回,开始调度器调度

当父进程调用 fork() 时,会发生以下几件事情:

1.进程复制:操作系统会创建父进程的一个副本,这个副本就是子进程。子进程几乎与父进程完全相同,它们拥有相同的程序文本、数据段、堆栈、文件描述符等。

2.资源共享与复制:尽管子进程是父进程的一个副本,但是它们之间还是有所区别的,例如,它们有不同的进程ID(PID)、不同的父进程ID(PPID)以及一些独立的资源,如虚拟内存等。

3.执行流程fork() 调用之后,父进程和子进程都会从 fork() 函数调用后的下一条指令开始执行。

4.返回值fork() 在父进程中返回子进程的 PID,在子进程中返回 0,如果出错则返回 -1。

fork常规用法:

  • 一个父进程希望复制自己,使父子进程同时执行不同的代码段。例如,父进程等待客户端请求,生成子进程来处理请求

  • 一个进程要执行一个不同的程序。例如子进程从fork返回后,调用exec函数

fork调用失败的原因

  • 系统中有太多的进程

  • 实际用户的进程数超过了限制

写时拷贝

写时拷贝(Copy-on-Write,简称COW)是一种计算机程序设计的优化策略。这种策略在多个进程试图写入同一块数据时,才会真正进行数据复制,而不是一开始就为每个进程分配独立的物理内存空间。

工作原理:

1.共享数据:当父进程通过 fork() 创建子进程时,并不立即为子进程分配一份父进程数据段的副本。相反,父子进程共享同一块物理内存页。

2.写操作检测:操作系统会标记这些共享的内存页为“写时拷贝”。这意味着如果任何一个进程试图写入这些页,操作系统会捕捉到这个写操作。

3.数据复制:当写操作发生时,操作系统会触发页错误(page fault)。操作系统随后会创建一个新的内存页,并将原页的内容复制到新页上,然后将写操作指向新的内存页。对于其他进程,原内存页保持不变。

4.页分离:这个过程称为页分离(page splitting)。之后,每个进程都会有自己的内存页副本,对其中一个进程的修改不会影响到另一个进程。

优点:

  • 效率提升:在 fork() 调用后,不需要立即复制父进程的所有资源,减少了不必要的内存消耗和复制时间。

  • 内存使用优化:只有在实际需要时才分配内存,这可以显著减少内存的使用。

  • 性能提升:减少了进程创建时的开销,提高了系统的整体性能。

缺点:

  • 写操作开销:第一次写操作时会有额外的开销,因为需要复制内存页。

  • 复杂性:实现写时拷贝会增加操作系统内核的复杂性。


 

二、进程终止

想明白:终止是在做什么?

操作系统要释放进程申请的相关内核数据结构和对应的数据和代码(本质就是释放系统资源)。

进程退出场景
  • 代码执行完毕,结果正确

#include <stdio.h>
int Add(int from, int to)
{
	int sum = 0;
	for(int i = from; i <= to; i++)
	{
		sum += i;
	}
	return sum;
}
int main()
{
	printf("Add 1 to 100 is %d\n", Add(0, 100));
	return 0;
}
[wuxu@Nanyi lesson16]$ gcc -o test test1.c -std=c99
[wuxu@Nanyi lesson16]$ ./test
Add 1 to 100 is 5050
  • 代码运行完毕,结果不正确

#include <stdio.h>
int Add(int from, int to)
{
        int sum = 0;
  //此处应该是 i<=to
        for(int i = from; i < to; i++)
        {
                sum += i;
        }
        return sum;
}

int main()
{
        printf("Add 1 to 100 is %d\n", Add(0, 100));
        return 0;
}
[wuxu@Nanyi lesson16]$ gcc -o test test1.c -std=c99
[wuxu@Nanyi lesson16]$ ./test
Add 1 to 100 is 4950
  • 代码异常终止。即代码没有跑完,程序崩溃

#include <stdio.h>

int main()
{
        int* p = NULL;
        *p = 100;//空指针解引用--->野指针
        return 0;
}
[wuxu@Nanyi lesson16]$ gcc -o test test2.c -std=c99
[wuxu@Nanyi lesson16]$ ./test
Segmentation fault
  • 在程序执行结束时,我们会使用return语句返回一个数值作为main函数的返回值,这个返回值有什么用呢?

【例子1】张三参加一场考试,回家后给老爹汇报成绩

如果小明考了100分(满分100)那么他的老爹并不会关心他为什么考了100分;但当小明考了1分,他的老爹则会问他为什么考1分。因此做出如下约定,每个数字标识不同的原因:

状态码描述
1考试过程中生病了导致没考好
2没有好好学习导致没考好
......

在操作系统中,对于程序正常终止我们并不关心(正常程序终止返回状态码0),但程序一旦出现错误(返回码非0),我们就需要知道程序出错的原因。操作系统对于不同的状态码给了不同的错误描述信息,我们可以使用errno.h 下的 errno 变量获取错误码,使用 strerror(errno)获取错误码的错误描述

#include <stdio.h>
#include <string.h>

int main()
{
	for(int i = 0; i < 200; i++)
	{
		printf("[%d]->%s\n", i, strerror(i));
	}
	return 0;
}

  • 谁会关心当前进程的退出码呢?-->父进程

父进程为何关心子进程的退出码?

错误处理:如果子进程因为错误而终止,它通常会返回一个非零的退出码。父进程可以根据这个退出码来决定是否需要采取补救措施,比如重新执行失败的子进程,或者记录错误信息。

流程控制:在某些情况下,父进程的后续行为可能依赖于子进程的成功执行。如果子进程返回一个表示成功的退出码(通常是0),父进程可以继续执行下一步操作;否则,它可能会停止执行或执行不同的代码路径。

状态报告:父进程可能需要向用户或其他进程报告子进程的执行结果。退出码是传递这种状态信息的简单方式。

在Linux中,可以使用echo $?来查看最近一次执行的子进程的退出码

我们在回到刚刚野指针的例子上,重新执行一下程序:

[wuxu@Nanyi lesson16]$ gcc -o test test2.c -std=c99
[wuxu@Nanyi lesson16]$ ./test
Segmentation fault
[wuxu@Nanyi lesson16]$ echo $?
127
[wuxu@Nanyi lesson16]$ echo $?
0

通过观察我们发现同一个程序,为什么两次的退出码不一样?

其实第一个127是./test的执行码,表示这个程序出现了Segmentation fault错误,第二个执行码表示echo这个命令执行成功,返回0

一旦程序出现异常,退出码就没有意义了

为什么出现了异常?--> 我们可以看进程退出的时候,退出信号是多少,就可以判断进程为什么异常了。

进程出异常本质是因为进程收到了OS发给进程的信号

【示例】我们写一个除0的程序,看会出现什么错误

#include <stdio.h>

int main()
{
	int a = 1 / 0;
	return 0;
}
[wuxu@Nanyi lesson16]$ ./error
Floating point exception

在该程序发生错误时,操作系统给该程序的进程发送了8号信号SIGFPE。我们可以通过 kill -l 查看所有的信号码以及对应信号名

我们来验证一下,上面的程序时接收到8号信号才终止的

#include <stdio.h>

int main()
{
	while(1)
	{}
	return 0;
}

 

常见信号码及其含义
信号码信号名称含义
1SIGHUP挂起,通常在终端关闭或控制进程结束时发送给子进程。
2SIGINT中断,通常在用户按下Ctrl+C时发送。
3SIGQUIT退出,用户按下Ctrl+\时发送,通常会导致进程终止并生成核心转储。
4SIGILL非法指令,执行了非法的机器语言指令。
5SIGTRAP跟踪陷阱,由调试器使用。
6SIGABRT中止,调用abort()函数时发送。
7SIGBUS总线错误,涉及硬件错误。
8SIGFPE浮点异常,如除以零。
9SIGKILL杀死,无法捕获、阻塞或忽略,总是终止进程。
10SIGUSR1用户定义的信号1,可用于应用程序。
11SIGSEGV段违例,访问非法内存地址。
12SIGUSR2用户定义的信号2,可用于应用程序。
13SIGPIPE管道破裂,写入无读者的管道时发生。
14SIGALRM报警,由alarm()函数设置的时间到期时发送。
15SIGTERM终止,请求进程终止。
信号码信号名称含义
16SIGSTKFLT栈溢出(Linux特有,在一些系统中不存在)
17SIGCHLD子进程结束,子进程处于停止状态或被终止时发送给父进程。
18SIGCONT继续执行,如果进程已停止,则使其继续运行。
19SIGSTOP停止进程的执行,无法被捕获或忽略。
20SIGTSTP停止进程的执行,可以被捕获,通常在用户按下Ctrl+Z时发送。
21SIGTTIN后台进程组尝试读取控制终端时发送。
22SIGTTOU后台进程组尝试写入控制终端时发送。
23SIGURGI/O紧急情况,套接字有紧急数据可读。
24SIGXCPU超过CPU时间限制(CPU时间限制超时)。
25SIGXFSZ超过文件大小限制。
26SIGVTALRM虚拟定时器警报(类似于SIGALRM,但是计算的是进程的虚拟时间)。
27SIGPROF性能计数器超时(类似于SIGALRM,但是包括了处理器时间和时钟时间)。
28SIGWINCH窗口大小改变,通常在终端窗口大小改变时发送。
29SIGIOI/O可执行(Solaris系统中为SIGPOLL)。
30SIGPWR电源故障(系统关机)。
31SIGSYS系统调用异常(无效的系统调用)。

进程退出的常见方法

正常终止与异常终止

正常终止(可以通过 echo $? 查看进程退出码)

  • 从main函数返回

  • 调用exit

  • _exit

异常终止

  • ctrl + c 信号终止

exit与_exit的区别

  • 终止处理程序和I/O缓冲区exit()会执行终止处理程序和I/O缓冲区的清理,而_exit()则不会。

  • 头文件exit()stdlib.h中定义,而_exit()unistd.h中定义。

  • 用途:由于_exit()不会进行清理工作,它通常用于不需要这些清理步骤的底层系统编程。

【例子】

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

int main()
{
        printf("1 + 1 = %d", 1 + 1);
        _exit(1);
        return 0;
}
[wuxu@Nanyi lesson16]$ vim test5.c
[wuxu@Nanyi lesson16]$ gcc -o test test5.c -std=c99
[wuxu@Nanyi lesson16]$ ./test
[wuxu@Nanyi lesson16]$ echo $?
1

通过结果我们发现,并没有打印1+1=2这个结果,也就是_exit不会刷新缓冲区,故最后并没有打印。

如果换成exit

[wuxu@Nanyi lesson16]$ gcc -o test test5.c -std=c99
[wuxu@Nanyi lesson16]$ ./test
1 + 1 = 2 [wuxu@Nanyi lesson16]$ echo $?
1

我们会发现它打印出最终结果,顺便提醒一下 exit与_exit 头文件不一样哦

exit最后也会调用_exit,但在exit除了调用_exit,还做了其他工作:

❍ 执行用户通过atexit或on_exit定义的清理函数 ​

❍ 关闭所有打开的流,所有的缓存数据均被写入(即刷新缓冲区)

​ ❍ 再调用_exit

return退出

return是一种更常见的退出进程方法。执行return n 等同于执行exit(n),因为调用main的运行时函数会将main的返回值当作exit的参数

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

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

相关文章

魔方财务升级指南

本文将详细介绍如何升级魔方财务系统&#xff0c;确保您能够顺利地更新到最新版本。 重要提示 在进行任何系统升级之前&#xff0c;请务必备份数据。这是良好的习惯&#xff0c;也是我们的建议。 备份数据库&#xff0c;并下载到本地。最好将网站目录文件打包&#xff0c;并…

【最新华为OD机试E卷-支持在线评测】最长连续子序列(100分)多语言题解-(Python/C/JavaScript/Java/Cpp)

🍭 大家好这里是春秋招笔试突围 ,一枚热爱算法的程序员 💻 ACM金牌🏅️团队 | 大厂实习经历 | 多年算法竞赛经历 ✨ 本系列打算持续跟新华为OD-E/D卷的多语言AC题解 🧩 大部分包含 Python / C / Javascript / Java / Cpp 多语言代码 👏 感谢大家的订阅➕ 和 喜欢�…

沉浸式利用自然语言无代码开发工具生成式AI产品应用(上)

背景 小伙伴们过去在开发应用时&#xff0c;经常需要编写大量代码文件以实现业务逻辑&#xff0c;想必肯定有小伙伴开发过类似于快消行业索赔处理、订单库存跟踪和项目审批等系统。去解决这些业务实际问题&#xff0c;我们需要定制地开发业务应用程序为这些问题提供解决方案。…

S100A9:鸡支原体感染中的免疫调控“双面间谍”【AbMole】

在生物学研究的广阔天地里&#xff0c;总有一些分子扮演着令人意想不到的角色。今天&#xff0c;我们要探索的主角是S100A9蛋白&#xff0c;一种在鸡支原体感染过程中展现出惊人双重功能的分子。这项来自华中农业大学动物科技学院与兽医学院的最新研究成果&#xff0c;揭示了S1…

(一)springboot2.6.13+mybatis-plus3.5.3.1+shardingsphere4.0.0-RC2

1,POM配置 <?xml version"1.0" encoding"UTF-8"?> <project xmlns"http://maven.apache.org/POM/4.0.0" xmlns:xsi"http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation"http://maven.apache.org/POM/4.0…

【Mac】系统环境配置

常用工具 Navicat PJ版本&#xff1a;this Host切换器 SwitchHosts termius 一款好用的Linux服务器连接工具&#xff1a; termius 小飞机 dddd&#xff1a;&#x1fa9c; Git mac安装git有好多种方式&#xff0c;自带的xcode或者通过Homebrew来安装&#xff0c;本文的…

文心一言 VS 讯飞星火 VS chatgpt (348)-- 算法导论23.2 7题

七、假定图G的一棵最小生成树已经被计算出来。如果在图中加入一个新结点及其相关的新边&#xff0c;我们需要多少时间来对最小生成树进行更新&#xff1f;如果要写代码&#xff0c;请用go语言。 文心一言&#xff1a; 在图中加入一个新节点及其相关边后&#xff0c;要更新最小…

【计算机毕设-大数据方向】基于Hadoop的在线教育平台数据分析可视化系统的设计与实现

&#x1f497;博主介绍&#xff1a;✌全平台粉丝5W,高级大厂开发程序员&#x1f603;&#xff0c;博客之星、掘金/知乎/华为云/阿里云等平台优质作者。 【源码获取】关注并且私信我 【联系方式】&#x1f447;&#x1f447;&#x1f447;最下边&#x1f447;&#x1f447;&…

QGis二次开发 —— 3、程序加载栅格tif与矢量shp文件可进行切换控制,可进行导出/导入工程(附源码)

效果 功能说明 软件可同时加载.tif栅格图片与.shp矢量图片、加载图片后可进行自由切换查看图层、可对加载的图片进行关闭 关闭后清空图层、可对加载的图片进行导出.qgs的QGIS工程、可对.qgs的QGis工程导入并导入后可进行自由切换查看图层。 源码 注意: 在加载tif栅格文件后会在…

类和对象补充

const 成员函数 const修饰的成员函数称之为const成员函数&#xff0c;const修饰成员函数放到成员函数参数列表的后面。 void Print() const { cout << _year << "-" << _month << "-" << _day << endl; } 为什么要…

SpringDoc介绍

一、SpringDoc 官方文档 1.1何为SpringDoc SpringDoc是一个用来自动生成API文档的库。它是基于SpringBoot项目的&#xff0c;遵循OpenAPI3(一个组织规定的规范)规范。它是通过检查我们运行中的程序&#xff0c;推断出基于Spring配置、类结构和各种注解的API语义&#xff0c;从…

介绍一些免费 的 html 5模版网站 和配色 网站

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 前言一、H5 网站介绍网站 二、配色网站个人推荐 前言 提示&#xff1a;以下是本篇文章正文内容&#xff0c;下面案例可供参考 一、H5 网站介绍 以下是一些提供免费…

系统优化工具 | TweakPower v2.0.6 绿色版

TweakPower 是一款专业的系统优化工具&#xff0c;它为Windows用户提供了全面的系统性能提升解决方案。这款软件通过精细调整系统设置&#xff0c;包括注册表项&#xff0c;来显著提高电脑的运行速度和整体性能。 全面优化&#xff1a;提供内存管理、垃圾清理、数据备份、硬盘…

从 Prompt 工程看职场PUA!明明是模型缺陷,为啥要 Prompt 雕花?

简介 小伙伴们好&#xff0c;我是微信公众号《小窗幽记机器学习》的小编&#xff1a;卖豆沙饼的小女孩。在当今的人工智能领域&#xff0c;Prompt 工程已被视为一项重要技能。然而&#xff0c;这一趋势也揭示了当前模型的不足&#xff1a;一个强大的模型应当能够直接理解用户明…

20240916 每日AI必读资讯

超强o1模型智商已超120&#xff01;1小时写出NASA博士1年代码&#xff0c;最新编程赛超越99.8%选手 - 一位UCI物理学博士实测o1&#xff0c;发现自己用时1年完成的博士论文代码&#xff0c;竟被AI在1个小时之内实现了。 - o1在最新门萨智商测试中&#xff0c;IQ水平竟超过了1…

Fish Speech - 新的 TTS 解决方案

文章目录 一、关于 Fish Speech架构Fish Speech V1.1 技术介绍[视频] 二、配置1、Linux 配置2、Docker 配置 三、快速开始1、设置For Windows User / win用户For Linux User / Linux 用户 2、准备模型3、WebUI Inference 四、Break-down CLI Inference1、从语音生成 prompt:2、…

鸿蒙开发之ArkTS 基础四 函数

函数 function 是可以重复使用的代码块 提高开发效率 &#xff0c;例如下面代码: 我需要执行无变&#xff0c;最方便的就是封装到一个方法里面 然后调用五次 fn()方法即可 如图&#xff1a; 函数的步骤是先定义函数 然后使用函数 定义的语法格式是: function 函数名字(){ 函…

Linux命令:文本处理工具sed详解

目录 一、概述 二、用法 1、基本语法 2、常用选项 3、命令格式 4、编辑命令 5、获取帮助 三、 示例 1、替换字符串 2、删除行 &#xff08;1&#xff09;删除包含"string"的所有行 ​编辑 &#xff08;2&#xff09;删除从第1行到第10行的所有行 3、插…

Java 入门指南:JVM(Java虚拟机)垃圾回收机制 —— 死亡对象判断方法

文章目录 垃圾回收机制死亡对象判断方法引用计数法可达性分析算法可以作为 GC Roots 的对象判断对象被回收需要经历的过程 引用类型引用汇总引用队列 废弃常量判定废弃常量废弃原因遵循原则 无用的类所需条件造成的问题解决步骤 垃圾回收机制 垃圾回收&#xff08;Garbage Col…

Anaconda安装并配置Python环境

背景概述 Anaconda&#xff0c;中文大蟒蛇&#xff0c;是一个开源的Anaconda是专注于数据分析的Python发行版本&#xff0c;包含了conda、Python等190多个科学包及其依赖项。 Anaconda就是可以便捷获取包且对包能够进行管理&#xff0c;包括了python和很多常见的软件库和一个…