01_fork()的使用

0号进程是空闲进程
1号进程是第一个用户进程
查看内核进程pid最大配置:cat /proc/sys/kernel/pid_max

**fork系统调用相关说明**
复制一个进程映象fork
   使用fork函数得到的子进程从父进程的继承了整个进程的地址空间,包括:进程上下文、进程堆栈、内存信息、打开的文件描述符、信号控制设置、进程优先级、进程组号、当前工作目录、根目录、资源限制、控制终端等。
   子进程与父进程的区别在于:
   1、父进程设置的锁,子进程不继承
   2、各自的进程ID和父进程ID不同
   3、子进程的未决告警被清除;
   4、子进程的未决信号集设置为空集。
fork系统调用
   包含头文件 <sys/types.h> 和 <unistd.h>
   函数功能:创建一个子进程
   函数原型
         pid_t  fork(void);
   参数:无参数。
   返回值:
   如果成功创建一个子进程,对于父进程来说返回子进程ID
   如果成功创建一个子进程,对于子进程来说返回值为0
   如果为-1表示创建失败
怎么样理解,一次调用,二次返回?
    问题的本质是:两次返回,是在各自的进程空间中返回的。
    子进程和父进程各有自己的内存空间 (fork:代码段、数据段、堆栈段、PCB进程控制块的copy)。
注意点:
   理解1fork系统调用之后,父子进程将交替执行。
   理解2:怎么样理解一次调用2次返回?
答:是linux内核做的工作,因为不管是子进程还是父进程,都是各自在自己的内存空间中返回。
   理解3:怎么样理解,fork返回值大于零的是父进程,为什么这样设计:?
答:因为父进程求子进程的pid比较困难,子进程求父进程的id比较容易。
   理解4:怎么样理解分支在fork之后,而不是父进程main函数的开始?
答:因为父进程已经把环境搭建起来了,子进程只需要把父进程的数据段、代码段、堆栈段、PCB复制过来就可以了。
#if  1

int procnum = 10;
int loopnum = 100;
void TestFunc(int loopnum)
{
    printf("loopnum:%d\n", loopnum);    
}

//连续创建n个进程
void test()
{
    int i = 0, j = 0;
    printf("please enter you procNum : \n");
    scanf("%d", &procnum);

    printf("please enter you loopnum :\n");
    scanf("%d", &loopnum);

    pid_t pid;
    //连续创建procnum个进程
    for (i=0; i<procnum; i++)
    {
        pid = fork();
        if (pid == 0)
        {
            for (j=0; j<loopnum; j ++)
            {
                TestFunc(j);
            }
            exit(0);
        }
    }

    //wait(NULL);   
    printf("hello...\n");
}
#endif

//会打印多少个hello?  答案8个
#if 1
void test(void )
{
    fork(); //会产生2个分支
    fork();  //2个分支,运行fork函数,会产生4个分支。。4个进程
    fork();  //4个进程运行fork调用,会产生8个分支。。。。

    printf("hello...\n");
}
#endif
写时复制copy on write原则
   如果多个进程要读取它们自己的那部分资源的副本,那么复制是不必要的。
   每个进程只要保存一个指向这个资源的指针就可以了。
   如果一个进程要修改自己的那份资源的“副本”,那么就会复制那份资源。这就是写时复制的含义
原因分析:加快速度,linux内核是段页式管理机制(因段管理从0开始,),也可叫页式管理机制。只复制对应的页。缺页,在中断查询,再赋值。
概念要深入理解
     子进程要拷贝父进程的东西,但是只有在子进程使用自己的变量时,那是系统才是真正的拷贝,而拷贝的时候也不是机械的拷贝,不是将父进程的页全拷贝到子进程的页,而是只拷贝那个变量所占的页。
