您的位置:首页 > 其它

fork()函数

2016-05-02 15:00 204 查看
进程标识:

每个进程有唯一的非负进程ID。

进程ID 0是调度进程,属于内核进程。

进程ID 1是init进程,属于用户进程,但具有超级用户权限。此进程在自举后启动一个UNIX系统,将系统引导到一个状态。init进程不会终止。

进程ID 2是页精灵进程,负责虚拟系统的请页操作。

fork函数:

一个现存进程调用fork是UNIX内核创建一个新进程的唯一方法,除了调度进程、init进程、页精灵进程是由内核作为自举过程创建的。

<span style="font-family:FangSong_GB2312;font-size:18px;"><span style="font-family:FangSong_GB2312;font-size:18px;">#include<sys/types.h>
#include<unistd.h>
pid_t fork(void);</span></span>


由fork创建的进程成为子进程。

fork被调用一次,返回两次。

子进程返回值为0,父进程返回值为子进程ID。

原因:一个进程可以有多个子进程,所以没有一个函数可以获取某个子进程的ID,所以需要返回值来区分。但一个子进程可以调用getppid()获取父进程的进程ID,所以不需要返回值来区分。

fork之后父子进程的执行顺序是不确定的。

子进程和父进程会继续执行fork之后的指令。

父进程打开的描述符都被复制到子进程,父子进程每个相同的描述符都共享一个文件表项。

处理共享文件描述符:

1,父进程等到子进程完成,这种情况下父进程无需对其描述符做任何处理,当子进程终止后,它曾读写过得任意共享描述符的文件位移量都已做了更新。

2,父子进程各自执行不同的程序段。这种情况下,fork之后父子进程各自关闭它们不需使用的文件描述符,并且不干扰对方使用文件描述符,常用与网络服务进程。

fork失败原因:

1系统中已有大量的进程

2该实际用户ID的进程总量超出系统限制。系统规定每个实际用户任一时刻所能拥有的最大进程数。

fork的两种用法:

1父进程希望复制自己,使父子进程执行不同代码段,常见于网络服务进程,父进程等待服务请求,当请求到达,父进程调用fork,让子进程处理请求,父进程则继续等待下一个服务请求。

2一个进程要执行不同的程序。fork返回后,子进程立即调用exec。常见于shell。

写时复制技术:

Linux在使用fork()函数进程创建时,传统fork()的做法是系统把所有的资源复制给新创建的进程,这种方式效率低下。由于所拷贝的数据或别的资源可能可以共享,现在Linux的fork()使用写时拷贝页来实现新进程的创建,它是一种可推迟甚至避免数据拷贝的技术,刚开始时内核并不会复制整个地址空间,而是让父子进程共享地址空间,只有在写时才复制地址空间,使得父子进程都拥有独立的地址空间,即资源的复制是在只有需要写入时才会发生,因此而称之为Copy
on Write(COW)。在此之前都是以读的方式去和父进程共享资源,这样,在页根本不会被写入的场景下,fork()立即执行exec(),无需对地址空间进行复制,fork()的实际开销就是复制父进程的一个页表和为子进程创建一个进程描述符,也就是说只有当进程空间中各段的内存内容发生变化时,父进程才将其内容复制一份传给子进程,大大提高了效率。

那么,这样子进程的物理空间中没有代码,又是如何取指令去执行exec调用的呢?

其实,在fork()之后,exec()之前,子进程和父进程是共享物理空间(内存区)的,子进程的代码段,数据段和堆栈都指向父进程物理空间,即两者的虚拟空间不同,但物理空间其实是同一个,当父进程或者子进程有需要修改段的行为时,再为子进程分配相应段的物理空间。

若不是exec则内核会给子进程的数据段,堆栈段分配相应的物理空间,至此二者各自有各自的物理空间,互不影响,而代码段则继续共享父进程的物理空间,因为两者的代码完全相同;

但如果是因为exec,由于二者的执行的代码不同,则也需为子进程分配代码段的物理空间。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: