• 【Linux实验】fork()创建子进程的一系列测试【进程管理】


    在这里插入图片描述

    #include 
    #include 
    #include 
    int main()
    {
       pid_t pid1, pid2;
       while ((pid1 = fork()) == -1);
       if (pid1 == 0)
       { printf("b");}
       else
       {
          while ((pid2 = fork()) == -1);
          if (pid2 == 0) printf("c");
          else printf("a");
       }
       return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    编译后执行:在这里插入图片描述
    分析:

    • 程序输出为’bac’,其中’b’为pid1的输出,’a’为父进程的输出,’c’为pid2的输出。
    • 首先在执行语句pid1 = fork()时,创建了子进程pid1,此时打印’b’;
    • 然后父进程运行,打印’a’。
    • 最后执行语句pid2 = fork()时,创建了子进程pid2,此时打印’c’;

    程序运行多次,发现结果一致,并未有改变。
    原因可能是操作系统总是按照如上的顺序进行运行。


    修改程序,将每个进程输出一个字符改为每个进程输出一句话,观察分析显示结果

    #include 
    #include 
    #include 
    
    int main()
    {
       pid_t pid1, pid2;
       while ((pid1 = fork()) == -1);
       if (pid1 == 0) printf("pid1 here : b\n");
       else
       {
          while ((pid2 = fork()) == -1);
          if (pid2 == 0) printf("pid2 here : c\n");
          else printf("father progress here : a\n");
       }
       return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    在这里插入图片描述
    结果和之前没有区别,但偶有输出 b c a 可能和操作系统执行顺序有关。

    再试试:

    如果在父进程fork之前,输出一句话,这句话后面不加“\n”或加“\n”,结果有什么不同,为什么?

    #include 
    #include 
    #include 
    
    int main()
    {
       pid_t pid1, pid2;
       printf("This is a sentence.");
       // printf("This is a sentence.\n");
       while ((pid1 = fork()) == -1);
       if (pid1 == 0) printf("pid1 here : b\n");
       else
       {
          while ((pid2 = fork()) == -1);
          if (pid2 == 0) printf("pid2 here : c\n");
          else printf("father progress here : a\n");
       }
       return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 先不加 \n

    在这里插入图片描述

    • 加上 \n

    在这里插入图片描述
    结果分析:

    • 第一种情况每个进程都打印了This is a sentence.这句话,而第二种情况只打印了一次。

    查阅相关资料后进行分析:

    (1)不加’\n’
    由于输出时,我们要将内存中的数据输出到磁盘中,系统会先将要printf的内容存到磁盘缓冲区buffer,也就是将This is a sentence.这句话写入buffer。而子进程会继承父进程的buffer内容,于是在子进程中也会打印这句话,第1个子进程会输出,第2个子进程也会输出。

    (2)加’\n’
    系统检测到“\n”会自动清空缓冲区,于是子进程继承父进程时,缓冲区里面没有要打印这句话的信息,于是在2个子进程中只分别输出c和b。


    修改已经编好的程序(使用fork()创建进程),将每个进程输出一个字符改为每个进程用for循环重复输出一句话,再观察程序执行时屏幕上出现的现象,并分析原因。

    #include 
    #include 
    #include 
    
    int main()
    {
       pid_t pid1, pid2;
       while ((pid1 = fork()) == -1);
       if (pid1 == 0) {
        for(int i=0;i<5;i++){
            printf("pid1 here : b\n");
        }
       }
       else
       {
          while ((pid2 = fork()) == -1);
        if (pid2 == 0) {
            for(int i=0;i<5;i++){
                printf("pid2 here : c\n");
            }
        }else{ 
            for(int i=0;i<5;i++){
            printf("father progress here : a\n");
            }
        }
       }
       return 0;
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29

    在这里插入图片描述
    结果分析:

    • 先输出子进程1,然后父进程和子进程2交替输出
    • 因为fork()创建进程所需要的时间要多于输出一个字符的时间
    • 由于函数printf()输出的字符串之间不会被中断,因此,字符串内部的字符顺序输出时不变。但是,由于进程并发执行时的调度顺序和父子进程的抢占处理机问题,处处字符串的顺序和先后随着执行的不同而发生变化。

    如果在程序中使用系统调用lockf()来给每一个进程加锁,可以实现进程之间的互斥,观察并分析出现的现象。

    如果在程序中使用系统调用lockf来给临界资源加锁,可以实现临界资源的互斥访问。将lockf加在输出语句前后运行试试;将一条输出语句变成多条输出语句,将lockf语句放在循环语句外部或内部试试,观察显示结果并分析原因。

    (一)直接对stdout加锁,发现和之前的输出没有区别,因为进行互斥约束后仍然可以访问stdout中的资源:

    #include 
    #include 
    #include 
    
    int main()
    {
       pid_t pid1, pid2;
       while ((pid1 = fork()) == -1);
       if (pid1 == 0)
       {
          lockf(1, 1, 0);
          printf("pid1 here : b\n");
          lockf(1, 0, 0);
       }
       else
       {
          while ((pid2 = fork()) == -1);
          if (pid2 == 0)
          {
             lockf(1, 1, 0);
             printf("pid2 here : c\n");
             lockf(1, 0, 0);
          }
          else
          {
             lockf(1, 1, 0);
             printf("father progress here : a\n");
             lockf(1, 0, 0);
          }
       }
       return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32

    在这里插入图片描述

    (二)进行循环访问并在内部加锁,发现子进程pid1先执行4次输出,然后parent输出,最后是pid2:

    #include 
    #include 
    #include 
    
    int main()
    {
       pid_t pid1, pid2;
       while ((pid1 = fork()) == -1);
       if (pid1 == 0)
       {
          for (int i = 0; i < 5; ++i)
          {
             lockf(1, 1, 0);
             printf("pid1 here : %d\n", i);
             lockf(1, 0, 0);
          }
       }
       else
       {
          while ((pid2 = fork()) == -1);
          if (pid2 == 0)
          {
             for (int i = 0; i < 5; ++i)
             {
                lockf(1, 1, 0);
                printf("pid2 here : %d\n", i);
                lockf(1, 0, 0);
             }
          }
          else printf("father progress here : a\n");
       }
       return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33

    在这里插入图片描述

    (三)进行循环访问并在外部加锁,发现输出情况一样:

    #include 
    #include 
    #include 
    
    int main()
    {
       pid_t pid1, pid2;
       while ((pid1 = fork()) == -1);
       if (pid1 == 0)
       {
          lockf(1, 1, 0);
          for (int i = 0; i < 5; ++i)
             printf("pid1 here : %d\n", i);
          lockf(1, 0, 0);
    
       }
       else
       {
          while ((pid2 = fork()) == -1);
          if (pid2 == 0)
          {
             lockf(1, 1, 0);
             for (int i = 0; i < 5; ++i)
                printf("pid2 here : %d\n", i);
             lockf(1, 0, 0);
          }
          else printf("father progress here : a\n");
       }
       return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30

    在这里插入图片描述
    (四)上锁后令进程进行短暂休眠,发现pid1和pid2交替输出,并且父进程不是最后输出:

    #include 
    #include 
    #include 
    
    int main()
    {
       pid_t pid1, pid2;
       while ((pid1 = fork()) == -1);
       if (pid1 == 0)
       {
          for (int i = 0; i < 5; ++i)
          {
             lockf(1, 1, 0);
             printf("pid1 here : %d\n", i);
             lockf(1, 0, 0);
             sleep(1);
          }
       }
       else
       {
          while ((pid2 = fork()) == -1);
          if (pid2 == 0)
          {
             for (int i = 0; i < 5; ++i)
             {
                lockf(1, 1, 0);
                printf("pid2 here : %d\n", i);
                lockf(1, 0, 0);
                sleep(1);
             }
          }
          else printf("father progress here : a\n");
       }
       return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35

    在这里插入图片描述

    分析:

    • pid1进程先用lockf(1,1,0)锁上stdout,执行输出之后,立即用lockf(1,0,0)释放stdout,而此时进程休眠了1秒,没有立即又给stdout加锁,在休眠的这段时间,pid2可以获得stdout资源,于是pid2执行输出;同理,pid2加锁,输出,释放锁之后一样也执行了休眠,于是pid1可以获得stdout资源,于是就出现了两个进程交替执行输出的情况。
    • 以上各种情况都运行多次发现运行结果不一定始终保持一致:原因是OS对进程执行的顺序管理和访问互斥共同造成的

    思考:

    系统是如何创建进程?当父进程fork子进程后,父进程和子进程从程序什么位置开始执行?为什么?

    (1)首先创建父进程,再根据父进程创建子进程。具体而言,是首先通过内核调用sys_clone来完成,先对父进程的数据和环境进行复制,然后修改新进程的一些属性,之后通过do_fork函数和ret_from_fork函数完成对新进程的创建以及运行。

    (2)经过资料查阅,个人认为,fork创建的父子进程并不保证任何有规律的执行顺序。不同linux,不同的系统配置环境,不同的系统负载下很可能结果不一样。

    (3)从实验的结果来推测,子进程和父进程都从调用fork函数的下一条语句开始执行。因为程序的顺序执行,也很难想象会从之前的语句重新执行。

  • 相关阅读:
    【python】二:基础学习-组织架构函数等
    3D全景:为各行业提供更真实的交互体验
    vue3-puzzle-vcode接入后端验证
    debian12 允许远程ssh登录
    CentOS7 编译源码升级内核
    springboot+springsecurity+jwt+elementui图书管理系统
    常用SQL语句大全
    MongoDB基础详解
    JAVA----钉钉机器人消息样式,关于PC 端与手机端文字消息样式显示不统一
    一起学习下RT-Thread的C语言编码规范
  • 原文地址:https://blog.csdn.net/Lov1_BYS/article/details/127862192