fork和exec函数

#include<unistd.h>
pid_t fork(void);
                                                                                                    返回:在子进程中为0,在父进程中为子进程IO,若出错则为-1

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,结果如下:

时间: 2024-11-10 00:13:28

fork和exec函数的相关文章

Linux下Fork与Exec使用

http://www.cnblogs.com/hicjiajia/archive/2011/01/20/1940154.html Linux下Fork与Exec使用 一.引言 对于没有接触过Unix/Linux操作系统的人来说,fork是最难理解的概念之一:它执行一次却返回两个值.fork函数是Unix系统最杰出的成就之一,它是七十年代UNIX早期的开发者经过长期在理论和实践上的艰苦探索后取得的成果,一方面,它使操作系统在进程管理上付出了最小的代价,另一方面,又为程序员提供了一个简洁明了的多进程

一、进程与信号之exec函数system函数

exec函数: 子进程调用exec函数执行另一个程序,exec函数进程完全由新程序代替,替换原有程序正文,数据,堆,栈段 #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

Linux系统编程_9_进程控制之exec 函数

exec函数 当进程调用exec函数时,该进程的执行程序完全的替换为新程序.新程序从它的main函数开始执行: 使用fork函数创建一个子进程后,子进程往往会使用exec函数去执行另一个程序. 注意:调用exec函数并不会创建新进程,所以创建前后的进程ID不会改变,exec只是用一个全新的程序替换了当前正在运行的程序的代码段.数据段.堆.栈. #include <unistd.h> extern char **environ; int execl(const char *path, const

fork()函数 —— 父子进程资源问

fork()函数功能--创建新进程 1.父子进程有独立的数据段.堆.栈,共享代码段 Linux中每个进程都有4G的虚拟地址空间(独立的3G用户空间和共享的1G内核空间),fork()创建的子进程也不例外.子进程资源的由来: 1.1G内核空间既然是所有进程共享,因此fork()创建的子进程自然也将拥有: 2.3G的用户空间是从父进程进程而来. fork()创建子进程时继承了父进程的数据段.代码段.栈段.堆,注意从父进程继承来的是虚拟地址空间,同时也复制了页表(没有复制物理块).因此,此时父子进程拥

fork() 和exec() 的区别

http://blog.sina.com.cn/s/blog_9d38f2eb01019lsf.html fork()和exec() (2013-03-22 19:36:25) 转载▼ 标签: it 分类: 嵌入式 1.fork()一个程序一调用fork函数,系统就为一个新的进程准备了前述三个段,首先,系统让新的进程与旧的进程使用同一个代码段,因为它们的程序还是相同的,对于数据段和堆栈段,系统则复制一份给新的进程,这样,父进程的所有数据都可以留给子进程,但是,子进程一旦开始运行,虽然它继承了父进

Linux环境编程之进程(五):竞争条件以及exec函数

(一) 当多个进程企图对共享数据进行某种处理,而最后的结果又取决于进程运行的顺序时,就认为它们发生了竞争关系.避免竞争的条件,给出apue上的一个代码吧: #include "apue.h" static void charatatime(char *); int main(void) { pid_t pid; TELL_WAIT(); /*set things up for TELL_XXX & WAIT_XXX*/ if((pid == fork()) < 0){ e

linux下的fork和execve函数使用

fork函数是linux中创建进程的函数,linux创建进程只有用fork,别无他法.我自己写代码fork用的不多,对它的一些细节还不是清楚,今天抽空研究了下fork,把它的一些关键点总结一下,以后用到了自己也好有个参考. 1)fork函数会在父进程中创建子进程,子进程的堆,栈,数据段,PC指针都是从父进程中复制过来的,和父进程是独立的,但是内容是一致的.代码段子进程和父进程是共享的. 2)fork()的返回值可能为-1,0,和一个正数.-1表示fork()调用失败,0表示返回子进程执行结果,正

linux中fork()函数详解

 一.fork入门知识 一个进程,包括代码.数据和分配给进程的资源.fork()函数通过系统调用创建一个与原来进程几乎完全相同的进程,也就是两个进程可以做完全相同的事,但如果初始参数或者传入的变量不同,两个进程也可以做不同的事.    一个进程调用fork()函数后,系统先给新的进程分配资源,例如存储数据和代码的空间.然后把原来的进程的所有值都复制到新的新进程中,只有少数值与原来的进程的值不同.相当于克隆了一个自己. 我们来看一个例子: [cpp] view plain copy /* *  f

linux中fork()函数详解(原创!!实例讲解) (转载)

 一.fork入门知识 一个进程,包括代码.数据和分配给进程的资源.fork()函数通过系统调用创建一个与原来进程几乎完全相同的进程, 也就是两个进程可以做完全相同的事,但如果初始参数或者传入的变量不同,两个进程也可以做不同的事. 一个进程调用fork()函数后,系统先给新的进程分配资源,例如存储数据和代码的空间.然后把原来的进程的所有值都 复制到新的新进程中,只有少数值与原来的进程的值不同.相当于克隆了一个自己. 我们来看一个例子: /* * fork_test.c * version 1 *