进程的几种终止方式(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. jquery 选择器 模糊匹配

    根据name值匹配 1.查询前缀为aa的数据 $("div[name^='aa']"); 2.查询后缀为aa的所有div的jquery对象 $("div[name$='a ...

  2. js 判断是什么浏览器加载页面

    一.Navigator 属性: 1)appcodeName  返回浏览器代码名 2)appminorVersion  返回浏览器次级版本 3)appname  返回浏览器名称 4)browserLan ...

  3. 浅析WebSocket 原理

    浅析WebSocket 原理 长恨此身非我有,何时忘却营营. 简介:先简单了解下WebSocket 原理,日后的使用中再进一步深入研究~ 一.什么是WebSocket WebSocket 是HTML5 ...

  4. Postgresql常见操作命令

    安装Postgresql 请查看我的另一篇博文: 博文连接:https://www.cnblogs.com/cndevops/p/14962745.html 连接Postgresql数据库 服务端连接 ...

  5. Jenkins 进阶篇 - 权限配置

    Jenkins的授权策略 Jenkins 默认的授权策略是[登录用户可以做任何事],也就是人人都是管理员,可以修改所有的设置以及构建所有的任务,不用做任何设置,有账号登录到 Jenkins 系统即可, ...

  6. Docker:docker部署Sqlite3数据库

    1.依赖Ubuntu系统安装sqlite3生成镜像 dockerfile文件 FROM ubuntu:trusty RUN sudo apt-get -y update RUN sudo apt-ge ...

  7. python log装饰器

    def log(func): #将原函数对象的指定属性复制给包装函数对象, 默认有 module.name.doc,或者通过参数选择 @functools.wraps(func) def wrappe ...

  8. (学习心路历程)Vue过渡/动画 VS. 过渡/动画

    [此篇为本人的个人见解和哔哔赖赖,如果有观点不对的地方,还请大家指出来哇!!] 最近实习在做一个项目,里面应用的动画效果还蛮复杂的,因为本身对Vue框架比较熟悉,所以最终选择了Vue框架. 自己之前从 ...

  9. Spring Cloud组件和架构图

    Spring Cloud是微服务架构的集大成者,将一系列优秀的组件进行了整合. 服务网关:聚合内部服务,提供统一的对外API接口,屏蔽内部实现.可以解决跨域.认证和前端调用负责的问题,便于项目重构.可 ...

  10. ARTS第九周

    1.Algorithm:每周至少做一个 leetcode 的算法题2.Review:阅读并点评至少一篇英文技术文章3.Tip:学习至少一个技术技巧4.Share:分享一篇有观点和思考的技术文章 以下是 ...