美文网首页
系统编程:进程的8种终止方式

系统编程:进程的8种终止方式

作者: 梁帆 | 来源:发表于2021-08-24 15:55 被阅读0次

一、5种正常终止

1.从main函数返回

int main()
{
    printf("Hello World!\n");
    return 0;
}

进程的返回值是给其父进程看的
如我们这里,父进程就是shell,程序返回的0就是由shell接收的。
在命令行输入:

echo $?

这条shell命令是打印上一条指令的执行状态,输出如下:

0

可以看到,父进程shell看到子进程的退出状态就是返回值0。
下面我们将return 0语句注释掉,如下:

int main()
{
    printf("Hello World!\n");
    //return 0;
}

这里我们再执行一下这个程序,再用shell命令行查下返回值,可以看到输出:

13

因为字符串"Hello World!\n",就占用了13个字符空间,printf函数的返回值就是返回的输出字符数目,即13。

2.调用exit

基本调用方法就是将exit(0)替换掉上文的return 0,如下:

int main()
{
    ......
    exit(0);
}

exit的函数声明是:

void exit(int status);

执行exit函数会导致进程正常地终止,并且会将 status & 0377 这个值返回给父进程。
0377是8进制,把它转换成二进制即为:

011,111,111

总共有8个1,status和低位8个1相与,意味着这个数值就是保留着低8位数值。而status是有符号型整数,就意味着8位数值能表示的范围是-127~128,即

exit能带回去的环境为256种,值范围是-127~128.

此外,exit()退出时会执行退出处理程序,如atexit()和on_exit()。两个函数大同小异,我们只举一个例子。

atexit():钩子函数

钩子函数的作用就相当于一些面向对象语言的析构函数。
atexit的函数声明如下:

int atexit(void (*function)(void));

钩子函数就类似注册函数,传入的参数是一个函数指针,函数指针的函数签名必须是void参数和void返回值。
有示例代码如下:

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

static void f1(void)
{
    puts("f1 is working!");
}

static void f2(void)
{
    puts("f2 is working!");
}

static void f3(void)
{
    puts("f3 is working!");
}


int main()
{
    puts("Begin!");

    atexit(f1);     // 只是把函数f1、f2、f3挂到钩子上,还未被调用
    atexit(f2);
    atexit(f3);

    puts("End!");
                    // 在执行exit时,才会逆序调用挂在钩子上的函数    
    exit(0);
}

输出如下:

[root@localhost fs]# ./atexit 
Begin!
End!
f3 is working!
f2 is working!
f1 is working!

可以看到如我们所料,f1、f2和f3这三个函数,调用顺序和注册顺序是相反的。
那么钩子函数到底有什么作用呢?
钩子函数的作用就相当于go语言中的defer语句。
试想一下这样的情景,我们要依次open 100个文件,最后也需要在主进程末尾 close100次,这就显得很不美观,而且很容易漏掉。这个时候最好的方法就是open一个,挂钩子一个,如下:

fd1 = open("/tmp/file1", O_RDONLY);
atexit();    // --->close(fd1)

fd2 = open("/tmp/file2", O_RDONLY);
atexit();    // --->close(fd2)
...

3.调用_exit或_Exit

首先要明确一点:
exit是库函数,_exit和_Exit是系统调用。
_exit的函数声明:

#include <unistd.h>
void _exit(int status);

_Exit的函数声明:

#include <stdlib.h>
void _Exit(int status);

这两个函数相比exit()函数的区别在于:

_exit()和_Exit()是不执行任何钩子函数程序标准I/O清理程序的。eixt()函数会先调用钩子函数atexit(),再刷新stdio流缓冲区,最后由status提供的值执行_exit()系统调用。

有时候为了避免故障扩大,不想要exit的钩子调用和I/O清理,就可以直接调用_exit()函数。

4.最后一个线程从其启动例程返回

5.最后一个线程调用pthread_exit

二、3种异常终止

1.调用abort

2.接到一个信号并终止

3.最后一个线程对其取消请求作出响应

相关文章

  • 系统编程:进程的8种终止方式

    一、5种正常终止 1.从main函数返回 进程的返回值是给其父进程看的。如我们这里,父进程就是shell,程序返回...

  • 检查进程的存在的几种方式

    wait()系统调用.当调用进程的任意子进程终止时,会返回,由此可判断子进程是否终止.wait系统调用存在以下几个...

  • 进程的终止方式和状态

    进程的终止方式: 有8种方式使进程终止,其中前5种为正常终止 从main返回调用exit调用_exit或者_Exi...

  • 进程控制

    进程控制 1、获取系统进程 2、终止当前进程 3、终止其他进程 4、保护进程 5、进程内存空间说明 6、读进程内存...

  • 查询端口号

    查询端口是否被占用,被哪个进程占用有两种方式: 终止进程的方式:

  • Android P进程优先级

    进程优先级 系统为了新建进程或运行更重要的进程,Android系统需要清除旧进程来回收内存。为了确定保留或终止哪些...

  • APUE第13章 守护进程Deameon

    守护进程 守护进程daemon是一种生存周期很长的进程。它们通常在系统引导时启动,在系统关闭时终止。守护进程是没有...

  • 【linux/unix系统编程手册笔记】系统编程概念

    【Linux/Unix系统编程手册笔记】系统编程概念 1.系统调用 系统调用: 创建新进程 执行I/O 进程间通信...

  • 守护进程

    守护进程 什么是守护进程 守护进程是生存期长的一种进程.它们常常在系统引导装入时启动,仅在系统关闭时才终止.因为它...

  • 第2章 2-2进程控制与同步

    进程控制的基本过程: 进程的创建 进程的终止 进程的阻塞与唤醒 进程的挂起和激活 关于进程的亲属关系 系统中运行的...

网友评论

      本文标题:系统编程:进程的8种终止方式

      本文链接:https://www.haomeiwen.com/subject/zdudiltx.html