一、进程的层次结构

在Linux操作系统中,允许一个进程创建另一个进程,通常把创建进程的进程称为父进程,而把被创建的进程称为子进程。子进程可继续创建更多的孙进程,由此便形成了一个进程的层次结构子进程可以继承父进程所拥有的资源。
注:windows中不存在任何进程层次结构的概念,所有的进程都具有相同的地位。因此下面的函数和头文件仅存在于Liunx系统中。

二、常用函数

(1)fork函数

#include<unistd.h>
#include<sys/types.h>  //头文件
pid_t fork(void)   //函数原型
(pid_t 是一个宏定义,其实质是int 被定义在#include<sys/types.h>中)

fork调用用于创建一个新进程 (子进程 ),它与系统调用fork的进程 (父进程 ) 同时运行。创建新的子进程后,两个进程将执行函数fork的下一条指令。子进程使用相同的pc (程序计数器 ),相同的CPU寄存器,即子进程把父进程当前的情况拷贝一份,子进程和父进程的资源是相同的

fork不需要参数。

fork会返回两个返回值,一个返回到子进程,一个返回到父进程。
对于返回到子进程的值:① (负值 − 1 -1 1)创建子进程失败。② (零)返回到子进程。
对于返回到父进程的值:(正值)返回到父进程,该值包含新创建的子进程的进程ID。

因此,如果创建新进程成功,则出现两个进程,一个是子进程,一个是父进程。在子进程中,fork函数返回0,在父进程中,fork返回新创建子进程的进程ID。用数据结构的知识来理解,其实就相当于链表,进程形成了链表,父进程的fork函数返回的值指向子进程的进程id,,因为子进程没有子进程,所以其fork函数返回的值为0。

看下面的代码:

#include<stdio.h>
#include<unistd.h>
#include<sys/types.h>
int main(void)
{
    fork();
    printf(“S\n”);
}    
//子进程和父进程都打印出S

运行结果:
在这里插入图片描述
注:代码编写和运行的知识可以查看此网址。
https://blog.csdn.net/weixin_45920385/article/details/109373809


进一步对fork和进程层次结构的了解,看如下代码:

#include<stdio.h>
#include<unistd.h>
#include<sys/types.h>
int main(void)
{
    fork();
    fork();
    fork();
    printf(“S\n”);
}    

运行结果:
在这里插入图片描述

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


(2)getfork函数

#include<unistd.h>
#include<sys/types.h>  //头文件
getfork(void)

getfork函数返回值是目前进程的进程ID。
例子在第三部分展示。


(3)lockf函数

#include <unistd.h>   //头文件
int lockf(int fd, int cmd, int len)    //函数原型

lockf函数对指定区域(有len指示)的资源进行加锁或解锁,以实现进程的同步或互斥。
① fd 是打开文件的文件描述符。
② cmd 是指定要采取的操作的控制值。1是进行锁定,0是解锁。
③ len是要锁定或解锁的连续字节数。

对于这个函数我们主要掌握两个常用命令
1)当len = 0时是个特殊情况,它代表锁定区域从该函数到程序结尾。lockf(1,1,0)意思是该进程的编号为1,并对进程的资源进行锁定,锁定区域从该函数到程序结尾。
2)lockf(1,0,0)意思是对编号为1的进程进行解锁,释放资源。


三、具体演示

(1)

#include<stdio.h>
#include<unistd.h>
#include<sys/types.h>
int main(void)
{
    int pid;
    pid=fork();
    if(pid < 0)
    	printf(“error in fork”);
    else if(pid == 0)
    	printf(“I am the child process, my process id is %d\n”,getpid());
    else
    	printf(“I am the parent process, my process id is %d\n”,getpid());
}

运行结果:
在这里插入图片描述
分析:
函数fork()被调用后,若是创建新进程成功,则出现两个进程,一个是子进程,一个是父进程。在子进程中,fork函数返回0,在父进程中,fork返回新创建子进程的进程ID。 ,对于子进程,此时 p i d = 0 pid = 0 pid=0,则运行的是第 11 11 11行的输出语句;对于父进程,此时 p i d ≠ 0 pid ≠ 0 pid=0,则就会运行第 13 13 13行的输出语句 (子进程 ID为父进程 ID加 1)


(2)在系统中有一个父进程和两个子进程活动。让每个进程在屏幕上显示一个字符:父进程显示字符”a”,子进程分别显示字符”b”和”c”。

#include<stdio.h>
#include<unistd.h>
#include<sys/types.h>
int main()
{
	int p1,p2;
    while((p1 = fork()) == -1);
    if(p1 == 0)
    	putchar('b');
    else
    {
    	while((p2 = fork()) == -1);
    	if(p2 == 0)
        	putchar('c');
    	else
        	putchar('a');
     }
}

运行结果:
在这里插入图片描述

分析:
两个while语句都是检测fork()调用是否出错,没有出错则退出循环。所以第一个while语句循环推出后 p 1 p_1 p1 必定不等于 − 1 -1 1,当然在父进程 p 1 > 0 p_1 > 0 p1>0,所以直接跳到第二个while语句,再进行循环,同理也可以知道在其父进程中 p 2 > 0 p_2>0 p2>0,所以接下来先打印 a a a,然后回到第一个子进程,此时 p 1 = 0 p_1 = 0 p1=0,所以打印 b b b,到下一个子进程,可知 p 2 = 0 p_2 = 0 p2=0,则打印 c c c


(3)实现进程之间的互斥。

#include<stdio.h>
#include<unistd.h>
#include<sys/types.h>
int main(void)
{
	int p1,p2;
    p1 = fork();
    if(p1)
    {
    	lockf(1,1,0);
    	printf("parent\n");
    	lockf(1,0,0);
    }
    else
    {
    	lockf(1,1,0);
    	printf("child\n");
    	lockf(1,0,0);
    }
}

运行结果:
情况一:
在这里插入图片描述
情况二:
在这里插入图片描述

分析:
可以看到运行结果有两种情况,发现即使没有lockf函数的影响下也是这两种情况,只体现了父进程和子进程同时执行的特性,而互斥体现在哪里呢,接下来进行分析。首先看情况一,运行fork()函数;在父进程中,可知 p 1 > 0 p_1 > 0 p1>0进入了第一个代码块中,运行lockf(1,1,0)把父进程的资源 (同一时刻只可以有一个进程执行输出的资源) 锁住了,因此只能等待父进程把 p a r e n t parent parent 输出之后然后解锁资源;解锁了资源后子进程再进行对资源加锁输出 c h i l d child child。情况二其实同理,在子进程中,可知 p 1 = 0 p_1 = 0 p1=0,进入而第二个代码块中,运行lockf(1,1,0)子进程把资源进行加锁,输出 c h i l d child child后,解锁后父进程才能使用输出资源。综上所诉我们可以看出,无论是父进程先执行,还是子进程先执行,都会分别占用资源,只有资源释放了另一个进程才可使用,因此达到了互斥的关系。


需要转载请标明出处

Logo

更多推荐