Chinaunix首页 | 论坛 | 博客
  • 博客访问: 85889
  • 博文数量: 17
  • 博客积分: 318
  • 博客等级: 二等列兵
  • 技术积分: 256
  • 用 户 组: 普通用户
  • 注册时间: 2008-11-07 21:53
文章分类

全部博文(17)

文章存档

2011年(17)

我的朋友

分类: LINUX

2011-12-27 20:38:18

pit_t fork(void)

注意点:

1.fork()结束后,会有两个进程存在。在父进程中,fork()返回子进程的ID,错误返回-1。在子进程中,fork()返回0

2.两个进程执行相同的程序段,但是栈、堆、数据段是各自拥有的(最初是共享的)。子进程的栈、堆、数据段最初都是从父进程拷贝的。

3.进程可以调用getpid()得到自己的ID,调用getppid()获得父进程的ID

4.fork()返回-1很有可能是该用户拥有的进程到达上限,或整个系统的进程到达上限。

5.fork()执行后,不能确定哪个进程会被先调度到(和线程相同)。像在父进程中使用sleep()来使子进程先运行的这种方法是不可靠的。

6.fork()的典型使用方法:

  1. pid_t childpid;

  2. switch (childpid = fork()) {

  3. case -1:

  4.        /*handle error*/

  5. break;

  6. case 0;

  7.        /*perform actions specific to child*/

  8. break;

  9. default:

  10.        /*perform actions specific to parent*/

  11. }

 

父进程和子进程文件的共享

当调用fork()后,子进程会复制父进程的文件描述符。该描述符包含了打开的文件和当前文件的偏移量(read(),write(),lseek()会修改该offset),和文件相关的标志。

父进程和子进程内存的共享

在早期linux中, fork()在创建子进程时会整个拷贝父进程的虚拟页表,这种方式是相当费时的,特别是调用fork()后,紧接着再调用会替代子进程整个空间的exec()函数,这时拷贝虚拟页表就显得多余了。

现代linux采用下面两种技术来避免在创建子进程时就马上拷贝虚拟页表:

1.内核把进程的代码段设置为只读,父进程和子进程都共享该段。

2.对于在数据段,堆,栈中的页(pages),内核使用写时复制(copy-on-write)。在最开始,内核让父子进程共享这些段中的页,并把这些页设置为可读。当任意一个进程要修改其中一个页时,内核负责把该页复制,然后让要修改该页的进程中的page-table entry指向复制后的页。

pid_t vfork(void)(该函数已被废弃)

注意点:

1.这是BSD实现的系统调用。它的出现是为了弥补fork()(未实现copy-on-write)性能上的缺失。通常在vfork()之后,会继续调用exec()

2.vfork()函数不会对父进程的页表和页进行复制,父子进程共享相同的页。当子进程调用exec()_exit()时,子进程才会创建自己的页表。如果子进程在vfork()exec()/_exit()之间返回了,这将会影响父进程(父进程中的资源和堆栈都被清除了)。

4.文件描述表在vfork()中会被复制。这就意味着子进程在vfork()exec()之间调用操作文件描述符的API不会影响到父进程。

5.父进程在执行vfork()后,会一直挂起,直到子进程执行了exec()_exit()。这保证了子进程在父进程之前被调度。

 

创建进程后的竞争

先调度父进程还是子进程?

fork()后到底是先调度父进程(parent first after fork)还是子进程(children first after fork),这个没有定论。children first after fork是基于以下考虑:当fork()后,子进程紧接着调用exec()。而父进程在fork()后,马上修改数据。如果先调度父进程,那么内核会因为父进程需要修改共享的数据页,而把该页先复制,然后让父进程的页表中某一项指向该页。在完成上面一系列操作后,父进程才能修改数据。接下来调度子进程,exec()会把子进程的整个空间用其他程序替代。所以,如果先调度子进程,那么父进程中的复制根本没必要。

 

parent first after fork是基于以下考虑:当父进程执行fork()后,进程的状态都已经存在于CPU中,内存的管理信息也已近被缓冲进了TLB。如果这时切换到子进程,那么所有在TLB的数据很有可能无效,这样CPU又会重新装载TLB,这会浪费时间。所以,先调度父进程比较好。

 

Linux为了满足这两种情况,提供了/proc/sys/kernel/sched_child_runs_first参数来让管理员手动修改。

用信号进行同步

注意:我们在写程序时,始终认为fork()之后的父子进程调度顺序都是随机的。如果必须在程序中保持某种顺序,我们必须使用同步技术(Synchronization Technique)。同步技术有很多种比如信号量(Semaphores),文件锁(File Locks),用Pipes发送信息。在这里将展示怎么用信号来进行进程同步。

 

  1. #include <signal.h>

  2. #include <stdlib.h>

  3. #include <stdio.h>

  4. #include <errno.h>

  5. #include <unistd.h>

  6.  

  7. #define SYNC_SIG SIGUSR1

  8.  

  9. static void handler(int sig)

  10. {

  11.        printf("receive sig %d \n", sig);

  12. }

  13.  

  14. int main(int argc, char *argv[])

  15. {

  16.        pid_t childpid;

  17.        sigset_t block_mask, orig_mask, empty_mask;

  18.        struct sigaction sa;

  19.  

  20.        sigemptyset(&block_mask);

  21.        sigaddset(&block_mask, SYNC_SIG);

  22.        if (sigprocmask(SIG_BLOCK, &block_mask, &orig_mask) == -1)

  23.               exit(-1);

  24.  

  25.        sigemptyset(&sa.sa_mask);

  26.        sa.sa_flags = SA_RESTART;

  27.        sa.sa_handler = handler;

  28.        if (sigaction(SYNC_SIG, &sa, NULL) == -1)

  29.               exit(-1);

  30.       

  31.        switch (childpid = fork()) {

  32.        case -1:

  33.               exit(-1);

  34.        case 0:/*child*/

  35.               sleep(2);

  36.               if (kill(getppid(), SYNC_SIG) == -1)

  37.                      exit(-1);

  38.               printf("child process send sig %d \n", SYNC_SIG);

  39.               _exit(0);

  40.               break;

  41.        default:/*parent*/

  42.               printf("parent about to wait for signal\n");

  43.               sigemptyset(&empty_mask);

  44.               if (sigsuspend(&empty_mask) == -1 && errno != EINTR)

  45.                      exit(-1);

  46.  

  47.               if (sigprocmask(SIG_SETMASK, &orig_mask, NULL) == -1)

  48.                      exit(-1);

  49.  

  50.               exit(0);

  51.        }

  52. }

阅读(4100) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~