Bootstrap

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

个人主页: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. 系统特定错误码:
    • 某些错误码可能特定于操作系统,错误描述可能不同。
;