进程的几种终止方式(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. POJ 1873 The Fortified Forest 凸包 二进制枚举

    n最大15,二进制枚举不会超时.枚举不被砍掉的树,然后求凸包 #include<stdio.h> #include<math.h> #include<algorithm& ...

  2. [心得]zookeeper

    1. 什么是zookeeper? 分布式协调服务 是一个典型的分布式数据一致性解决方案,分布式应用程序可以基于 ZooKeeper 实现诸如数据发布/订阅.负载均衡.命名服务.分布式协调/通知.集群管 ...

  3. mysql json_extract函数获取json字段中某个key的值

    参考:https://www.cnblogs.com/chuanzhang053/p/9139624.html json_extract函数可以获取json对象中指定key的值,用法:json_ext ...

  4. Redis中的布隆过滤器及其应用

    什么是布隆过滤器 布隆过滤器(Bloom Filter)是由Howard Bloom在1970年提出的一种比较巧妙的概率型数据结构,它可以告诉你某种东西一定不存在或者可能存在.当布隆过滤器说,某种东西 ...

  5. Android hacking event 2017

    1.you can't find me, 老规矩先打开jeb,然后看下主活动, 发现又调用了mainthread类的startWrites方法,继续跟进去. 发现是新建了一个随机输入流的文件对象,然后 ...

  6. vuejs知识总结

    1.Vue.js是当下很火的一个JavaScript MVVM库,它是以数据驱动和组件化的思想构建的.ViewModel是Vue.js的核心,它是一个Vue实例. <!DOCTYPE html& ...

  7. C语言:2.1

    int main() { char zi='A'; short bla=10; int blb=20; long blc=30; float bld=340.56; double ble=34.324 ...

  8. Scala学习——简介

    一.Scala简介 Scala 是 Scalable Language 的简写,是一门多范式的编程语言,设计初衷是实现可伸缩的语言并集成面向对象编程和函数式编程的各种特性. 二.Scala 环境搭建 ...

  9. Python+Requests+Xpath实现动态参数获取实战

    1.古诗文网直接登录时,用浏览器F12抓取登录接口的入参,我们可以看到框起来的key对应的value是动态参数生成的,需获取到: 2.登录接口入参的值一般是登录接口返回的原数据值,若刷新后接口与对应源 ...

  10. Python多线程爬取某网站表情包

    # 爬取网络图片import requestsfrom lxml import etreefrom urllib import requestfrom queue import Queue # 导入队 ...