进程的几种终止方式(Termination)

(1)正常退出

从main函数返回[return]

调用exit

调用_exit或者_Exit

最后一个线程从其启动处返回

从最后一个线程调用pthread_exit (最后两点见后面博客)

(2)异常退出

调用abort   产生SIGABOUT信号

由信号终止  Ctrl+C [SIGINT]

最后一个线程对取消请求做出响应

从图中可以看出,_exit 函数的作用是:直接使进程停止运行,清除其使用的内存空间,并清除其在内核的各种数据结构;exit 函数则在这些基础上做了一些小动作,在执行退出之前还加了若干道工序。exit() 函数与 _exit() 函数的最大区别在于exit()函数在调用exit  系统调用前要检查文件的打开情况,把文件缓冲区中的内容写回文件。也就是图中的“清理I/O缓冲”。另外注意_exit是一个系统调用,exit是一个c库函数。

  1. int main()
  2. {
  3. pid_t result;
  4. result=fork();
  5. if(result<0)
  6. ERR_EXIT("fork error");
  7. if(result==0)
  8. {
  9. printf("This is the _exit test.Child pid=%d\n",getpid());
  10. printf("Output the content in Child!");
  11. _exit(0);
  12. }
  13. else
  14. {
  15. printf("This is the exit test.Parent pid=%d\n",getpid());
  16. printf("Output the content in Parent!");
  17. exit(0);
  18. }
  19. return 0;
  20. }

结果分析:

子进程中运行_exit(0)并未将This
is the content in Child打印出来,而父进程中运行的exit(0)将This is the content in Parent打印出来了。说明,exit(0)会在终止进程前,将缓冲I/O内容清理掉,所以即使printf里面没有 \n也会被打印出来,而_exit(0)是直接终止进程,并未将缓冲I/O内容清理掉,所以不会被打印出来。

终止处理程序:atexit

按照ISO C的规定,一个进程可以登记多至32个函数,这些函数将由exit自动调用,我们称这些函数为终止处理函数,并调用atexit函数来登记这些函数

  1. void exitHa1()
  2. {
  3. printf("when exit,the num is one\n");
  4. }
  5. void exitHa2()
  6. {
  7. printf("when exit,the num is two\n");
  8. }
  9. int main()
  10. {
  11. printf("In main ,pid=%d\n",getpid());
  12. atexit(exitHa1);
  13. atexit(exitHa2);
  14. return 0;
  15. }

我们可以看出的是:先注册的后执行。

如果将return -0替换成abort()异常退出的话,那么程序运行的结果如上图。

exec函数族

exec替换进程印象

在进程的创建上,Unix采用了一个独特的方法,它将进程创建与加载一个新进程映象分离。这样的好处是有更多的余地对两种操作进行管理。

当我们创建了一个进程之后,通常将子进程替换成新的进程映象,这可以用exec系列的函数来进行。当然,exec系列的函数也可以将当前进程替换掉。

exec只是用磁盘上的一个新程序替换了当前进程的正文段, 数据段, 堆段和栈段.并没有创建新进程,所以进程的ID是不变的。

exec函数族:

  1. int execve(const char *filename, char *const argv[],
  2. char *const envp[]);
  3. #include <unistd.h>
  4. extern char **environ;
  5. int execl(const char *path, const char *arg, ...);
  6. int execlp(const char *file, const char *arg, ...);
  7. int execle(const char *path, const char *arg,
  8. ..., char * const envp[]);
  9. int execv(const char *path, char *const argv[]);
  10. int execvp(const char *file, char *const argv[]);
  11. int execvpe(const char *file, char *const argv[],
  12. char *const envp[]);

说明:

execl,execlp,execle(都带“l”, 代表list)的参数个数是可变的,参数以必须一个空指针结束。

execv和execvp的第二个参数是一个字符串数组(“v”代表“vector”,字符串数组必须以NULL结尾),新程序在启动时会把在argv数组中给定的参数传递到main。

名字最后一个字母是“p”的函数会搜索PATH环境变量去查找新程序的可执行文件。如果可执行文件不在PATH定义的路径上,就必须把包括子目录在内的绝对文件名做为一个参数传递给这些函数;