/*
孤儿进程和僵尸进程
   如果父进程先退出,子进程还没退出那么子进程的父进程将变为init进程。(注:任何一个进程都必须有父进程)
   如果子进程先退出,父进程还没退出,那么子进程必须等到父进程捕获到了子进程的退出状态才真正结束,
    否则这个时候子进程就成为僵尸进程。

孤儿进程
如果父亲进程先结束,子进程会托孤给1号进程
*/
//测试僵尸进程
#if  1
int test(void )
{
    pid_t pid;
    printf("befor fork pid:%d \n", getpid());
    int abc = 10;
    pid = fork(); //errno
    if (pid == -1)  
    {
        perror("tile");
        return -1;
    }
    if (pid > 0)
    {
        abc ++;
        printf("parent: pid:%d \n", getpid());
        printf("abc: %d \n", abc);
        sleep(20);
    }
    else if (pid == 0)
    {
        abc ++;
        printf("child: %d, parent: %d \n", getpid(), getppid());
        printf("abc: %d \n", abc);
        //sleep(100);
    }

    printf("fork after....\n");
    return 0;
}


#endif
结果:
hzmct@U-64:~$ ps -ef| grep dm03_fork
hzmct     3789  3290  0 20:40 pts/8    00:00:00 ./dm03_fork
hzmct     3790  3789  0 20:40 pts/8    00:00:00 [dm03_fork] <defunct>(僵尸进程)
hzmct     3796  3687  0 20:40 pts/9    00:00:00 grep --color=auto dm03_fork
//测试孤儿进程
#if  1
int test(void )
{
    pid_t pid;
    printf("befor fork pid:%d \n", getpid());
    int abc = 10;
    pid = fork(); //errno
    if (pid == -1)  
    {
        perror("tile");
        return -1;
    }
    if (pid > 0)
    {
        abc ++;
        printf("parent: pid:%d \n", getpid());
        printf("abc: %d \n", abc);
        //sleep(20);
    }
    else if (pid == 0)
    {
        abc ++;
        printf("child: %d, parent: %d \n", getpid(), getppid());
        printf("abc: %d \n", abc);
        sleep(100);
    }

    printf("fork after....\n");
    return 0;
}
#endif
结果:
hzmct@U-64:~$ ps -ef| grep dm03_fork
hzmct     3818     1  0 20:45 pts/8    00:00:00 ./dm03_fork   托孤给了1号进程
hzmct     3820  3687  0 20:45 pts/9    00:00:00 grep --color=auto dm03_fork

查看僵尸进程
ps -e -o stat,ppid,pid,cmd | egrep '^[Zz]'
说明:
ps:ps命令用于获取当前系统的进程信息.
-e:参数用于列出所有的进程
-o:参数用于设定输出格式,这里只输出进程的stat(状态信息)、ppid(父进程pid)、pid(当前进程的pid),cmd(即进程的可执行文件。
egrep:是linux下的正则表达式工具
'^[Zz]':这是正则表达式,^表示第一个字符的位置,[Zz],表示z或者大写的Z字母,即表示第一个字符为Z或者z开头的进程数据,只所以这样是因为僵尸进程的状态信息以Z或者z字母开头。
(2)现在大多数linux系统,也会将僵尸进程标识为defunct,所以你也可以通过如下命令来获取僵尸进程信息。
ps -ef | grep "defunct"
//避免僵尸进程
#if 1
int test(void )
{
    pid_t pid;
    signal(SIGCHLD, SIG_IGN);//避免僵尸进程,即忽略子进程发给父进程的信号

    printf("befor fork pid:%d \n", getpid());
    int abc = 10;
    pid = fork(); //errno
    if (pid == -1)  
    {
        //printf("pid < 0 err.\n");
        perror("tile");
        return -1;
    }
    if (pid > 0)
    {
        abc ++;
        printf("parent: pid:%d \n", getpid());
        printf("abc: %d \n", abc);
        sleep(20);
    }
    else if (pid == 0)
    {
        abc ++;
        printf("child: %d, parent: %d \n", getpid(), getppid());
        printf("abc: %d \n", abc);
    }

    printf("fork after....\n");
    return 0;
}
结果:
hzmct@U-64:~$ ps -ef| grep dm03_fork
hzmct     3838  3290  0 20:49 pts/8    00:00:00 ./dm03_fork
托孤给了session会话了
hzmct     3841  3687  0 20:49 pts/9    00:00:00 grep --color=auto dm03_fork
#endif

猜你喜欢

转载自blog.csdn.net/WUZHU2017/article/details/81636851
今日推荐