当前位置:   article > 正文

Linux C语言 进程详解——fork()/wait()/waitpid()_c fork函数 输出子进程的id

c fork函数 输出子进程的id

一、fork()函数

1. 头文件
#include<unistd.h>
#include<sys/types.h>
  • 1
  • 2
2. 函数原型及功能介绍
函数原型
   pid_t fork( void);
  (pid_t 是一个宏定义,其实质是int 被定义在#includesys/types.h>中)
   返回值:若成功调用一次则返回两个值,子进程返回0,父进程返回子进程ID;否则,出错返回-1
  • 1
  • 2
  • 3
  • 4

fork系统调用用于创建一个新进程,称为子进程,它与父进程 同时运行(并发),且运行顺序不定(异步)
fork()函数如果成功调用一次返回两个值,一共可能有三种不同的返回值:

(1)在父进程中,fork返回新创建子进程的进程ID;
(2)在子进程中,fork返回0;
(3)如果出现错误,fork返回一个负值。

3.1 测试代码
#include<stdio.h>
#include<unistd.h>
#include<sys/types.h>
int main()
{
    pid_t child;
    child = fork();
    if(child == 0)//子进程抢到处理机资源并运行
    {//子进程运行代码
        for(int i = 0; i < 3; i++)
        {
            printf("I am child!\n");
            sleep(1);
        }
    }
    else if(child > 0)//父进程抢到处理机资源并运行
    {//父进程运行代码
        for(int i = 0; i < 3; i++)
        {
            printf("I am parent!\n");
            sleep(1);
        }
    }
    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
3.2 测试结果

父进程与子进程并发运行,且运行顺序不一定(可以调整循环次数观察结果)
在这里插入图片描述

4.1 创建多个子进程(注意事项)
#include<stdio.h>
#include<unistd.h>
#include<sys/types.h>
int main()
{
    pid_t child1, child2;
    child1 = fork();	//执行到这段代码时,子进程同时开始和父进程一起向下执行
    child2 = fork();	//该段代码被父进程和子进程(child1)同时执行,即子进程多创建了一个进程
    if(child1 == 0)
    {//因为子进程(child1)会还会创建一个子进程(孙进程), 故这段代码会被执行两次
        printf("I am child1!\n");
    }
    else if(child1 > 0)
    {
        if(child2 == 0)
        {
            printf("I am child2!\n");
        }
        else if(child2 > 0)
        {
            printf("I am parent!\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
4.2 测试结果:

因为子进程(child1)下已经创建了一个子进程(孙进程),当子进程(child1)抢到处理机资源后
从父进程(child1)和子进程(孙进程)的角度来推导,父进程(child1)就会和它的子进程同时执行进程(child1)的代码段

在这里插入图片描述

4.3 解决方案:
#include<stdio.h>
#include<unistd.h>
#include<sys/types.h>
int main()
{
    int i, n;
    pid_t child;
    printf("请输入需要创建的子进程个数:");
    scanf("%d", &n);

    for(i = 0; i < n; i++)
    {
        child = fork();
        if(child == 0)	//如果子进程抢到处理机后就退出循环,不然子进程的循环下还会i个创建进程
            break;	 	//若不退出,最终加上父进程一共有2^n个进程 
    }
    //循环创建完子进程后,(i+1)值对应着每个子进程
    if(i < n)
    {
        printf("I am %d child!(pid = %d)\n", i+1, getpid());
    }
    else
    {
        printf("I am parent!\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

在这里插入图片描述

二、wait()和waitpid()函数

1. 头文件
#include<sys/types.h>
#include<sys/wait.h>
  • 1
  • 2
2. 函数原型及功能介绍
函数原型:pid_t wait(int *status);
返回值:
	成功:返回结束的子进程pid,终止回收子进程,
	失败:返回-1(没有子进程)失败原因存于errno 中
参数:
  	(1)wait()会暂时停止目前进程的执行, 即阻塞父进程,等待子进程结束或者其他信号. 
  	(2)如果在调用wait()时子进程已经结束,wait()会立即返回子进程结束状态值. 
  	(3)子进程的结束状态值会由参数status返回, 而子进程的进程识别码也会一并返回. 
  	(4)如果不考虑结束状态值, 则参数 status 可以设成NULL.
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
函数原型:pid_t waitpid(pid_t pid, int *status, int options);
返回值:
   	正常:返回已成功结束运行的子进程的进程号
   	使用选项WNOHANG且没有子进程退出:0
	失败:-1
参数
	(1)pid:
		pid > 0|只等待进程ID等于pid的子进程,不管其他子进程是否结束,只要指定子进程未结束,waitpid()就会一直等下去
		pid =-1|等待任何一个子进程退出,此时和wait()作用一样
		pid = 0|等待其组ID等于调用进程的组ID的任一子进程
		pid <-1|等待其组ID等于pid的绝对值的任一子程序
	(2)options:
		WNOHANG:若pid指定的子进程未结束,则waitpid()不阻塞父进程,立即返回0;
		WUNTRACED:若pid指定进程已被暂停,且其状态自暂停以来还未报告过,则返回其状态
		0:wait(),阻塞父进程,等待子进程退出
	(3)stasus: 同wait
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
3.1 测试代码:
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<sys/types.h>
#include<sys/wait.h>
int main()
{
	int status;
    pid_t child1, child2, parent;
    printf("parent(pid = %d, ppid = %d\n", getpid(), getppid());
    if((child1 = fork()) == 0)//子进程child1抢到处理机资源并运行
    {//子进程child1运行代码
        for(int i = 1; i <= 10; i++)
        {
            printf("child1(进度 = %d%) is working!(pid = %d,ppid = %d)\n",i * 10, getpid(), getppid());
            sleep(1);
        }
         printf("Child1 had completed!\n");
    }
    else if(child1 > 0)//返回到父进程时还需要判断子进程child2是否抢到资源
    {
        if((child2 = fork()) == 0)//子进程child2抢到处理机资源并运行
        {//子进程child2运行代码
            for(int i = 1; i <= 10; i++)
            {
                printf("child2(进度 = %d%) is working!(pid = %d,ppid = %d)\n",i * 10, getpid(), getppid());
                sleep(1);
            }
             printf("Child2 had completed!\n");
        }
        else if(child2 > 0)//父进程parent抢到处理机资源并运行
        {
        	printf("The last child's pid = %d\n", wait(&status));
            for(int i = 1; i <= 3; i++)
            {
            	printf("parent(进度 = %d%) is working(pid = %d ppid = %d)\n", i * 10, getpid(), getppid());
            	sleep(1);
            }
            printf("Parent had Complete!\n");
        }
        else if(child2 == -1)
        {
            printf("Error!\n");
        }
    }
    else
    {
        printf("Error!\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
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
3.2 测试结果:

如下运行结果可以证明进程的并发性和异步性,进程之间并发运行,且运行的顺序不定
wait()会阻塞父进程,等待子进程全部结束后,父进程才开始运行
在这里插入图片描述

4.1 测试代码:
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<sys/types.h>
#include<sys/wait.h>
int main()
{
	int status;
    pid_t child1, child2, parent;
    printf("parent(pid = %d, ppid = %d\n", getpid(), getppid());
    if((child1 = fork()) == 0)//子进程child1先抢到处理机资源并运行
    {//子进程child1运行代码
        for(int i = 1; i <= 10; i++)
        {
            printf("child1(进度 = %d%) is working!(pid = %d,ppid = %d)\n",i * 10, getpid(), getppid());
            sleep(1);
        }
        printf("Child1 had completed!\n");
    }
    else if(child1 > 0)//子进程没有抢到处理机资源,还需要判断子进程child2是否抢到
    {
        if((child2 = fork()) == 0)//子进程child2抢到处理机资源并运行
        {//子进程child2运行代码
            for(int i = 1; i <= 10; i++)
            {
                printf("child2(进度 = %d%) is working!(pid = %d,ppid = %d)\n",i * 10, getpid(), getppid());
                sleep(1);
            }
            printf("Child2 had completed!\n");
        }
        else if(child2 > 0)//父进程parent抢到处理机资源并运行
        {
        	//父进程不阻塞,与子进程并发
        	waitpid(child1, NULL, WNOHANG);
    		waitpid(child2, NULL, WNOHANG);
        	for(int i = 1; i <= 3; i++)//父进程parent运行到30%时就提前退出
            {
            	printf("parent(进度 = %d%) is working\n", i * 10);
            	sleep(1);
            }
            printf("Parent had Complete!\n");
        }
        else if(child2 == -1)
        {
            printf("Error!\n");
        }
    }
    else
    {
        printf("Error!\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
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
4.2 运行结果:

waitpid()不会阻塞父进程,父进程与子进程共同竞争资源,父进程完成后提前退出
在这里插入图片描述

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/笔触狂放9/article/detail/210663
推荐阅读
相关标签
  

闽ICP备14008679号