Linux 进程终止

news2024/11/16 6:41:38

引入

在写 C 语言程序的时候,我们必写的结构就是:

int main()
{
	return 0;
}

在学习 C 语言的时候,我们好像并没有讨论过这个 return 0 有什么用,是干什么的!return 1 可以吗?return 的返回值给谁看?这样的问题!
那么今天我们就会浅浅地解决一下这些问题!

进程退出的场景

一个进程被创建出来就是用来执行特定任务的!比如你写了一个快速排序的代码,他的任务就是排序!那么当这个进程退出的时候会有以下三种情况:

  1. 代码执行完毕,结果正确。
  2. 代码执行完毕,结果不正确。
  3. 代码异常终止。

设想一下,如果你的代码执行完毕,结果正确!我们还要不要关心他为什么正确呢?就比如你考试考了满分💯,你的家长要不要问你为什么要考满分?

因此,只有当进程没有完成任务,我们才会去关心:

  • 代码执行完毕结果不正确,那么是那里不正确呢?
  • 代码异常终止了,又是哪里出了问题呢?

回到一开始的问题,main 函数中 return 的值有什么意义呢?如果 main 函数返回 0,代表代码执行完毕,结果正确(约定俗称 0 表示这个意义);如果 return 返回其他值,我们就能通过返回值的不同得到进程退出时出错的原因!我们称进程结束时,返回给操作系统的数值称为退出码

进程的退出码

退出码表征了进程运行的状态/结果,关心这个退出码的一定是创建这个进程的父进程!其实本质上关心出错原因的应该是用户!
Linux 操作系统中,有一组通用的退出码以及其大致含义:

  • 0:成功,表示程序或者命令成功执行。
  • 1:通用错误,通常表示一般性错误,没有特定的详细信息。
  • 2:误用命令,表示命令的使用方式错误。
  • 126:表示命令无法执行。
  • 127:表示系统找不到要执行的命令

如果你不想用系统的,进程的退出码可以自定义,我们可以自定义退出码的描述信息,比如你可以设置进程退出码为 1 表示什么什么错误;退出码为 2 表示什么什么错误 ······

查看进程的退出码

想要查看进程的退出码可以使用:

echo $?

该命令可以查看最近的一个进程的退出码!

return 设置退出码

在下面的代码中我们通过 return 11 将退出码设置为 11。./test 运行程序,使用 echo $? 打出了最近的一个进程的退出码,的确是 11。我们发现再次使用 echo $? 打印出来的结构就变成了 0,这是为什么呢?因为 echo 本身也是一个进程嘛,在运行 ./test 之后第二次使用 echo 这个 echo 就是最近的那个进程啦~而 echo 又是成功运行的,当然就是 0 啦!
在这里插入图片描述
return 设置错误码仅限在 main 函数中呢!因为在其他函数中 return 仅仅表示结束当前函数嘛!

exit 设置退出码

exit 函数可以直接结束当前进程,在任何地方!参数用来设置退出码!
在这里插入图片描述
在下面的代码中,我们定义了一个函数 Div,在函数体中检测到除数如果为 0 我们直接终止进程,退出码设置为 1,如果你自定义了退出码 为 1 时为除零错误,就能更好的展示错误信息啦!

#include<stdio.h>
#include<stdlib.h>

int Div(int x, int y)
{
    if(y == 0)
        exit(1);
    return x / y;
}

int main()
{
    Div(1, 0);
    return 0;
}

我们看到退出码为 1,并不是 0 说明,exit() 的确能够终止进程并设置退出码的!
在这里插入图片描述

补充:exit VS _exit

相同点:

  • 可以在程序的任意位置终止进程。
  • 可以设置进程的退出码

