精华内容
下载资源
问答
  • fork系统调用

    2016-09-01 17:10:36
    (1) fork系统调用说明 fork系统调用用于从已存在进程中创建一个新进程,新进程称为子进程,而原进程称为父进程。fork调用一次,返回两次,这两个返回分别带回它们各自的返回值,其中在父进程中的返回值是子进程...

    转载:http://blog.csdn.net/guoping16/article/details/6580006

    (1) fork系统调用说明

    fork系统调用用于从已存在进程中创建一个新进程,新进程称为子进程,而原进程称为父进程。fork调用一次,返回两次,这两个返回分别带回它们各自的返回值,其中在父进程中的返回值是子进程的进程号,而子进程中的返回值则返回 0。因此,可以通过返回值来判定该进程是父进程还是子进程。

    使用fork函数得到的子进程是父进程的一个复制品,它从父进程处继承了整个进程的地址空间,包括进程上下文、进程堆栈、内存信息、打开的文件描述符、信号控制设定、进程优先级、进程组号、当前工作目录、根目录、资源限制、控制终端等,而子进程所独有的只有它的进程号、计时器等。因此可以看出,使用fork系统调用的代价是很大的,它复制了父进程中的数据段和堆栈段里的绝大部分内容,使得fork系统调用的执行速度并不很快。

    fork的返回值这样设计是有原因的,fork在子进程中返回0,子进程仍可以调用getpid函数得到自己的进程ID,也可以调用getppid函数得到父进程的进程ID。在父进程中使用getpid函数可以得到自己的进程ID,然而要想得到子进程的进程ID,只有将fork的返回值记录下来,别无它法。

    fork的另一个特性是所有由父进程打开的文件描述符都被复制到子进程中。父、子进程中相同编号的文件描述符在内核中指向同一个file结构体,也就是说,file结构体的引用计数要增加。

       由于代码段(加载到内存的执行码)在内存中是只读的,所以父子进程可共用代码段,而数据段和堆栈段子进程则完全从父进程复制拷贝了一份。

     

    (2)父进程进行fork系统调用时完成的操作

          假设id=fork(),父进程进行fork系统调用时,fork所做工作如下:

    ①    为新进程分配task_struct任务结构体内存空间。

    ②    把父进程task_struct任务结构体复制到子进程task_struct任务结构体。

    ③    为新进程在其内存上建立内核堆栈。

    ④    对子进程task_struct任务结构体中部分变量进行初始化设置。

    ⑤    把父进程的有关信息复制给子进程,建立共享关系。

    ⑥    把子进程加入到可运行队列中。

    ⑦    结束fork()函数,返回子进程ID值给父进程中栈段变量id。

    ⑧    当子进程开始运行时,操作系统返回0给子进程中栈段变量id。

    (3)fork调用时所发生的事情

    下面代码是fork函数调用模板,fork函数调用后常与if-else语句结合使用使父子进程执行不同的流程。假设下面代码执行时产生的是X进程,fork后产生子进程的是XX进程,XX进程的进程ID号为1000。

        int pid ;

    pid = fork();

        if (pid < 0) {

            perror("fork failed");

            exit(1);

        }

        if (pid == 0) {

            message = "This is the child/n";   

    调用fork前,内存中只有X进程,如图12-9所示,此时XX进程还没“出生”。

         

      图12-9 fork前的内存

           调用fork后,内存中不仅有X进程(父进程),还有XX进程(子进程)。fork的时候,系统几乎把父进程整个堆栈段(除代码段,代码段父子进程是共享的)复制给了子进程,复制完成后,X进程和XX进程是两个独立的进程,如下图12-10所示,只不过X进程栈中变量id值此时为1000,而XX进程栈中变量id值为0。fork调用完成后,X进程由系统态回到用户态。此后,X进程和XX进程各自都需要从自己代码段指针指向的代码点继续往下执行,父进程X往下执行时判断id大于0,所以执行大于0的程序段,而子进程XX往下执行时判断id等于0,所以执行等于0的程序段。

     

      图12-10 fork后的内存

    (4)fork 函数原型

    所需头文件

    #include <sys/types.h>   // 提供类型 pid_t 的定义

    #include <unistd.h>

    函数说明

    建立一个新的进程

    函数原型

    pid_t fork(void)

    函数返回值

    0:返回给子进程

    子进程的ID(大于0的整数):返回给父进程

    -1:出错,返回给父进程,错误原因存于errno中

    错误代码

    EAGAIN:内存不足

    ENOMEM:内存不足,无法配置核心所需的数据结构空间

      

    (5)fork函数使用实例

    fork.c源代码如下:

    #include <sys/types.h>

    #include <sys/wait.h>

    #include <unistd.h>

    #include <stdio.h>

    #include <stdlib.h>

    int main(void)

    {

        pid_t pid;

        char *message;

        int n;

        pid = fork();

        if (pid < 0) {

            perror("fork failed");

            exit(-1);

        }

        if (pid == 0) {

            message = "This is the child\n";

            n = 3;

        } else {

            wait(0) ; /*阻塞等待子进程返回*/

            message = "This is the parent\n";

            n = 1;

        }

        for(; n > 0; n--) {

            printf(message);

            sleep(1);

        }

        return 0;

    }

    编译 gcc fork.c –o fork。

    执行./fork,执行结果如下:

    This is the child

    This is the child

    This is the child

    This is the parent

    读者可以把sleep(1)改成sleep(30),然后通过ps -ef|grep fork查看进程数。

    (6)fork后程序处理的两种情形

            一种为父进程希望复制自己,使父、子进程同时执行不同的代码段。这是网络并发服务端常见的模型,父进程等待客户端的服务请求,当这种请求到达时,父进程调用fork,让子进程处理此请求,父进程则继续等待下一个服务请求。

           另一种为fork后通过exec执行另一个程序,在终端上执行命令属于这种情况,Shell进程fork后立即调用exec去执行执行命令。

     (7)fork之后处理文件描述符有两种常见情况

    父进程等待子进程完成。在这种情况下,父进程无需对其文件描述符做任何处理,当子进程终止后,它曾进行过读、写操作的任一共享描述符的文件位移量已做了相应更新。

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

     

    (8)除了打开文件之外,很多父进程的其他性质也由子进程继承

    Ÿ      实际用户ID、实际组ID、有效用户ID、有效组ID;

    Ÿ      附加组ID;

    Ÿ      进程组ID;

    Ÿ      会话ID;

    Ÿ      控制终端;

    Ÿ      设置-用户-ID标志和设置-组-ID标志;

    Ÿ      当前工作目录;

    Ÿ      根目录;

    Ÿ      文件权限屏蔽字;

    Ÿ      信号屏蔽和排列;

    Ÿ      打开的文件描述符;

    Ÿ      环境变量;

    Ÿ      连接的共享存储段;

    Ÿ      数据段、代码段、堆段、.bss段;

    Ÿ      资源限制。

     

    (9)父、子进程之间有如下区别

    Ÿ      fork的返回值;

    Ÿ      进程ID;

    Ÿ      不同的父进程ID;

    Ÿ      子进程的tms_utime、tms_stime、tms_cutime以及tms_ustime设置为0;

    Ÿ      父进程设置的锁,子进程不继承;

    Ÿ      未处理的闹钟信号子进程将清除;

    Ÿ      子进程的未决告警被清除;

    Ÿ      子进程的未决信号集设置为空集。

    (10)fork与vfork的区别

           使用fork调用会为子进程复制父进程所拥有的资源(进程环境、栈堆等),而vfork设计时要求子进程立即调用exec,而不修改任何内存,vfork新建的子进程则是和父进程共享所有的资源,在子进程中对数据的修改也就是对父进程数据的修改,这一点一定要注意。

           使用fork系统调用产生父子进程,在默认情况下无需人为干预,父子进程的执行顺序是由操作系统调度的,谁先执行并不确定。而对于vfork所生成的父子进程,父进程是在子进程调用了_exit或者exec后才会继续执行,不调用这两个函数父进程会等待(父进程由于没有收到子进程表示已执行的相关信号所以进行等待)。

           vfork的出现是为了解决当初fork浪费用户空间内存的问题,因为在fork后,很有可能去执行exec函数重生,vfork设计思想就是取消fork造成堆栈的复制,使用vfork可以避免资源的浪费,但是也带了资源共享所产生的问题。

            在Linux中,对fork进行了优化,调用时采用写时复制 (COW,copy on write)的方式,在系统调用fork生成子进程的时候,不马上为子进程复制父进程的资源,而是在遇到“写入”(对资源进行修改)操作时才复制资源。它使得一个普通的fork调用非常类似于vfork,但又避免了vfork的缺点,使得vfork变得没有必要。      

         

              摘录自《深入浅出Linux工具与编程》

    展开全文
  • fork 系统调用

    2015-01-06 18:23:33
    包含头文件 和 函数功能:创建一个子进程... pid_t fork(void); 参数:无参数。 返回值: 如果成功创建一个子进程,对于父进程来说返回子进程ID 如果成功创建一个子进程,对于子进程来说返回值为0 如果为-1表示创建失败
    包含头文件 <sys/types.h> 和 <unistd.h>
    函数功能:创建一个子进程
    函数原型
             pid_t  fork(void);
    参数:无参数。
    返回值:
    如果成功创建一个子进程,对于父进程来说返回子进程ID
    如果成功创建一个子进程,对于子进程来说返回值为0

    如果为-1表示创建失败

    注释 :fork一次调用两次返回

               fork成功意味着创建了一个进程副本,两个进程,他们是在各自的进程地址空间中返回的。


    /*
     * 01fork.cpp
     *
     *  Created on: Aug 22, 2014
     *      Author: zfs
     */
    
    #include <unistd.h>
    #include <sys/stat.h>
    #include <sys/types.h>
    #include <sys/stat.h>
    #include <fcntl.h>
    
    #include <stdlib.h>
    #include <stdio.h>
    #include <errno.h>
    #include <string.h>
    #include <unistd.h>
    #include <signal.h>
    #define ERR_EXIT(m) \
            do \
            { \
                    perror(m); \
                    exit(EXIT_FAILURE); \
            } while(0)
    
    int main(int argc, char *argv[]) {
    	/*signal(SIGCHLD,SIG_IGN);//解决僵尸进程的出现*/
    	printf("before fork pid=%d\n", getpid());
    	pid_t pid;
    	pid = fork();
    	if (pid == -1)
    		ERR_EXIT("PORK ERROR");
    	if (pid == 0) {
    		printf("这是子进程中运行空间中...");
    		printf("this is childpid=%d  parentpid=%d\n", getpid(), getppid());
    
    	} else if (pid > 0) {
    		printf("这是父进程中运行空间中...");
    		printf("this is parentpid=%d childpid=%d\n", getpid(), pid);
    	    //sleep(10);//可以让父进程10秒后退出,避免出现子进程空间运行时的父进程为1de情况.
    	}
    	return 0;
    }
    


    如果加上sleep(10)秒后会出现僵尸进程,即子进程先退出,父进程尚未查询子进程的退出状态,子进程就处于僵尸状态即不活跃状态







    fork 系统调用注意点


    1、fork系统调用之后,父子进程将交替执行。
    2、(1)如果父进程先退出,子进程还没退出那么子进程的父进程将变为init进程。此时的进程变为孤儿进程,即托孤                 给1号进程

    (注:任何一个进程都必须有父进程)
        (2)如果子进程先退出(子进程退出时会向父进程发送信号),父进程还没退出,那么子进程必须等到父进程捕获到了子进程的退出状态才真正结束,否则这个时候子进程就成为僵进程。

    展开全文
  • fork系统调用 fork系统调用用于创建进程 fork创建的进程初始化状态与父进程一样(对于进程有进程空间,包括内存以及内核态等,初始化时一模一样,包括他们拥有的变量、逻辑空间等) 系统为fork进程分配新的资源...

    fork系统调用

    • fork系统调用用于创建进程
    • fork创建的进程初始化状态与父进程一样(对于进程有进程空间,包括内存以及内核态等,初始化时一模一样,包括他们拥有的变量、逻辑空间等)
    • 系统为fork进程分配新的资源(包括CPU、内存资源等)

    fork系统调用没有参数,fork调用一次会返回两次,分别是子进程id是父进程,返回的0是子进程。返回两次是因为当时有fork系统调用的时候,子进程初始化和父进程一模一样,子进程也拥有fork的逻辑,所以会返回两次。第一次由父进程所返回,第二次由子进程所返回。

    fork_demo

    //
    // Created by 文若 on 2020/9/5.
    //
    
    #include <iostream>
    #include <unistd.h>
    
    using namespace std;
    
    int main() {
        // 定义进程id
        pid_t pid;
        int num = 666;
        pid = fork();
    
        // 对返回进行判断
        if (pid == 0) {
            cout << "这是一个子进程||";
            cout << "子进程中的num:" << num << endl;
            for (int i = 0; i < 5; ++i) {
                num += 1;
                cout << num << " ";
            }
        } else if (pid > 0) {
            cout << "这是一个父进程||" << "子进程id" << pid << "||父进程中的num:" << num << endl;
            for (int i = 0; i < 5; ++i) {
                num -= 1;
                cout << num << " ";
            }
            cout << endl;
        } else {
            cout << "创建进程失败" << endl;
        }
        return 0;
    }
    
    
    

    输出结果:

    这是一个父进程||子进程id2926||父进程中的num:666
    665 664 663 662 661 
    这是一个子进程||子进程中的num:666
    667 668 669 670 671 
    

    打印结果分析

    1. 虽然进行了条件判断,仍旧输出了父进程和子进程两种情况,说明fork函数确实返回两次。
    2. num的初始值在父进程和子进程中一样,说明了fork调用初始化状态一模一样
    3. 但是子进程不继承父进程,可以看到父子进程分别对num进行不同的逻辑操作,他们的运算结果也是不同的。
    展开全文
  • Linux中fork系统调用

    2017-05-16 09:17:25
    1. fork系统调用头文件:; 2. fork系统调用的原型:pid_t fork(); 3. fork系统调用的返回值:pid_t是进程描述符类型,本质就是一个int。如果fork函数执行失败,返回一个负数( 4. fork系统调用的功能:以当前...

    1. fork系统调用头文件:<unistd.h>;

    2. fork系统调用的原型:pid_t fork();

    3. fork系统调用的返回值:pid_t是进程描述符类型,本质就是一个int。如果fork函数执行失败,返回一个负数(<0);如果fork调用执行成功,返回两个值:0和所创建子进程的ID。

    4. fork系统调用的功能:以当前进程作为父进程创建出一个新的子进程,并且将父进程的所有资源拷贝给子进程,这样子进程作为父进程的一个副本存在。父子进程几乎时完全相同的,但也有不同的如父子进程ID不同。

    5. 注意:当fork系统调用成功时,它会返回两个值:一个是0,另一个是所创建的新的子进程的ID(>0)。当fork成功调用后此时有两个数据相同的父子进程,我们可以通过fork的返回值来判断接下来程序是在执行父进程还是子进程。

    pid_t pid = fork();
    if(pid == 0)
    {
          //在子进程中
    }
    else if(pid > 0)
    {
          //在父进程中
    }
    else
    {
         //fork调用失败
    }
    6. fork调用与文件描述符的关系

    前面我们知道当我们使用fork系统调用以当前进程作为父进程创建出了一个子进程,父进程将自己的在fork系统调用之前的所有数据和代码都复制一份给子进程,此时子进程相当于父进程的一个拷贝。对于在fork调用之前打开的文件也一样,也会做一个拷贝将文件描述符复制给子进程,这样父子进程可以同时对一个文件进行操作。但是这种操作是共享式的,父子进程共享这个文件描述符所索引的文件。何谓父子进程共享这个文件呢?举个简单例子说明下

    如果一个已经打开的txt文件,里边的内容是一个字符串“abcdefg”。在父进程中读取前两个字符“ab”,此时文件指针指向c。如果在子进程中再次读取两个字符,那么它读取就是“cd”,并且读完后文件指针指向e。这就是父子进程以共享的方式来操作文件。下面我们通过代码说明这个关系:

    #include<stdio.h>
    #include<stdlib.h>
    #include<unistd.h>
    #include<iostream>
    #include<pthread.h>
    #include<fcntl.h>//为了使用O_RD_ONLY
    int gb = 10;
    using namespace std;
    int main()
    {
    	int fd = open("fork.txt", O_RDONLY);
    	if(fd < 0)//文件描述符是一个非负整数:0是stdin的文件描述符,1是stdout的文件描述符,2是stderr的文件描述符
    	{
    		cout<<"error in open the file!"<<endl;
    		exit(1);
    	}
    	cout<<"文件描述符为:"<<fd<<endl;
    	char *c = new char[3];
    	int a = 5;
    	cout<<"main函数中的进程ID:"<<getpid()<<endl;
    	pid_t pid = fork();//fork系统调用的作用是:使用当前进程作为副本创建出一个子进程。它会返回两个值,一个是子进程的ID,一个是0
    	if(pid == 0)//在子进程中
    	{
    		read(fd, c, 2);
    		c[2] = '\0';
    		cout << "---------------------------------------------"<<endl;
    		cout << c << endl;
    		gb += 10;
    		a += 10;
    		cout << "gb = " << gb << ",a = " << a << endl;
    		cout << "子进程ID:" << getpid() << ",子进程的父进程ID为:" << getppid() << endl;
    		cout << "子进程中pthread_self():" << pthread_self() << endl;//因为两个pthread_self()函数都是在主线程中的,所以两个获取的线程ID相同
    	}
    	else if(pid > 0)//在当前进程中,也就是父进程中
    	{
    		read(fd, c, 2);
    		c[2] = '\0';
    		cout << "--------------------------------------------"<<endl;
    		cout << c << endl;
    		cout << "gb = " << gb << ",a = " << a << endl;
    		cout << "父进程ID:" << getpid() << ",父进程的父进程ID为:" << getppid() << endl;
    		cout<<"父进程中pthread_self():"<<pthread_self()<<endl;//因为两个pthread_self()函数都是在主线程中的,所以两个获取的线程ID相同
    	}
    	else
    	{
    		cout<<"fork error!"<<endl;
    		exit(1);
    	}
    	close(fd);//关闭文件描述符对应的文件,如果关闭成功返回0,关闭失败返回-1
    	return 0;
    }
    在linux中执行结果:

     

    这里我们使用open系统调用打开了一个名为fork.txt的文件,文件中的是一个个字符串。
     (1)文件描述符为3:因为系统中0时stdin的文件描述符,1是stdout的文件描述符,2是stderr的文件描述符。其他的用于索引我们自己打开的文件描述符,每打开一个一个文件就是使用最小的可使用的文件描述符表示。如果再打开一个文件,他的fd是4.

    (2)使用getpid()函数可以获取当前进程的ID,使用getppid()可以获取当前进程的父进程的ID,使用pthread_self()可以获取当前线程的ID。

    (3)通过结果可以看出我们使用fork创建了子进程并在子进程中对变量gb和a做了修改,但是这个修改并不会影响到父进程中两个变量的值。这是因为fork系统调用将父进程中的变量拷贝一份给子进程中。父子进程中的数据各不相关。

    (4)在父进程中使用read系统调用读取文件的前两个字符ab。然后在子进程中再次读取两个字符是cd,而不是ab。这说明父子进程共享同一个文件,它们对文件的操作是同步的。

    展开全文
  • 下一个我想查看的例子叫做fork。fork会创建一个新的进程,下面是使用fork...fork系统调用在两个进程中都会返回,在原始的进程中,fork系统调用会返回大于0的整数,这个是新创建进程的ID。而在新创建的进程中,fork系...
  • linux下fork系统调用分析。设计do_fork,copy_process函数以及x86体系下系统调用执行过程。更多分析请看edsionte.com.linux下fork系统调用分析。设计do_fork,copy_process函数以及x86体系下系统调用执行过程。更多...
  • 关于fork系统调用的学习最简单的尝试第二个尝试,多个顺序的fork调用 学完了这一章,自己想尝试一下fork具体的执行,于是运行了几个小程序。 最简单的尝试 void fork0() { if (fork() == 0) { printf(...
  • linux fork系统调用

    千次阅读 2012-09-17 21:08:36
    fork系统调用是内核中相当麻烦的一部分,由于进程数据结构struct task_struct包含了进程运行所需的所有的数据结构,包括虚拟地址空间,文件系统,打开的文件,信号处理程序,sys v实例,命名空间和IO上下文等。fork...
  • 本文为第十五篇,使用fork系统调用创建进程。创建进程属于非常重要的内容,无论是哪种语言,底层在创建进程的时候都是使用fork函数,本文使用C语言来熟悉fork系统调用创建进程 使用fork系统调用创建进程 fork系统...
  • fork系统调用是用于创建进程的。 fork创建的进程初始化状态与父进程一样。 系统会为fork的进程分配新的资源。 fork系统调用无参数。 fork会返回两次,分别返回子进程id和0。 返回子进程id的是父进程,返回0的是子...
  • fork系统调用再次总结(图文版)废话不多说,直接上干货。。。 好困啊
  • 微软研究人员发表论文称用于创建进程的 fork 系统调用方式已经很落后,并且对操作系统的研究与发展产生了极大的负面影响,需要淘汰,作者同时提出了替代方案。相信每位开发者都对操作系统中的 fork () 有一定的了解...
  • 进程控制——fork系统调用学习笔记
  • fork系统调用(转载)

    2015-04-10 14:12:00
    (1) fork系统调用说明 fork系统调用用于从已存在进程中创建一个新进程,新进程称为子进程,而原进程称为父进程。fork调用一次,返回两次,这两个返回分别带回它们各自的返回值,其中在父进程中的返回值是子进程的...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 5,632
精华内容 2,252
关键字:

fork系统调用