Linux进程理解与实践(三)进程终止函数和exec函数族的使用
进程的几种终止方式(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库函数。
- int main()
- {
- pid_t result;
- result=fork();
- if(result<0)
- ERR_EXIT("fork error");
- if(result==0)
- {
- printf("This is the _exit test.Child pid=%d\n",getpid());
- printf("Output the content in Child!");
- _exit(0);
- }
- else
- {
- printf("This is the exit test.Parent pid=%d\n",getpid());
- printf("Output the content in Parent!");
- exit(0);
- }
- return 0;
- }
结果分析:
子进程中运行_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函数来登记这些函数
- void exitHa1()
- {
- printf("when exit,the num is one\n");
- }
- void exitHa2()
- {
- printf("when exit,the num is two\n");
- }
- int main()
- {
- printf("In main ,pid=%d\n",getpid());
- atexit(exitHa1);
- atexit(exitHa2);
- return 0;
- }
我们可以看出的是:先注册的后执行。
如果将return -0替换成abort()异常退出的话,那么程序运行的结果如上图。
exec函数族
exec替换进程印象
在进程的创建上,Unix采用了一个独特的方法,它将进程创建与加载一个新进程映象分离。这样的好处是有更多的余地对两种操作进行管理。
当我们创建了一个进程之后,通常将子进程替换成新的进程映象,这可以用exec系列的函数来进行。当然,exec系列的函数也可以将当前进程替换掉。
exec只是用磁盘上的一个新程序替换了当前进程的正文段, 数据段, 堆段和栈段.并没有创建新进程,所以进程的ID是不变的。
exec函数族:
- int execve(const char *filename, char *const argv[],
- char *const envp[]);
- #include <unistd.h>
- extern char **environ;
- int execl(const char *path, const char *arg, ...);
- int execlp(const char *file, const char *arg, ...);
- int execle(const char *path, const char *arg,
- ..., char * const envp[]);
- int execv(const char *path, char *const argv[]);
- int execvp(const char *file, char *const argv[]);
- int execvpe(const char *file, char *const argv[],
- 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[]数组,而不使用当前环境*/
- int main()
- {
- printf("Test the execlp:\n");
- pid_t pid=fork();
- if(pid==0)
- {
- if(execlp("/bin/pwd","pwd",NULL)==-1)
- ERR_EXIT("execlp pwd error");
- }
- wait(NULL);
- pid=fork();
- if(pid==0)
- {
- if(execlp("/bin/ls","-l",NULL)==-1)
- ERR_EXIT("execlp ls -l error");
- }
- wait(NULL);
- printf("Test the execve:\n");
- char *argv_execve[]={"/bin/data","+%F",NULL};
- pid=fork();
- if(pid==0)
- {
- if(execve("/bin/date",argv_execve,NULL)==-1)
- ERR_EXIT("execve error");
- }
- wait(NULL);
- return 0;
- }
另外,如果你理解execv, 那么execle和他的区别就是, 前者的调用参数是以数组形式给的,而后者则是以列表方式给,也就是execle(path, arg1, arg2, ..., envp), 并且提供了环境变量参数;
- #include<unistd.h>
- #include<stdio.h>
- #include<stdlib.h>
- int main()
- {
- char *envp[]={"PATH=/tmp","USER=shan",NULL};
- if(fork()==0)
- {
- if(execle("/bin/dir","dir",NULL,envp)<0)
- perror("execle error!");
- }
- return 0;
- }
System系统调用
system()函数调用“/bin/sh -c command”执行特定的命令,阻塞当前进程直到command命令执行完毕,system函数执行时,会调用fork、execve、waitpid等函数。
原型:
- int system(const char *command);
返回值:
如果无法启动shell运行命令,system将返回127;出现不能执行system调用的其他错误时返回-1。如果system能够顺利执行,返回那个命令的退出码。
自己利用execl实现system:
- int mySystem(const char *command)
- {
- if (command == NULL)
- {
- errno = EAGAIN;
- return -1;
- }
- pid_t pid = fork();
- if (pid == -1)
- {
- perror("fork");
- exit(-1);
- }
- else if (pid == 0)
- {
- execl("/bin/sh","sh","-c",command,NULL);
- exit(127);
- }
- int status;
- waitpid(pid,&status,0);
- //wait(&status);
- return WEXITSTATUS(status);//宏,子进程如果正常结束返回0
- }
- int main()
- {
- mySystem("ls -la");
- return 0;
- }
原文:http://blog.csdn.net/nk_test/article/details/48324609
Linux进程理解与实践(三)进程终止函数和exec函数族的使用的更多相关文章
- Linux进程理解与实践(一)基本概念和编程概述(fork,vfork,cow)
进程 and 程序 什么是程序? 程序是完成特定任务的一系列指令集合. 什么是进程? [1]从用户的角度来看:进程是程序的一次执行过程 [2]从操作系统的核心来看:进程是操作系统分配的内存.CPU时间 ...
- Linux进程理解与实践(五)细谈守护进程
一. 守护进程及其特性 守护进程最重要的特性是后台运行.在这一点上DOS下的常驻内存程序TSR与之相似.其次,守护进程必须与其运行前的环境隔离开来.这些环境包括未关闭的文件描述符,控制终端, ...
- Linux进程理解与实践(四)wait函数处理僵尸进程
Wait的背景 当子进程退出的时候,内核会向父进程发送SIGCHLD信号,子进程的退出是个异步事件(子进程可以在父进程运行的任何时刻终止) 子进程退出时,内核将子进程置为僵尸状态,这个进程称为僵尸进程 ...
- Linux进程理解与实践(二)僵尸&孤儿进程 和文件共享
孤儿进程与僵尸进程 孤儿进程: 如果父进程先退出,子进程还没退出那么子进程的父进程将变为init进程.(注:任何一个进程都必须有父进程) [cpp] view plaincopy #include & ...
- Linux 环境下 fork 函数和 exec 函数族的使用
前言 接触 Linux 已经有几个月了,以前在网上看各路大神均表示 Windows 是最烂的开发平台,我总是不以为然,但是经过这段时间琢磨,确实觉得 Linux 开发给我带来不少的便利.下面总结一下学 ...
- OWIN的理解和实践(三) –Middleware开发入门
上篇我们谈了Host和Server的建立,但Host和Server无法产出任何有实际意义的内容,真正的内容来自于加载于Server的Middleware,本篇我们就着重介绍下Middleware的开发 ...
- Linux系统编程——进程替换:exec 函数族
在 Windows 平台下,我们能够通过双击运行可运行程序,让这个可运行程序成为一个进程.而在 Linux 平台.我们能够通过 ./ 运行,让一个可运行程序成为一个进程. 可是.假设我们本来就执行着一 ...
- 《Linux及安全》实践3.1
<Linux及安全>实践三 ELF格式文件分析 一.基础操作 1.查看大小端.32还是64 由此可以看出,本人实践所用到的是32位Ubuntu,数据存储采用小端法. 2.编写hello.c ...
- 《Linux及安全》实践3.3
<Linux及安全>实践三 字符集总结与分析 [by lwr] 一.ISO.UCS/UTF.GB系列字符集分析 1.字符集&字符编码 字符集(Charset):是一个系统支持的所有 ...
随机推荐
- Python | 更换pip源到国内镜像
pip国内的一些镜像 阿里云 https://mirrors.aliyun.com/pypi/simple/ 中国科技大学 https://pypi.mirrors.ustc.edu.cn/simpl ...
- CRM系统如何帮助企业管理多条业务线的?
在如今的市场环境中,许多企业为了提高销售效率,增加业绩收入,都会选择使用CRM客户关系管理系统来帮助进行对客户和销售的管理.CRM系统能够帮助企业在不同的产品线上同时开展营销活动.各个销售团队能够独立 ...
- PHP经典算法之背包问题
问题:假设有一个背包的负重最多可达8公斤,而希望在背包中装入负重范围内可得之总价物品,假设是水果好了,水果的编号.单价与重量如下所示: 1 栗子 4KG $4500 2 苹果 5KG $5700 3 ...
- MySQL存储引擎——InnoDB和MyISAM的区别
MySQL5.5后,默认存储引擎是InnoDB,5.5之前默认是MyISAM. InnoDB(事务性数据库引擎)和MyISAM的区别补充: InnoDB是聚集索引,数据结构是B+树,叶子节点存K-V, ...
- Java | this的本质 和 static的本质
this 在说this之前先说一下,对象创建的过程: 1.分配对象空间,并将对象成员变量初始化. 2.执行属性值的显式初始化. 3.执行构造方法. 4.返回相关的地址给相关的对象. this的本质 ...
- ARTS第十三周(阅读Tomcat源码)
1.Algorithm:每周至少做一个 leetcode 的算法题2.Review:阅读并点评至少一篇英文技术文章3.Tip:学习至少一个技术技巧4.Share:分享一篇有观点和思考的技术文章 考研真 ...
- 简单DOS命令
1.nslookup命令 nslookup命令是用来解析域名的,举个例子:我们只知道百度的域名是www.baidu.com 想要知道它的ip的话就要使用nslookup命令了nslookup www. ...
- JAVA 中日志的记录于使用
java中常用的日志框架 日志接口 Commons Logging Apache Commons Logging是一个基于Java的日志记录实用程序,是用于日志记录和其他工具包的编程模型.它通过其他一 ...
- 家庭账本开发day09
编写数据表格的编辑操作,大体思路和删除操作一样 点击按钮,弹出修改项目,从父窗口获取已有的值赋给 弹出的子窗口中相应的值,在子窗口中点击提交,ajax请求 servlet修改.成功后重载表格,或者up ...
- python -- 程序异常与调试(程序调试)
一.程序调试 A.使用assert语句检测程序代码中的错误. assert 表达式[, 参数] 如果表达式为True,则继续往下运行:如果为False,则抛出一个AssertionError异常,并且 ...
