• 进程-(2)


    创建子进程

    1、 fork() 函数,创建一个新进程
    1) 如果创建失败, 出错返回-1
    2) 由fork函数创建的进程叫子进程(child proccess)
    3) 此函数调用一次,返回两次。分别在子进程和和父进程中返回,子进程中返回0,父进程返回子进程的PID
    4) 子进程是父进程的副本,子进程获得父进程的数据空间,堆和栈的副本,但子进程共享父进程的正文段
    5) fork之后 父子进程会继续执行。父进程先执行还是子进程先执行不确定
    6) fork时,文件描述符也会被复制,那么两个进程可能会共享同一个文件表。
    7) fork失败的原因
      系统中有太多的进程
      实际用户ID的进程总数已经超过系统限制。
    8) fork的用法
    一个父进程希望复制自己,使父子进程同时执行不同的代码段
    一个进程要执行一个不同的程序
    fork() 通过复制父进程 创建子进程
    9) 使用例子说明
    fork() 非常简单的复杂函数。
    pid_t fork(void);

    2、 代码验证阶段:

    例子1: fork()后的效果

    #include <stdio.h>

    #include <unistd.h>

    int main(){

      printf("begin ");

      pid_t pid = fork();

      printf("end:%d ",pid);

    }

    aiyq195@aiyq195-virtual-machine:~/桌面/uc$ gcc fork.c -o fork

    aiyq195@aiyq195-virtual-machine:~/桌面/uc$ ./fork

    begin

    end:6090

    end:0

    结果如上:

    产生上面的结果的思考,fork函数调用后,才会创建子进程,fork()之前的代码,因为只有父进程在运行,所以,只有一个begin打印出来,而后,fork()函数创建了子进程,这时候父子进程都会执行后续的代码,也就是fork()后面的printf()函数,会执行两遍;产生两个end;

    fork()创建子进程后,fork()之前的代码只有父进程执行一次,fork()之后的代码父子进程都执行一次(一共执行二次)。

    而返回的ID,fork()函数也会返回2次,父进程返回 子进程pid,子进程返回 0。

     

    例子2: fork()后父子进程的查看

    #include <stdio.h>

    #include <unistd.h>

    int main()

    {

      printf("begin ");//要求:在父进程加上打印子进程ID

      pid_t pid = fork();//在子进程中加上打印父进程ID

      if(pid == 0)

      {//子进程

          printf("子进程pid=%d,父进程pid=%d ",getpid(),getppid());

      }

      else

      {//父进程

        printf("父进程pid=%d,子进程pid=%d ",getpid(),pid);

        }

    }

    结果如下:

    aiyq195@aiyq195-virtual-machine:~/桌面/uc/day06$ ./fork2

    begin

    父进程pid=6238,子进程pid=6239

    子进程pid=6239,父进程pid=6238

    可以看出:

    fork()创建子进程后,规范中没有确定谁先运行,父子进程谁先运行 和 操作系统自身的算法有关,Linux中 父子进程谁先运行是 不确定的。

    也就是说,这里可能子进程里获取到的父进程id,也有可能是1(即init进程)。

    例子3:fork()查看变量
    #include <stdio.h>
    #include <unistd.h>
    #include <stdlib.h>
    #include <string.h>

    int i = 10;//全局变量
    int main()
    {
      int i2 = 10;//栈
      int* pi = malloc(4);//堆
      *pi = 10;
      pid_t pid = fork();
      int i4 = 10;//不是复制,是父子进程分别定义
      if(pid == 0)
      {//子进程
        i = 20; i2 = 20; *pi = 20; i4 = 20;
        printf("child:i=%d,i2=%d,*pi=%d,i4=%d ",i,i2,*pi,i4);
        printf("child:%p,%p,%p,%p ",&i,&i2,pi,&i4);
        exit(0); //退出子进程
      }
      sleep(2); //可以确保子进程先进行
      printf("father:i=%d,i2=%d,*pi=%d,i4=%d ",i,i2,*pi,i4);
      printf("father:%p,%p,%p,%p ",&i,&i2,pi,&i4);
    }

    结果如下:
    aiyq195@aiyq195-virtual-machine:~/桌面/uc/day06$ ./fork3
    child:i=20,i2=20,*pi=20,i4=20
    child:0x804a030,0xbf954cc0,0x9260008,0xbf954cc4
    father:i=10,i2=10,*pi=10,i4=10
    father:0x804a030,0xbf954cc0,0x9260008,0xbf954cc4

    可以说明:内存有复制;
    fork()在创建子进程时,复制父进程的除代码区之外的内存区域,和父进程 完全共享代码区。

    其实,上面的内容,通俗点说就是,fork()函数执行后,父进程还在原来的内存区域内,而它产生的子进程呢,会再找一个内存区域,来存放子进程自己的数据,子进程会把父进程的代码内容给拷贝过来,但是,它如果变化;它自己内部修改,它只会修改自己的那片内存区的数据,而父进程,不会因为子进程的改变,而改变;

    例子4:fork()关于文件

    #include <stdio.h>
    #include <stdlib.h>
    #include <unistd.h>
    #include <fcntl.h>

    int main()
    {
      int fd = open("a.txt",O_RDWR|O_CREAT,0666);
      if(fd == -1)

      {

        perror("open"),exit(-1);

      }

      pid_t pid = fork();//先open()后fork() 一张文件表
      if(pid == 0 )
      {
        write(fd,"abc",3);
        close(fd);
        exit(0);
      }
      sleep(1);
      write(fd,"123",3);
      close(fd);
    }
    结果如下:
    aiyq195@aiyq195-virtual-machine:~/桌面/uc$ cat a.txt
    abc123
    fork()创建子进程,如果父进程有文件描述符,子进程将复制文件描述符,但不复制文件表。
    如果文件在fork()函数之前打开,父子进程只有一个偏移量,也就是只有一张文件表:

    也就是说,如果父进程有打开一个文件,子进程跟父进程会共用一个文件描述符,而且是针对一个文件;

    例子5:fork()在文件打开之前
    #include <stdio.h>
    #include <stdlib.h>
    #include <unistd.h>
    #include <fcntl.h>

    int main()
    {
      pid_t pid = fork();//先fork()后open(),两个文件表
      int fd = open("a.txt",O_RDWR|O_CREAT,0666);
      if(fd == -1)
      {
        perror("open"),exit(-1);
      }
      if(pid == 0 )
      {
        write(fd,"abc",3);
        close(fd);
      exit(0);
      }
      sleep(1);
      write(fd,"123",3);
      close(fd);
    }

    结果如下:
    aiyq195@aiyq195-virtual-machine:~/桌面/uc$ ./fork4
    aiyq195@aiyq195-virtual-machine:~/桌面/uc$ ls
    a.txt fork2 fork3 fork4 fork5 fork.c
    fork fork2.c fork3.c fork4.c fork5.c
    aiyq195@aiyq195-virtual-machine:~/桌面/uc$ cat a.txt
    123

    fork()创建子进程之前,打开的文件,则父进程有文件描述符,子进程将复制文件描述符,但不复制文件表。也就是说这时候,有两个文件描述符,但是只有一个文件表,也就是一个文件偏移量;
    如果创建子进程之后,才open的文件;则有两个文件描述符,两张表;
    文件偏移量存在文件表中,不存在描述符中;

    上面的内容,也就说明了,进程对于文件的操作内容:如果在fork之前,则父子进程共用一个文件表,有两个文件描述符,如果在fork之后,则有两个文件表,两个文件描述符。后执行的会吧先执行的给冲掉;

    例子6:孤儿进程
    思路:先打印 子进程的父进程,然后子进程sleep(),父进程在sleep()期间结束,子进程sleep
    ()结束后 再次打印父进程。

    #include <stdio.h>
    #include <stdlib.h>
    #include <unistd.h>

    int main()
    {
      pid_t pid = fork();
      if(pid == 0)
      {//子进程满足条件,而父进程判断不满足
        printf("pid=%d,ppid=%d ",getpid(),getppid());
        sleep(3);
        printf("pid=%d,ppid=%d ",getpid(),getppid());
        exit(0);
      }
      sleep(1);
    }


    结果如下:
    aiyq195@aiyq195-virtual-machine:~/桌面/uc/day06$ ./fork5
    pid=6463,ppid=6462
    pid=6463,ppid=1
    因为,父子进程都建立后,子进程的打印开始,也可能是父进程先开始,但是,父进程直接休眠了,然后是子进程开始休眠,父进程先休眠结束,接着结束,然后,子进程再次醒来,发现父进程已经死掉,则认1进程为父进程;

  • 相关阅读:
    Host IP地址 is not allowed to connect to this MySQL server
    本地或远程连接mysql:Unable to connect to any of the specified MySQL hosts.
    Table xxx is marked as crashed and should be repaired
    使用Linq 做数据去重
    SharePoint2010与Reporting Services集成方案
    上下左右布局(DIV+CSS)
    .NET 内存管理—CLR的工作
    删除数据库所有用户表
    未在本地计算机上注册“Microsoft.ACE.OLEDB.12.0”提供程序
    c# 10位数int时间单位换算为datetime
  • 原文地址:https://www.cnblogs.com/aiyq195/p/6427826.html
Copyright © 2020-2023  润新知