探索Linux中的进程控制:从启动到退出的背后原理

ops/2025/1/24 12:38:18/

个人主页:chian-ocean

文章专栏-Linux

前言:

进程控制是操作系统对进程的创建、运行、调度、中止等活动进行管理和协调的行为。它是操作系统中至关重要的一部分,保证多任务处理环境下的资源分配和系统稳定性。

在这里插入图片描述

进程创建

fork( )

  • fork() 调用后:

    • 子进程是父进程的副本,拥有相同的代码和数据。
    • 子进程的所有资源(如内存、打开的文件描述符)最初与父进程共享,但在写入数据时会触发**写时复制(Copy-on-Write,COW)**机制。

    返回值:

    • **在父进程中:**返回子进程的 PID(进程 ID)。
    • **在子进程中:**返回 0
    • **如果失败:**返回 -1 并设置错误码(通常是因为系统资源不足)
int main()
{pid_t id = fork();if(id > 0){while(1){cout << "父进程"<< "pid :"<<getpid() <<endl; sleep(2);}      }else if(id == 0){while(1){   cout << "子进程"<<endl;sleep(2);                     }}else {perror("fork");cout << "子进程"<< "pid :"<<getpid() <<endl;sleep(2);}}else {perror("fork");}return 0;
}

执行代码

在这里插入图片描述

进程退出(终止)

进程终止是操作系统中一个进程完成任务或由于某种原因被终止运行的过程。在进程终止后,操作系统会释放该进程占用的资源,并将其从系统中的进程表中移除

退出码

退出码是进程在退出时返回给操作系统或父进程的一个状态值,表示进程的退出状态。退出码常用于判断进程是否成功完成任务或发生了错误

成功退出:

  • 退出码为 0,表示进程正常结束,没有错误。
  • 这也就是为什么默认return 0

失败退出:

  • 0 值通常表示进程发生了错误或异常。
  • 不同的退出码可以表示不同的错误原因(由程序员定义)。

检查退出码

#终端检查
echo $?

demo

  • 在这里面设置退出码为 8 ,以便观察。
#include <iostream>    
#include <unistd.h>    
#include <stdlib.h>    using namespace std;    int main()    
{    int i = 10;    while(i--)    {    cout << i <<" ";    }    cout << endl;    return 8;    
}

执行

在这里插入图片描述

进程退出的方式

  • **正常退出:**主函数执行到最后,正常返回。
  • **异常退出:**发生异常(如段错误、非法访问内存、除以零)导致进程被操作系统终止。

正常退出

主函数返回:

  • 主函数(main())返回值即为进程的退出码。

调用 exit() 函数:

  • 显式终止进程,返回退出码。
  • 执行清理操作(如刷新缓冲区、关闭文件等)。
#include <iostream>      
#include <unistd.h>      
#include <stdlib.h>      using namespace std;      int func()      
{               cout << "调用func"<<endl;      exit(1);      return 1;      
}               int main()      
{                                                                                                                                   int funcret = func();      cout << funcret << endl;      return 0;                     
} 

代码功能说明

  1. func 函数
    • 输出一条消息:调用func
    • 调用 exit(1) 直接终止程序,返回退出码 1
    • 后面的 return 1 永远不会被执行,因为 exit(1) 会导致程序立即退出。
  2. main 函数
    • 调用 func()
    • 由于 func() 中调用了 exit(1),程序会立即退出,func 返回值永远不会被赋给 funcret
    • main 中的 cout << funcret << endl;return 0; 永远不会被执行。

在这里插入图片描述

_exit和exit

**_exit()不会执行任何用户态的清理工作,直接终止进程,不会:

  1. 调用 atexit() 注册的清理函数。
  2. 刷新标准 I/O 流。
  3. 关闭打开的文件缓冲区。
#include <iostream>
#include <unistd.h>
using namespace std;
int main()    
{    cout << "This is exit"<< endl;    cout << "This will not be flushed.";  // 没有换行符,缓冲区未刷新    exit(0);  // 直接终止程序,刷新缓冲区  cout << "This is _exit"<< endl;    cout << "This will not be flushed.";  // 没有换行符,缓冲区未刷新    _exit(0);  // 直接终止程序,不刷新缓冲区                                                    return 0;    
}

cout << "This is exit << endl;":

  • 输出 "This is exit" 并刷新缓冲区。
  • 输出立即显示在屏幕上。

cout << "This will not be flushed.";:

  • 输出内容存储在缓冲区中,但未刷新(没有换行符)。

_exit(0);:

  • 程序立即终止,不刷新缓冲区。
  • "This will not be flushed." 不会被输出,因为缓冲区未刷新。

在这里插入图片描述

returnexit()_exit() 总结对比表