不同点:

  • exit 是 C 语言的库函数(#include <stdlib.h>),_exitLinux 操作系统的系统调用函数(#include<unistd.h>)。
  • exit 函数除了终止进程,设置进程的退出码之外,还会执行用户定义的清理函数,冲刷缓冲区,关闭流等!;_exit 仅仅是终止进程,设置进程的退出码!

我们可以用下面的程序进行验证:
我们使用 printf 函数打印 “hello linux” 这个字符串,不加换行符的打印哦!然后使用 exit 函数终止进程!

#include<stdio.h>
#include<stdlib.h>

int main()
{
    printf("hello linux");
    exit(11);
    return 0;
}

我们可以看到顺利将 hello linux 这个字符串打印出来了!
在这里插入图片描述
再来看下面的程序:同样是不带换行符打印 hello linux 这次我们使用 _exit 终止进程!

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


int main()
{
    printf("hello linux");
    _exit(11);
    return 0;
}

我们看到使用 _exit 函数终止进程没有打印 hello linux
在这里插入图片描述
这是为什么呢?我们知道在 Linux 操作系统中,printf 的刷新策略是行刷新!也就是说在没有遇到换行符或者没有将缓冲区写满之前,printf 的数据都是不会刷新到显示器上的!结合 exit 会在进程终止之前刷新缓冲区!因此我们可以得出结论:缓冲区绝对不在内核中!而是在用户区
如果缓冲区是在内核中的话,_exit 也一定会把数据刷新到显示器!不然为什么要向内核中写数据呢!!!


可见:exit 函数是在刷新缓冲区,关闭流等工作完成之后调用的 _exit 系统调用。

在平时的使用中还是建议使用 exit 因为刷新缓冲区,关闭流的确是需要的!

错误码

再讲完了退出码,就不得不提错误码的概念!错误码通过全局变量 errno 来表示,errno 是一个整数,用于最近一次系统调用或者库函数导致的错误,这个错误提供了一种标准化的方式来处理和传递错误信息!
我们也不知道系统提供了多少错误码,我们可以打印错误码,以及他的描述信息来看看!查看错误码对应描述信息可以使用 strerror 函数哈!

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

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

我们看到错误码系统提供到了 133 号!
在这里插入图片描述
如果你不喜欢系统提供的错误码,也可以自定义错误码对应的描述信息!


我们来使用使用错误码:
我们调用 fopen 函数以读的方式打开一个不存在的文件,在 fopen 函数的描述中,可以看到如果有错误,错误码就会被设置,我们可以通过 errno 变量来打印错误的描述信息。也可以通过 perror 函数来打印错误的描述信息,这些都是在 C 语言阶段学习过的,就不详细解释了!
errno 的使用需要包含头文件:#include<errno.h>
在这里插入图片描述

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

int main()
{
    extern int errno;
    fopen("test.txt", "r");
    printf("%d: %s\n", errno, strerror(errno));
    perror("fopen");
    return 0;
}

在这里插入图片描述

代码异常

如果代码出现了异常,那么退出码还有意义嘛?

代码异常说明代码很可能没跑完,return 语句都没有执行,用户都不知道这个退出码怎么来的,因此是没有任何意义的!就比如你作弊考了 100 分,你爸会关心你的 100 分嘛,直接就是辣椒炒肉了!

代码异常引起的进程终止,我们要关心的是为什么异常了!发生了什么异常!
Linux 中代码异常的本质是进程收到了信号!信号我们后续会详解!之前我们不是还用过 9 号信号嘛:杀死一个进程!
我们可以写一个会发生异常的代码:

#include<stdio.h>

int main()
{
    int a = 10;
    a /= 0;

    return 0;
}

在这里插入图片描述
怎么验证代码异常的本质是进程收到信号了呢?很简单只需要我们手动给进程发信号就行了,上面的异常是几号信号呢?我们使用 kill -l 查看一下所有信号,仔细找找发现是 8 号信号:
在这里插入图片描述
因此我们手动给进程发 8 号信号看看结果:

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

int main()
{
    while(1)
    {
        printf("hello linux, pid: %d\n", getpid());
        sleep(1);
    }

    return 0;
}

在这里插入图片描述
可以看到发送 8 号信号之后,进程也是出现了浮点异常!说明代码异常的本质就是进程受到了信号

知识点小结:

  • 进程终止的三种情况
  • 退出码
  • 错误码

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

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

相关文章

英伟达危机大爆发!一夜之间,四面楚歌

今年以来&#xff0c;AI大模型明争暗斗、百花齐放。 但不管各种大模型打的有多厉害&#xff0c;很多人都认为“卖铲子”的英伟达才是最大赢家。 看一下英伟达今年的股票就知道英伟达赚的是多么盆满钵满。 英伟达CEO黄仁勋在发布 H200显卡时&#xff0c;应该是今年最意气风发的…

Swagger2的使用

手写Api文档的几个痛点&#xff1a; 文档需要更新的时候&#xff0c;需要再次发送一份给前端&#xff0c;也就是文档更新交流不及时。 接口返回结果不明确 不能直接在线测试接口&#xff0c;通常需要使用工具&#xff0c;比如postman 接口文档太多&#xff0c;不好管理 Sw…

Linux:进程优先级与命令行参数

目录 1.进程优先级 1.1 基本概念 1.2 查看系统进程 1.3 修改进程优先级的命令 2.进程间切换 2.1 相关概念 2.2 Linux2.6内核进程调度队列&#xff08;了解即可&#xff09; 3.命令行参数 1.进程优先级 1.1 基本概念 cpu资源分配的先后顺序&#xff0c;就是指进程的优…

探秘机器学习核心逻辑:梯度下降的迭代过程 (图文详解)

一 需求解函数 f() 和 g()函数分别为求y值和求导数的函数。 目的&#xff1a;求该函数的最小值&#xff1a; 代码&#xff1a; import numpy as np import matplotlib.pyplot as plt f lambda x : (x - 3.5) ** 2 - 4.5 * x 10 g lambda x : 2 * (x - 3.5) - 4.5x np.l…

class064 Dijkstra算法、分层图最短路【算法】

class064 Dijkstra算法、分层图最短路【算法】 算法讲解064【必备】Dijkstra算法、分层图最短路 code1 743. 网络延迟时间 // Dijkstra算法模版&#xff08;Leetcode&#xff09; // 网络延迟时间 // 有 n 个网络节点&#xff0c;标记为 1 到 n // 给你一个列表 times&…

一个或多个筛选器或者Listeners启动失败 的问题

核心&#xff1a; 这个就是有好多情况会导致这个问题&#xff0c;像是文件找不到&#xff0c;缺少jar包等原因&#xff0c;还是要看报错的具体信息。 报错情况&#xff1a; 一个或多个listeners启动失败&#xff0c;更多详细信息查看对应的容器日志文件 由于之前的错误&#x…

Flutter视频播放器在iOS端和Android端都能实现全屏播放

Flutter开发过程中&#xff0c;对于视频播放的三方组件有很多&#xff0c;在Android端适配都挺好&#xff0c;但是在适配iPhone手机的时候&#xff0c;如果设置了UIInterfaceOrientationLandscapeLeft和UIInterfaceOrientationLandscapeRight都为false的情况下&#xff0c;无法…

基于lambda简化设计模式

前言 虽说使用设计模式可以让复杂的业务代码变得清晰且易于维护&#xff0c;但是某些情况下&#xff0c;开发可能会遇到我为了简单的业务逻辑去适配设计模式的情况&#xff0c;本文笔者就以四种常见的设计模式为例&#xff0c;演示如何基于lambda来简化设计模式的实现。 策略…

postgresql自带指令命令系列二

简介 在安装postgresql数据库的时候会需要设置一个关于postgresql数据库的PATH变量 export PATH/home/postgres/pg/bin:$PATH&#xff0c;该变量会指向postgresql安装路径下的bin目录。这个安装目录和我们在进行编译的时候./configure --prefix [指定安装目录] 中的prefix参…

TypeScript中的单件设计模式

基本概念 &#xff08;1&#xff09; 了解设计模式 设计模式通俗的讲&#xff0c;就是一种更好的编写代码方案&#xff0c;打个比喻&#xff1a;从上海到武汉&#xff0c;你可以选择做飞机&#xff0c;做轮船&#xff0c;开车&#xff0c;骑摩托车多种方式&#xff0c;把出行…

【Kubernetes】四层代理Service

Service四层代理 一、Service概念原理1.1、为什么要有Service1.2、Service概述1.3、工作原理1.4、三类IP地址【1】Node Network&#xff08;节点网络&#xff09;【2】Pod network&#xff08;pod 网络&#xff09;【3】Cluster Network&#xff08;服务网络&#xff09; 二、S…

四川技能大赛——2023年四川网信人才技能大赛(网络安全管理员赛项)决赛

四川技能大赛——2023年四川网信人才技能大赛&#xff08;网络安全管理员赛项&#xff09;决赛 文章目录 四川技能大赛——2023年四川网信人才技能大赛&#xff08;网络安全管理员赛项&#xff09;决赛C1-比64少的bas - DONEC2-affine - DONEC3-简单的RSA - DONEM1-不要动我的f…

【C++数据结构 | 字符串速通】10分钟秒杀字符串相关操作 | 字符串的增删改查 | 字符串与数组相互转换

字符串 by.Qin3Yu 文中所有代码默认已使用std命名空间且已导入部分头文件&#xff1a; #include <iostream> #include <string> using namespace std;概念速览 字符串是一种非常好理解的数据类型&#xff0c;它用于存储和操作文本数据。字符串可以包含任意字符…

认识存储管理

存储器是计算机系统中最重要的资源之一。因为任何程序和数据以及各种控制用的数据结构都必须占有一定的存储空间&#xff0c;因此&#xff0c;存储管理直接影响系统性能。 存储器由内存和外存组成。内存是由系统实际提供的存储单元&#xff08;常指字节&#xff09;组成的一个连…

delphi android打开外部文件,报错android.os.FileUriExposedException解决方法

Android 7.0强制启用了被称作 StrictMode的策略&#xff0c;带来的影响就是你的App对外无法暴露file://类型的URI了。 如果你使用Intent携带这样的URI去打开外部App(比如&#xff1a;打开系统相机拍照)&#xff0c;那么会抛出FileUriExposedException异常。 Delphi 为Android…

spring集成mybatis简单教程

首先说下实现了什么效果&#xff0c;就是不用每次查询前手动创建 sessionFactory和添加datasource文件了。 整个工程结构是这样的 这次我也把代码放在了gitee上&#xff0c;方便大家更全貌的看到所有的实现细节。代码链接如下&#xff1a; Java: 一些Java代码 (gitee.com) …

交换机基本原理和配置

目录 一、数据链路层功能 二、交换机的工作原理 三、交换机的四大功能 一、数据链路层功能 位于网络层与物理层之间 数据链路的建立、维护与拆除帧包装、帧传输、帧同步帧的差错恢复流量控制 二、交换机的工作原理 交换机通过数据帧的源 MAC 地址&#xff0c;学习到交换机端…

生成模型 | 数字人类的三维重建(3D reconstruction)调研及总结【20231210更新版】

本文主要集中于图片到三维重建的算法模型&#xff0c;其中包含人体重建&#xff0c;人脸重建等 1.三维人体重建 1.1.2015_SMPL: A Skinned Multi-Person Linear Model 论文地址&#xff1a;SMPL2015.pdf (mpg.de) 代码地址&#xff1a;CalciferZh/SMPL: NumPy, TensorFlow an…

我的隐私计算学习——隐私集合求交(1)

笔记内容来自多本书籍、学术资料、白皮书及ChatGPT等工具&#xff0c;经由自己阅读后整理而成。 &#xff08;一&#xff09;PSI的介绍 隐私计算关键技术&#xff1a;隐私集合求交&#xff08;PSI&#xff09;原理介绍 隐私计算关键技术&#xff1a;隐私集合求交&#xff08…

利用Node.js和cpolar实现远程访问,无需公网IP和路由器设置的完美解决方案

文章目录 前言1.安装Node.js环境2.创建node.js服务3. 访问node.js 服务4.内网穿透4.1 安装配置cpolar内网穿透4.2 创建隧道映射本地端口 5.固定公网地址 前言 Node.js 是能够在服务器端运行 JavaScript 的开放源代码、跨平台运行环境。Node.js 由 OpenJS Foundation&#xff0…