关于fork函数

  一、怎么用fork

  fork是分叉,走叉路的意思。在代码时常常需要在一个进程中创建另一个新的进程,fork就是用来创建新进程的。创建完成之后原来的进程管它叫它父进程,而新的进程叫做子进程。其实父子是相对的,子有时候也会是父。

  关于fork()的一些说明可以参考一下这个:http://man.he.net/?topic=fork&section=all

  下面看一段代码

 1 /*****************************************    
 2 filename:    testfork.c
 3 Author:        zhouyoulie
 4 date:        2013.05.27
 5 *****************************************/
 6 #include <stdlib.h>
 7 #include <stdio.h>
 8 #include <sys/types.h>
 9 #include <unistd.h>
10 int main()
11 {
12     pid_t pid;
13     int count = 0;
14     printf("Before fork,process id is:%d\n",getpid());
15     pid = fork();
16     if( pid < 0 )
17     {
18         printf("Fork Failure!\n");
19     }
20     else if( pid == 0 )
21     {
22         printf("This is son process,and my own ID parent ID are:%d and %d\n",getpid(),getppid());
23         count++;
24     }
25     else if( pid > 0 )
26     {
27         printf("the back pid is:%d\n",pid);
28         printf("This is parent process,and my own ID parent ID are:%d and %d\n",getpid(),getppid());
29         count++;
30     }
31 
32     printf("count equals %d\n",count);
33     exit(0);
34 }
View Code1

  通过这段代码应该可以初略的了解fork的使用方法了。先说明两个函数:getpid()使用来获取当前进程的ID的,getppid()是用来获取当前进程的父进程ID。代码运行的结果是这样的

              

图1

  在调用fork之前本进程的ID是7301,调用fork之后返回7302,父进程和它自己的父进程ID分别为7301和4893,子进程和其父进程ID分别为7302和7301。结合上面的代码和运行结果不难看出fork被调用之后其实返回两次,返回的进程ID大于零说明该进程为父进程(其实返回的是他的子进程的ID,上图结果中的7302),返回值为零说明是子进程。那么两次count都为1说明什么呢?说明两个进程都独自拥有自己的count,实际上在生成子进程是就相当于克隆了一个自己,这种克隆包括数据、代码以及分配给进程的资源。

  二、fork的内部实现

  在用户态模式下调用fork时实际上调用的是C库提供的fork函数,在C库中封装了系统调用fork。在最新的内核代码linux3.9.3的

linux-3.9.3\include\uapi\asm-generic\unistd.h头文件中可查看相关系统调用号的定义,并且从unistd.h中还可以知道fork的系统调用例程为sys_fork,见图2

            