/*总结:l代表可变参数列表,与v互斥;v表示该函数取一个argv[]矢量;p代表在path环境变量中搜索file文件;e表示该函数取envp[]数组,而不使用当前环境*/

  1. int main()
  2. {
  3. printf("Test the execlp:\n");
  4. pid_t pid=fork();
  5. if(pid==0)
  6. {
  7. if(execlp("/bin/pwd","pwd",NULL)==-1)
  8. ERR_EXIT("execlp pwd error");
  9. }
  10. wait(NULL);
  11. pid=fork();
  12. if(pid==0)
  13. {
  14. if(execlp("/bin/ls","-l",NULL)==-1)
  15. ERR_EXIT("execlp ls -l error");
  16. }
  17. wait(NULL);
  18. printf("Test the execve:\n");
  19. char *argv_execve[]={"/bin/data","+%F",NULL};
  20. pid=fork();
  21. if(pid==0)
  22. {
  23. if(execve("/bin/date",argv_execve,NULL)==-1)
  24. ERR_EXIT("execve error");
  25. }
  26. wait(NULL);
  27. return 0;
  28. }

另外,如果你理解execv, 那么execle和他的区别就是, 前者的调用参数是以数组形式给的,而后者则是以列表方式给,也就是execle(path, arg1, arg2, ..., envp), 并且提供了环境变量参数;

  1. #include<unistd.h>
  2. #include<stdio.h>
  3. #include<stdlib.h>
  4. int main()
  5. {
  6. char *envp[]={"PATH=/tmp","USER=shan",NULL};
  7. if(fork()==0)
  8. {
  9. if(execle("/bin/dir","dir",NULL,envp)<0)
  10. perror("execle error!");
  11. }
  12. return 0;
  13. }

System系统调用

system()函数调用“/bin/sh -c command”执行特定的命令,阻塞当前进程直到command命令执行完毕,system函数执行时,会调用fork、execve、waitpid等函数。

原型:

  1. int system(const char *command);

返回值:

如果无法启动shell运行命令,system将返回127;出现不能执行system调用的其他错误时返回-1。如果system能够顺利执行,返回那个命令的退出码。

自己利用execl实现system:

  1. int mySystem(const char *command)
  2. {
  3. if (command == NULL)
  4. {
  5. errno = EAGAIN;
  6. return -1;
  7. }
  8. pid_t pid = fork();
  9. if (pid == -1)
  10. {
  11. perror("fork");
  12. exit(-1);
  13. }
  14. else if (pid == 0)
  15. {
  16. execl("/bin/sh","sh","-c",command,NULL);
  17. exit(127);
  18. }
  19. int status;
  20. waitpid(pid,&status,0);
  21. //wait(&status);
  22. return WEXITSTATUS(status);//宏,子进程如果正常结束返回0
  23. }
  24. int main()
  25. {
  26. mySystem("ls -la");
  27. return 0;
  28. }

原文:http://blog.csdn.net/nk_test/article/details/48324609