特性/行为returnexit()_exit()
作用范围退出当前函数,返回控制权给调用者终止整个程序,返回控制权给操作系统立即终止当前进程,不返回控制权
适用范围函数正常结束,返回值给调用者程序正常或错误退出,需要完成清理工作子进程退出或紧急情况下立即终止程序
I/O 缓冲区刷新自动刷新(如果退出程序)刷新所有标准 I/O 缓冲区不刷新标准 I/O 缓冲区
atexit() 注册函数不调用调用所有通过 atexit() 注册的清理函数不调用
局部变量析构调用局部变量的析构函数(C++ 对象)不调用析构函数不调用析构函数
性能性能高,适用于正常函数返回较慢,涉及缓冲区刷新和清理操作性能最高,直接调用系统内核
适用场景函数正常结束,逻辑返回值给调用者程序终止,需要完成缓冲区刷新和清理工作子进程退出或需要立即终止进程时
调用位置仅在当前函数中有效可在程序任意位置调用可在程序任意位置调用

异常退出

异常终止的表现

非零退出码:表示异常终止的原因。

#include<iostream>
using namespace std;
int main()
{int a = 10, b = 0;int c = a / b;  // 除以零导致异常终止cout << c << endl;
}

执行代码

在这里插入图片描述

常见退出码:

退出码原因
139段错误(SIGSEGV)。
136浮点异常(SIGFPE)。
1一般错误。
6调用 abort()SIGABRT)。
9强制终止(SIGKILL
# 查看错误信号
kill -l

在这里插入图片描述

错误码

打印错误码

#include <iostream>    
#include <cerrno>    
#include <cstring>        
using namespace std;        
int main()    
{    for(int i = 0; i < 135; i++)    {    const char * errmessage = strerror(i);    if(errmessage)    {    cout<< "code error" <<" "<< i <<":"<< errmessage <<endl;     }    }    return 0;       
}

执行代码

在这里插入图片描述

  • 可以观察到默认0编码是success
  • 这里面只显示一部分
  1. 标准错误码:
    • 通常范围是 1 到 134,其中每个错误码都由 POSIX 标准定义,常见于 errno.h
  2. 系统特定错误码:
    • 某些错误码可能特定于操作系统,错误描述可能不同。

http://www.ppmy.cn/ops/152740.html

相关文章

Linux 网络:cBPF 简介

文章目录 1. 前言2. 什么是 cBPF&#xff1f;2.1 cBPF 工作原理2.2 cBPF 的使用2.3 实现细节2.3.1 挂接 BPF 指令到 sokcet2.3.2 执行 BPF 指令过滤数据包 3. cBPF 示例&#xff1a;过滤 EDSA 协议下的 PTP 以太网帧4. 参考资料 1. 前言 限于作者能力水平&#xff0c;本文可能…

kotlin的协程的基础概念

Kotlin的协程是一种用于简化异步编程的强大工具。 理解协程的基础概念可以帮助开发者有效地利用其能力。 以下是Kotlin协程的一些关键基础概念&#xff1a; 协程&#xff08;Coroutines&#xff09; &#xff1a; 协程是一种用于处理并发任务的编程模型&#xff0c;它可以在单…

使用qwen作为基座训练分类大模型

训练大模型 import torch from transformers import AutoModelForSequenceClassification, AutoTokenizer, Trainer, TrainingArguments from datasets import load_dataset, DatasetDict# 1. 加载 Qwen2.5-0.5B 预训练模型和分词器 model_name "Qwen/Qwen2.5-0.5B"…

理解深度学习pytorch框架中的线性层

文章目录 1. 数学角度&#xff1a; y W x b \displaystyle y W\,x b yWxb示例 2. 编程实现角度&#xff1a; y x W T b \displaystyle y x\,W^T b yxWTb3. 常见错误与易混点解析4. 小结参考链接 在神经网络或机器学习的线性层&#xff08;Linear Layer / Fully Connect…

doris:阿里云 OSS 导入数据

Doris 提供两种方式从阿里云 OSS 导入文件&#xff1a; 使用 S3 Load 将阿里云 OSS 文件导入到 Doris 中&#xff0c;这是一个异步的导入方式。使用 TVF 将阿里云 OSS 文件导入到 Doris 中&#xff0c;这是一个同步的导入方式。 使用 S3 Load 导入​ 使用 S3 Load 导入对象存…

网络安全 | 0day漏洞介绍

关注&#xff1a;CodingTechWork 引言 在网络安全领域&#xff0c;0day漏洞&#xff08;Zero-day Vulnerability&#xff09;是指一个尚未被厂商、开发者或安全人员发现、修复或发布修补程序的安全漏洞。0day漏洞是黑客利用的一个重要攻击工具&#xff0c;因其未被披露或未被修…

Geek Uninstaller,绿色免安装轻量的应用卸载工具!

软件介绍 链接 一个轻量级拥有简洁交互界面、快速卸载电脑安装程序的工具。可快速扫描删除残余文件和注册表&#xff0c;对顽固和损坏的程序可执行强制删除、独立页面管理卸载系统Microsoft Store应用、快速打开程序安装文件夹、快速打开编辑程序注册表位置、将安装程序列表导…

Web入门

Spring 官网:spring.io Spring发展到今天已经形成了一种开发生态圈&#xff0c;Spring提供了若干个子项目&#xff0c;每个项目用于完成特定的功能 Spring Boot 可以帮助我们非常快速的构建应用程序、简化开发、提高效率 SpringBootWeb入门 ①.创建springboot工程&#xff0…