图2

  通过sys_fork可以追踪到linux-3.9.3\kernel\Fork.c,有如下实现

            

                                    图3

  在上述kernel实现里调用了do_fork,do_fork又是做什么的呢?继续查找代码可以觅得如下实现

 1 long do_fork(unsigned long clone_flags,
 2           unsigned long stack_start,
 3           unsigned long stack_size,
 4           int __user *parent_tidptr,
 5           int __user *child_tidptr)
 6 {
 7     struct task_struct *p;
 8     int trace = 0;
 9     long nr;
10 
11     /*
12      * Do some preliminary argument and permissions checking before we
13      * actually start allocating stuff
14      */
15     if (clone_flags & (CLONE_NEWUSER | CLONE_NEWPID)) {
16         if (clone_flags & (CLONE_THREAD|CLONE_PARENT))
17             return -EINVAL;
18     }
19 
20     /*
21      * Determine whether and which event to report to ptracer.  When
22      * called from kernel_thread or CLONE_UNTRACED is explicitly
23      * requested, no event is reported; otherwise, report if the event
24      * for the type of forking is enabled.
25      */
26     if (!(clone_flags & CLONE_UNTRACED)) {
27         if (clone_flags & CLONE_VFORK)
28             trace = PTRACE_EVENT_VFORK;
29         else if ((clone_flags & CSIGNAL) != SIGCHLD)
30             trace = PTRACE_EVENT_CLONE;
31         else
32             trace = PTRACE_EVENT_FORK;
33 
34         if (likely(!ptrace_event_enabled(current, trace)))
35             trace = 0;
36     }
37 
38     p = copy_process(clone_flags, stack_start, stack_size,
39              child_tidptr, NULL, trace);
40     /*
41      * Do this prior waking up the new thread - the thread pointer
42      * might get invalid after that point, if the thread exits quickly.
43      */
44     if (!IS_ERR(p)) {
45         struct completion vfork;
46 
47         trace_sched_process_fork(current, p);
48 
49         nr = task_pid_vnr(p);
50 
51         if (clone_flags & CLONE_PARENT_SETTID)
52             put_user(nr, parent_tidptr);
53 
54         if (clone_flags & CLONE_VFORK) {
55             p->vfork_done = &vfork;
56             init_completion(&vfork);
57             get_task_struct(p);
58         }
59 
60         wake_up_new_task(p);
61 
62         /* forking complete and child started to run, tell ptracer */
63         if (unlikely(trace))
64             ptrace_event(trace, nr);
65 
66         if (clone_flags & CLONE_VFORK) {
67             if (!wait_for_vfork_done(p, &vfork))
68                 ptrace_event(PTRACE_EVENT_VFORK_DONE, nr);
69         }
70     } else {
71         nr = PTR_ERR(p);
72     }
73     return nr;
74 }
View Code2

  在do_fork中又调用了copy_process函数。所以可以总结一下用户态与产生新进程的整体流程:

                  

                            图3

  

转载于:https://www.cnblogs.com/zhouyoulie/archive/2013/05/28/3104001.html

<think>好的,我需要回答用户关于fork函数在哪个头文件中定义的问题。用户提到了C/C++和POSIX,所以我需要确定正确的头文件。 首先,fork函数是POSIX标准的一部分,主要用于创建进程。在C语言中,POSIX相关的函数通常位于<unistd.h>头文件中。我记得之前学过的进程管理部分,fork和exec系列函数都在这里声明。 接下来,查看用户提供的引用内容。引用[1]提到了execvp函数在<unistd.h>中定义,而引用[4]讨论了fork函数如何创建进程。虽然没有直接提到fork的头文件,但execvp和fork都属于进程控制函数,通常在同一头文件里。因此可以推断fork也在<unistd.h>中。 用户还提到C++标准库和POSIX库的区别。C++标准库可能包含不同的头文件,但fork是POSIX特有的,所以C++中同样使用<unistd.h>,但需要注意这是C标准库的一部分,不是C++独有的。 需要确认是否还有其他可能的头文件。比如,有些系统可能有不同的惯例,但POSIX标准明确规定了<unistd.h>,所以应该以这个为准。用户提供的引用[3]提到了<fcntl.h>,但那是文件控制相关的,和进程创建无关。 总结起来,fork函数在C/C++的POSIX环境下定义于<unistd.h>头文件中。需要确保用户知道这属于POSIX标准,可能在非Unix系统上不可用。</think>在C/C++中,`fork`函数属于**POSIX标准库**的进程控制函数,其定义位于头文件`<unistd.h>`[^4]。该头文件是Unix/Linux系统编程中用于声明系统调用和POSIX API的核心头文件之一。 ### 说明 1. **适用场景**: - 仅适用于遵循POSIX标准的系统(如Linux、macOS、BSD等)。 - 在Windows系统中不可直接使用,需通过跨平台库(如Cygwin、WSL)模拟POSIX环境。 2. **函数原型**: ```c #include <unistd.h> pid_t fork(void); ``` - `fork`会创建当前进程的子进程,返回`pid_t`类型的进程ID。 3. **相关引用**: - `exec`系列函数(如`execvp`)也定义在`<unistd.h>`中[^1]。 - 进程终止函数`exit`和`_exit`的清理行为差异可参考进程生命周期管理。 ### 代码示例 ```c #include <stdio.h> #include <unistd.h> // 必须包含此头文件 int main() { pid_t pid = fork(); if (pid == 0) { printf("子进程\n"); } else { printf("父进程\n"); } return 0; } ``` ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值