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

devtools/2025/1/24 14:13:57/

个人主页: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/devtools/153152.html

相关文章

SQL 递归 ---- WITH RECURSIVE 的用法

SQL 递归 ---- WITH RECURSIVE 的用法 开发中遇到了一个需求&#xff0c;传递一个父类id&#xff0c;获取父类的信息&#xff0c;同时获取其所有子类的信息。 首先想到的是通过程序中去递归查&#xff0c;但这种方法着实孬了一点&#xff0c;于是想&#xff0c;sql能不能递归查…

DC-DC稳压电源——实战(基于Ti5450芯片)基础知识篇(1)

一&#xff1a;基础知识-耦合 1&#xff09;去耦电容 &#xff08;1&#xff09;耦合与去耦 耦合&#xff1a;系统内部的各个部分之间存在相互依赖、相互影响、相互制约的情况。用人话说就是不同部分之间的相互影响。 去耦&#xff1a;自然就是消除不同部分之间的影响了。 &…

【玩转全栈】----基于ModelForm完成用户管理页面

目录 大致效果 添加用户代码 引入ModelForm ModelForm 与一般表单的区别&#xff1a; ModelForm 与传统 Form 的区别&#xff1a; 使用ModelForm制作用户管理 新建用户 编辑用户&#xff1a; 删除数据 完整代码 在学完前面的部门管理案例后&#xff0c;自己独立写出个用户管理应…

MongoDB中的横向扩容数据分片

MongoDB中的分片启用及应用 分片&#xff08;Sharding&#xff09;是MongoDB为解决大规模数据集存储和高并发访问设计的一种分布式存储机制。通过分片&#xff0c;数据可以水平拆分并分布在多个服务器&#xff08;物理或虚拟&#xff09;上&#xff0c;以提升性能和容量。 1. …

基于LangGraph、Groq和Tavily打造可以调用外部搜索引擎工具的对话机器人(核心代码 万字详解)

一、python环境 & 相关库版本信息 代码运行在 conda 创建的python环境下&#xff0c;python和相关库的版本信息如下&#xff1a; $ python --version Python 3.12.3$ pip list | grep langchain langchain 0.3.15 langchain-community 0.3.15 lang…

高效建站指南:通过Portainer快速搭建自己的在线网站

文章目录 前言1. 安装Portainer1.1 访问Portainer Web界面 2. 使用Portainer创建Nginx容器3. 将Web静态站点实现公网访问4. 配置Web站点公网访问地址4.1公网访问Web站点 5. 固定Web静态站点公网地址6. 固定公网地址访问Web静态站点 前言 Portainer是一个开源的Docker轻量级可视…

【玩转全栈】----Django模板的继承

先赞后看&#xff0c;养成习惯&#xff01;&#xff01;&#xff01; 目录 模板继承的好处 模板继承的语法规则 更新代码 上文中的部门管理页面&#xff1a; 【玩转全栈】----Django制作部门管理页面-CSDN博客 大家会发现&#xff0c;由于定义了多个html文件&#xff0c;多个ht…

Selenium配合Cookies实现网页免登录

文章目录 前言1 方案一&#xff1a;使用Chrome用户数据目录2 方案二&#xff1a;手动获取并保存Cookies&#xff0c;后续使用保存的Cookies3 注意事项 前言 在进行使用Selenium进行爬虫、网页自动化操作时&#xff0c;登录往往是一个必须解决的问题&#xff0c;但是Selenium每次…