传奇手游风暴活动专区

  • 首页
  • 跨服动态
  • 行会战报
  • 装备图鉴
  • 2025-11-16 04:52:05

    【Linux探索学习】第十七弹——进程终止:深入解析操作系统中的进程终止机制

    Linux学习笔记:

    https://blog.csdn.net/2301_80220607/category_12805278.html?spm=1001.2014.3001.5482

    前言:

    在操作系统中,进程终止是一个至关重要的阶段,它标志着进程的生命周期结束。进程终止可能是因为任务完成,也可能是因为异常或外部干预。本文将详细讲解操作系统中的进程终止相关知识,包括终止的原因、类型、实现方式、Linux系统中的具体操作,以及其影响和管理策略,并配以表格和代码示例,帮助全面掌握这一主题。

    一、什么是进程终止?进程终止(Process Termination)是操作系统中进程生命周期的最后一个阶段,意味着操作系统回收该进程的所有资源,包括内存、文件描述符、CPU时间等,使这些资源可以被其他进程使用。

    二、进程终止的主要原因进程可能因多种原因终止:

    终止原因

    描述

    正常终止

    进程完成所有任务后自然结束,例如程序执行到return语句或调用exit函数。

    异常终止

    由于未处理的错误或异常导致进程终止,例如除以零、非法访问内存等。

    外部干预

    进程被操作系统或其他进程强制终止,例如接收到SIGKILL信号。

    父进程终止

    当父进程终止且子进程未被接管时,子进程可能成为孤儿进程,由init或systemd进程接管。

    资源耗尽

    进程因超出系统资源限制(如内存、文件句柄等)被操作系统强制终止。

    三、进程终止的类型进程终止根据触发方式可以分为以下几类:

    类型

    触发方式

    常见场景

    正常终止

    调用exit()、返回主函数

    程序完成任务后自然结束。

    异常终止

    未处理的错误或调用abort()

    例如访问非法地址、未处理的信号等。

    强制终止

    外部进程调用kill()、操作系统干预

    父进程发送SIGKILL信号或管理员手动终止进程。

    核心转储终止

    错误导致生成核心转储文件

    例如段错误(SIGSEGV)导致的异常。

    一般进程终止的场景包含一下三种:

    1. 代码运行完毕,结果正常

    2. 代码运行完毕,结果不正常

    3. 代码异常终止

    下面我们会对上面的内容做出讲解

    四、Linux中的进程终止实现在Linux操作系统中,进程终止主要通过以下系统调用和信号实现:

    4.1 运行完毕且正常终止4.1.1 使用return终止进程我们平时用的最多的方式就是return,我们先来看下面一个简单的代码

    代码语言:javascript复制 #include

    int main()

    {

    printf("This is a test message");

    return 0;

    } 我们平时所写的代码main函数中一般都有一个返回值,那么这个返回值是干什么的呢?

    main函数返回值是返回给进程看的,本质表示:进程运行完成时是否是正确的结果,如果是一般返回0,如果不是返回其它数字代表不同的退出信息(退出码)

    我们可以通过这个指令打印退出码:

    代码语言:javascript复制echo $?4.1.2 使用exit终止进程exit系统调用用于正常终止进程,并返回一个状态码给操作系统或父进程。

    我们使用exit一般是在进程正常终止但没有正常执行的场景,或者是在合适的地方进行截停的场景,我们来看下面一段代码:

    代码语言:javascript复制#include

    #include

    void print()

    {

    printf("hello linux\n");

    printf("hello linux\n");

    printf("hello linux\n");

    printf("hello linux\n");

    exit(20);

    printf("hello linux\n");

    printf("hello linux\n");

    }

    int main()

    {

    print();

    return 10;

    } 我们来看一下上面内容的执行结果和返回值:

    我们发现返回值是exit中的返回值,并不是return的返回值,而且打印也只执行了四行,所以我们可以知道带有exit的进程,在执行到它时会直接返回,并不会再继续执行后面的内容,返回值也返回exit的返回值,这一点与return有较大差别

    4.2 errno常量和strerror函数4.2.1 strerror函数上面我们提到我们可以通过不同的退出码来代表不同的错误信息,那么不同的退出码究竟各自代表什么信息呢?我们可以通过strerror函数来查看

    比如我们来看一下退出码0到10所代表的信息:

    代码语言:javascript复制#include

    #include

    int main()

    {

    for(int i=0;i<=10;i++)

    {

    printf("%d: %s\n",i,strerror(i));

    }

    return 0;

    } 运行结果:

    4.2.2 errno常量上面我们讲到进程在退出是会有退出码,我们可以通过echo来查看退出码,那我们如何获取呢?

    C/C++中其实还定义了一个叫errno的常量来记录错误码

    所以我们就可以将errno常量与strerror函数结合使用,用errno来记录进程的错误码,然后传给strerror函数得到错误信息,比如下面的例子:

    代码语言:javascript复制#include

    #include

    #include

    #include

    #include //注意要带好头文件

    int main()

    {

    int ret=0;

    char *p=(char*)malloc(1000*1000*1000*4); //这个扩容肯定会出错的,因为扩容空间太大了

    if(p==NULL)

    {

    printf("mallo error, %d:%s\n",errno,strerror(errno)); //errno会记录错误码,将它传到strerror中就可以得到错误信息

    ret=errno; //将错误码作为返回值返回,从而让父进程得到返回信息

    }

    else

    {

    printf("malloc success\n");

    }

    return ret;

    } 运行结果:

    4.3 异常终止:abort 4.2和4.3都牵扯到了信号的内容,这里我们主要还是以了解为主,后期我们会详细讲解信号的知识

    abort函数用于非正常终止进程,通常在遇到不可恢复的错误时调用。

    示例代码:abort

    代码语言:javascript复制#include

    #include

    int main() {

    printf("遇到严重错误,程序终止!\n");

    abort(); // 异常终止

    return 0; // 不会被执行

    }调用abort会产生一个信号(SIGABRT),通常会生成一个核心转储文件供调试使用。

    4.4 强制终止:killkill系统调用或命令用于向目标进程发送信号,例如SIGKILL信号会立即强制终止目标进程。

    示例代码:kill

    代码语言:javascript复制#include

    #include

    #include

    int main() {

    pid_t pid = fork();

    if (pid == 0) {

    // 子进程

    while (1) {

    printf("子进程正在运行: PID = %d\n", getpid());

    sleep(1);

    }

    } else if (pid > 0) {

    // 父进程

    sleep(5);

    printf("终止子进程: PID = %d\n", pid);

    kill(pid, SIGKILL); // 发送SIGKILL信号

    }

    return 0;

    }执行结果:

    我们发现子进程在被执行了五次后被终止掉了,对应的就是代码中执行了五次后会执行kill指令杀死进程

    4.4 子进程资源回收:wait 和 waitpid 进程等待与回收我们会在下一篇详细讲解

    当子进程终止后,其状态会保留在系统中,直到父进程回收。这种未被回收的子进程称为僵尸进程。

    示例代码:回收子进程资源

    代码语言:javascript复制#include

    #include

    #include

    #include

    int main() {

    pid_t pid = fork();

    if (pid == 0) {

    // 子进程

    printf("子进程开始: PID = %d\n", getpid());

    sleep(2);

    printf("子进程结束\n");

    } else if (pid > 0) {

    // 父进程

    int status;

    wait(&status); // 等待子进程终止

    if (WIFEXITED(status)) {

    printf("子进程正常终止,状态码: %d\n", WEXITSTATUS(status));

    }

    }

    return 0;

    }执行结果:

    五、进程终止的影响5.1 资源释放进程终止后,操作系统会回收以下资源:

    内存:代码段、数据段、堆栈等。文件描述符:关闭该进程打开的所有文件。CPU时间:释放进程的时间片。5.2 僵尸进程当子进程终止但父进程未调用wait或waitpid回收其状态时,子进程会变成僵尸进程。

    如何避免僵尸进程? 父进程调用wait或waitpid回收子进程。使用信号处理机制,如捕获SIGCHLD信号。六、信号与进程终止常见信号与作用信号

    描述

    默认行为

    SIGKILL

    强制终止进程,无法捕获或忽略。

    终止

    SIGTERM

    请求终止进程,进程可以选择捕获或忽略。

    终止

    SIGABRT

    异常终止进程,通常由abort触发。

    终止并生成核心转储

    SIGCHLD

    子进程终止或停止时通知父进程。

    忽略

    示例代码:捕获SIGTERM信号代码语言:javascript复制#include

    #include

    #include

    void handle_sigterm(int sig) {

    printf("接收到SIGTERM信号,进程终止\n");

    exit(0);

    }

    int main() {

    signal(SIGTERM, handle_sigterm); // 注册信号处理函数

    while (1) {

    printf("程序正在运行...\n");

    sleep(2);

    }

    return 0;

    }七、进程终止的常见问题与解决7.1 僵尸进程问题问题:父进程未回收子进程,导致系统中出现僵尸进程。

    解决:

    调用wait或waitpid。使用SIGCHLD信号处理函数自动回收。7.2 非预期终止问题:进程意外终止导致数据未保存。

    解决:通过信号处理函数捕获终止信号,并在终止前完成必要的清理工作。

    八、总结 进程终止是操作系统中管理资源的重要环节。通过本文的讲解,我们了解了进程终止的主要原因、类型以及Linux中的具体实现方式。进程终止不仅影响单个进程的生命周期,还对系统资源的利用和稳定性产生重要影响。

    通过合理地使用exit、kill、wait等系统调用,以及信号机制,可以高效管理进程终止,避免僵尸进程问题,提高系统性能和可靠性。希望通过本文的详细分析和代码示例,你能更加深入理解操作系统的这一重要机制!

    本篇笔记:

    感谢各位大佬观看,创作不易,还请各位大佬点赞支持!!!

    家庭版肉夹馍🥙
    华为Mate20
    装备图鉴

    友情链接:

    ©Copyright © 2022 传奇手游风暴活动专区 All Rights Reserved.