zoukankan      html  css  js  c++  java
  • 进程-(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进程为父进程;

  • 相关阅读:
    Saltstack module gem 详解
    Saltstack module freezer 详解
    Saltstack module firewalld 详解
    Saltstack module file 详解
    Saltstack module event 详解
    Saltstack module etcd 详解
    Saltstack module environ 详解
    Saltstack module drbd 详解
    Saltstack module dnsutil 详解
    获取主页_剥离百度
  • 原文地址:https://www.cnblogs.com/aiyq195/p/6427826.html
Copyright © 2011-2022 走看看