fork和exec函数
#include<unistd.h>
pid_t fork(void);
返回:在子进程中为0,在父进程中为子进程IO,若出错则为-
fork最困难之处在于调用它一次,它却返回两次。它在调用进程(称为父进程)中返回一次,返回值是新派生进程(称为子进程)的进程ID号;在子进程中又返回一次,返回值为0.因此,返回值本身告知当前进程是子进程还是父进程。
fork在子进程返回0而不是父进程的进程ID的原因在于:任何子进程只有一个父进程,而子进程总是可以通过getppid取得父进程的进程ID。相反,父进程可以有许多子进程而且无法获取各个子进程的进程ID。如果父进程想要跟踪所有子进程的进程ID,那么它必须记录每次调用fork的返回值。
父进程中调用fork之前打开的所有描述符在fork返回之后由子进程分享。我们将看到网络服务器利用了这个特性:父进程调用accept之后调用fork。所接受的已连接套接字随后就在进程与子进程之间共享。通常情况下,子进程接着读写这个已连接套接字,父进程则关闭这个已连接套接字。
fork的两个典型用法。
(1)一个进程创建一个自身的副本,这样每个副本都可以在另一个副本执行其他任务的同时处理各自的某个操作。这是网络服务器的典型用法。
(2)一个进程想要执行另一个程序。既然创建新进程的唯一方法是调用fork,该进程于是首先调用fork创建一个自身的副本,然后其中一个副本(通常是子进程)调用exec把自身替换成新的程序。这是诸如shell之类程序的典型用法。
存放在硬盘上的可执行程序文件能够被Unix执行的唯一方法是:由一个现有进程调用六个exec函数中的某一个。exec把当前进程映像替换成新的程序文件,而且该新程序通常从main函数开始执行。进程ID并不改变。我们称调用exec的进程为调用进程,称新执行的程序为新程序。
这6个exec函数之间的区别在于:(a)待执行的程序文件是由文件名(filename)还是由路径名(pathname)指定;(b)新程序的参数是一一列出还是由一个指针数组来引用;(c)把调用进程的环境传递给新程序还是给新程序指定新的环境。
exec函数族
函数族说明
fork() 函数用于创建一个新的子进程,该子进程几乎复制了父进程的全部内容,但是,这个新创建的子进程如何执行呢?exec 函数族就提供了一个在进程中启动另一个程序执行的方法。它可以根据指定的文件名或目录名找到可执行文件,并用它来取代原调用进程的数据段、代码段和堆栈段,在执行完之后,原调用进程的内容除了进程号外,其他全部被新的进程替换了。另外,这里的可执行文件既可以是二进制文件,也可以是Linux下任何可执行的脚本文件。
在 Linux 中使用exec函数族主要有两种情况:
● 当进程认为自己不能再为系统和用户做出任何贡献时,就可以调用 exec 函数族中的任意一个函数让自己重生。
● 如果一个进程想执行另一个程序,那么它就可以调用 fork() 函数新建一个进程,然后调用 exec 函数族中的任意一个函数,这样看起来就像通过执行应用程序而产生了一个新进程(这种情况非常普遍)。
函数族语法
实际上,在Linux中并没有exec()函数,而是由6个以 exec 开头的函数,它们之间的语法有细微差别。下表列出了 exec 函数族的6个成员函数的语法:
这些函数只有在出错时才返回到调用者,否则,控制将被传递给新程序的起始点,通常就是main函数。
这6个函数在函数名和使用语法的规则上都有细微的区别,下面就从可执行文件查找方式、参数传递方式和环境变量这几个方面进行比较。
● 查找方式:表1中的前4个函数的查找方式都是完整的文件目录路径,而最后两个函数(也就是以 p 结尾的两个函数)可以只给出文件名,系统就会自动按照环境变量“$PATH” 所指定的路径进行查找。
● 参数传递方式:exec函数族的参数传递有两种:一种是逐个列举的方式,而另一种则是将所有参数整体构造指针数组传递。在这里是以函数名的第5位字母来区分的,字母为 "l"(list)的表示逐个列举参数的方式,其语法为const char *arg;字母为“v”(vector)的表示将所有参数整体构造指针数组传递,其语法为 char *const argv[]。这里的参数实际上就是用户在使用这个可执行文件时所需的全部命令选项字符串(包括该可执行程序命令本身)。要注意的是,这些参数必须以NULL结束。
● 环境变量: exec函数族可以默认系统的环境变量,也可以传入指定的环境变量。这里以 “e”(environment)结尾的两个函数 execle()和 execve()就可以在 envp[]中指定当前进程所使用的环境变量。
表2再对这6个函数中的函数名和对应语法做了一个小结,主要指出了函数名中每一位对应所表明的含义,以此表加以记住这6个函数。
事实上,这6个函数中真正的系统调用只有execve(),其他5个都是库函数,它们最终都会调用execve()这个系统调用。在使用exec函数族时,一定要加上错误判断语句。exec 很容易执行失败,其中最常见的原因有:
① 找不到文件或路径,此时 errno 被设置为 ENOENT。
② 数组argv 和envp 忘记用NULL结束,此时,errno被设置为 EFAUL。
③ 没有对应可执行文件的运行权限,此时 errno 被设置为EACCES。
基础实验
实验1
本实验是为了说明如何使用文件名来查找可执行文件,同时使用参数列表的方式。这里用的函数是 execlp()。程序代码如下:
在该程序中,首先使用 fork()函数创建一个子进程,然后在子进程中使用 execlp()函数。可以看到,这里的参数列表列出了在 shell 中使用的命令名和选项,并且当使用文件名进行查找时,系统会在默认的环境变量PATH中寻找该可执行文件。
使用命令:gcc execlp.c -o execlp编译后,然后再执行,结果如下图:
使用env命令,可以查看到环境变量的路径名
此程序的执行结果与在shell中直接输入命令“ps -ef”是一样的,当然,在不同系统的不同时刻可能会有不同的结果。
实验2
本实验实现的功能和实验1的一样,不同的是使用的函数不同。本实验将使用完整的文件目录来查找对应的可执行文件。注意,目录必须以“/”开头,否则将其视为文件名。程序代码如下:
编写保存源文件,然后使用命令:gcc execl.c -o execl编译,接着执行命令:./execl,可以看到实验结果和实验1一样
实验3
本实验是利用execle()函数将环境变量添加到新建的子进程中,这里的“env”是查看当前进程环境变量的命令,实验代码如下:
编写保存源文件后,使用命令:gcc execle.c -o execle,再执行命令:./execle,执行结果如下图
实验4
本实验实现功能和实验3一样,不同的是使用的execve()函数,通过构造指针数组的方式来传递参数,注意参数列表一定要以NULL作为结尾标识符,实验代码如下:
编写保存源文件,使用命令:gcc execve.c -o execve,再执行命令:./execve,结果如下:
fork和exec函数的更多相关文章
- linux c语言 fork() 和 exec 函数的简介和用法
linux c语言 fork() 和 exec 函数的简介和用法 假如我们在编写1个c程序时想调用1个shell脚本或者执行1段 bash shell命令, 应该如何实现呢? 其实在<std ...
- linux进程之fork 和 exec函数
---恢复内容开始--- fork函数 该函数是unix中派生新进程的唯一方法. #include <unistd.h> pid_t fork(void); 返回: (调用它一次, 它 ...
- 从0开始自己用C语言写个shell__01_整体的框架以及fork和exec族函数的理解
最近才忙完了一个操作系统的作业,让我们用C语言实现一个Shell.总的来说,其实就是让我们 对系统调用有比较深的了解. 首先 介绍一下我的Shell 所实现的功能.1.运行可执行程序 即输入某个 标志 ...
- 【转】Linux下Fork与Exec使用
Linux下Fork与Exec使用 转自 Linux下Fork与Exec使用 一.引言 对于没有接触过Unix/Linux操作系统的人来说,fork是最难理解的概念之一:它执行一次却返回两个值.for ...
- Linux下多进程编程之exec函数语法及使用实例
exec函数族 1)exec函数族说明 fork()函数用于创建一个子进程,该子进程几乎复制了父进程的全部内容,但是,这个新创建的进程如何执行呢?exec函数族就提供了一个在进程中启动另一个程序执行的 ...
- 程序清单 8-8 exec函数实例,a.out是程序8-9产生的可执行程序
/* ============================================================================ Name : test.c Author ...
- fork和exec一起使用
先预览一下工程的目录树: 实现的功能:master进程启动slave进程. 看看Makefile内容: all: master.out slave.out master.out: master.cpp ...
- 进程控制之exec函数
用fork函数创建子进程后,子进程往往要调用一种exec函数以执行另一个程序.当进程调用一种exec函数时,该进程执行的程序完全替换为新程序,而新程序则从其main函数开始执行.因为调用exec并不创 ...
- linux exec函数家族
1.exec家族一共有六个函数,分别是: (1)int execl(const char *path, const char *arg, ......); (2)int execle(const ch ...
随机推荐
- 关于Java(介绍)
基于Java官方指导文档,开展学习 Java是什么 Java 是编程语言,也是一个平台 特性 简单 可移植 面向对象 分布式运算高性能 健壮 安全 动态 体系结构中立 记忆宫殿:恋爱是件简单的事,但放 ...
- 一周一话题之一(EF-CodeFirst、MEF、T4框架搭建学习)
本话题是根据郭明峰博客<MVC实用架构系列>的搭建学习总结. -->目录导航 一.数据仓储访问的构建 1.UnitOfWork的构建 2.Repository的构建 ...
- ANDROID_MARS学习笔记_S02_015_Gson解析json串为对象集合
package com.example.s02_e12_json3; import java.lang.reflect.Type; import java.util.Iterator; import ...
- POP3、SMTP、IMAP和Exchange的区别在哪里?
著作权归作者所有.商业转载请联系作者获得授权,非商业转载请注明出处.作者:永遇乐链接:http://www.zhihu.com/question/24605584/answer/29039357来源: ...
- 学会使用git
廖雪峰Git教程 这个教程较为简单,循序渐进 易百Git教程 较为系统 在线代码格式化 可以下载全球最大视频网站的视频支持搜索 点这里
- MySQL查询原理及其慢查询优化案例分享(转)
MySQL凭借着出色的性能.低廉的成本.丰富的资源,已经成为绝大多数互联网公司的首选关系型数据库.虽然性能出色,但所谓“好马配好鞍”,如何能够更 好的使用它,已经成为开发工程师的必修课,我们经常会从职 ...
- javascript外部使用
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/ ...
- bzoj3524 bzoj2223
bzoj3524这是我主席树的第一题 什么是主席树呢,就是n棵线段树,每棵线段树记录前缀每个数值(显然想到里离散化)出现次数 由于第i棵树相对于第i-1棵树只有logS个点不同(S为数值的种类数) 所 ...
- Modifying the ASP.NET Request Queue Limit
Modifying the ASP.NET Request Queue Limit When ASP.NET is queried, the request for service is carrie ...
- JavaScript高级程序设计53.pdf
共有的表单字段方法 每个表单字段都有两个方法:focus()和blur(),其中focus()用于将浏览器焦点设置到表单字段,激活表单字段.可以侦听页面的load事件 EventUtil.addHan ...