Linux进程理解与实践(三)进程终止函数和exec函数族的使用的更多相关文章

  1. Linux进程理解与实践(一)基本概念和编程概述(fork,vfork,cow)

    进程 and 程序 什么是程序? 程序是完成特定任务的一系列指令集合. 什么是进程? [1]从用户的角度来看:进程是程序的一次执行过程 [2]从操作系统的核心来看:进程是操作系统分配的内存.CPU时间 ...

  2. Linux进程理解与实践(五)细谈守护进程

    一. 守护进程及其特性      守护进程最重要的特性是后台运行.在这一点上DOS下的常驻内存程序TSR与之相似.其次,守护进程必须与其运行前的环境隔离开来.这些环境包括未关闭的文件描述符,控制终端, ...

  3. Linux进程理解与实践(四)wait函数处理僵尸进程

    Wait的背景 当子进程退出的时候,内核会向父进程发送SIGCHLD信号,子进程的退出是个异步事件(子进程可以在父进程运行的任何时刻终止) 子进程退出时,内核将子进程置为僵尸状态,这个进程称为僵尸进程 ...

  4. Linux进程理解与实践(二)僵尸&孤儿进程 和文件共享

    孤儿进程与僵尸进程 孤儿进程: 如果父进程先退出,子进程还没退出那么子进程的父进程将变为init进程.(注:任何一个进程都必须有父进程) [cpp] view plaincopy #include & ...

  5. Linux 环境下 fork 函数和 exec 函数族的使用

    前言 接触 Linux 已经有几个月了,以前在网上看各路大神均表示 Windows 是最烂的开发平台,我总是不以为然,但是经过这段时间琢磨,确实觉得 Linux 开发给我带来不少的便利.下面总结一下学 ...

  6. OWIN的理解和实践(三) –Middleware开发入门

    上篇我们谈了Host和Server的建立,但Host和Server无法产出任何有实际意义的内容,真正的内容来自于加载于Server的Middleware,本篇我们就着重介绍下Middleware的开发 ...

  7. Linux系统编程——进程替换:exec 函数族

    在 Windows 平台下,我们能够通过双击运行可运行程序,让这个可运行程序成为一个进程.而在 Linux 平台.我们能够通过 ./ 运行,让一个可运行程序成为一个进程. 可是.假设我们本来就执行着一 ...

  8. 《Linux及安全》实践3.1

    <Linux及安全>实践三 ELF格式文件分析 一.基础操作 1.查看大小端.32还是64 由此可以看出,本人实践所用到的是32位Ubuntu,数据存储采用小端法. 2.编写hello.c ...

  9. 《Linux及安全》实践3.3

    <Linux及安全>实践三 字符集总结与分析 [by lwr] 一.ISO.UCS/UTF.GB系列字符集分析 1.字符集&字符编码 字符集(Charset):是一个系统支持的所有 ...

随机推荐

  1. nginx用Certbot配置免费SSL证书(ngx_http_ssl_module模块)

    一.准备工作 1.先安装nginx https://files.cnblogs.com/files/blogs/676936/nginx-1.18.0.sh #nginx-1.18.0版安装脚本2.在 ...

  2. Spring:Spring注解大全

    @Controller 标识一个该类是Spring MVC controller处理器,用来创建处理http请求的对象. @Controller public class TestController ...

  3. [小工具] chrome上日语翻译工具

    rikaikun -> 日语 "理解君" 下载地址: https://chrome.google.com/webstore/detail/rikaikun/jipdnfibh ...

  4. ESP32的Flash加密知识

    一.Flash 加密功能用于加密与 ESP32-S2 搭载使用的 SPI Flash 中的内容.启用 Flash 加密功能后,物理读取 SPI Flash 便无法恢复大部分 Flash 内容.通过明文 ...

  5. HanLP使用教程——NLP初体验

    话接上篇NLP的学习坑 自然语言处理(NLP)--简介 ,使用HanLP进行分词标注处词性. HanLP使用简介 HanLP是一系列模型与算法组成的NLP工具包,目标是普及自然语言处理在生产环境中的应 ...

  6. TestNG基础001

    一.什么是TestNG TestNG是一个强大的测试框架,NG是指Next Generation ,被视为是Junit的升级版本 二.TestNG适用范围 Java单元测试 接口测试 web自动化测试 ...

  7. python import 导入两个模块同时有同一名称的方法如何调用 ?

    from moudule import *(这种方法不推荐) 一般不推荐使用"from 模块 import"这种语法导入指定模块内的所有成员,因为它存在潜在的风险. 比如同时导入 ...

  8. ES6新增语法(七)——async...await

    什么是async async的意思是"异步",顾名思义就是有关异步操作的关键字,async 是 ES7 才有的,与我们之前说的Promise.Generator有很大的关联. 使用 ...

  9. 正则表达式的模式匹配----V客学院技术分享

    正则表达式是由一个字符序列形成的搜索模式. 你在文本中搜索数据时,你可以用搜索模式来描述你要查询的内容. 正则表达式可以是一个简单的字符,或一个更复杂的模式. 正则表达式可用于所有文本搜索和文本替换的 ...

  10. 【开发工具】-- IDEA集成Git在实际项目中的运用

    1.企业实际项目中Git的使用 在实际的企业项目开发中,我们一般Java的项目在公司都有自己的局域网代码仓库,仓库上存放着很多的项目.以我工作过的公司如华为的项目,一般是存放在企业内部的CodeHub ...