进程间通信一:子进程的创建与通信

一:进程间通信方式

每个进程各自有不同的用户地址空间
任何一个进程的全局变量在另一个进程中都看不到
所以进程之间要交换数据必须通过内核,在内核中开辟一块缓冲区
进程A把数据从用户空间拷到内核缓冲区,
进程B再从内核缓冲区把数据读走
内核提供的这种机制称为进程间通信。

在这里插入图片描述

**

1.不同进程间的通信本质

**:进程之间可以看到一份公共资源;而提供这份资源的形式或者提供者不同,造成了通信方式不同,而 pipe(管道)就是提供这份公共资源的形式的一种。
父进程fork出子进程时,子进程会继承父进程的数据等信息
**

2.管道建立:

**

  管道是由调用pipe函数来创建
#include <unistd.h>
int pipe (int fd[2]);//返回:成功返回0,出错返回-1     
 //fd参数返回两个文件描述符,fd[0]指向管道的读端,fd[1]指向管道的写端。fd[1]的输出是fd[0]的输入。

2.1管道间通信实现
(1)父进程创建管道,得到两个文件指向符指向管道两侧
(2)父进程创建子进程,子进程继承父进程的数据,也有两个文件指向符指向管道两侧
(3)父进程kill fd[0],子进程kill fd[1] (0为读取管道间传递的数据,1为在管道间写入数据,因为管道只支持单向通信)。⽗进程可以往管道⾥写,⼦进程可以从管道⾥读,管道是⽤环形队列实现的,数据从写端流⼊从读端流出,这样就实现了进程间通信。

在这里插入图片描述

2.2管道(pipe)
管道特点:

1.管道只允许具有血缘关系的进程间通信,如父子进程间的通信。

2.管道只允许单向通信。

3.管道内部保证同步机制,从而保证访问数据的一致性。

4.面向字节流

5.管道随进程,进程在管道在,进程消失管道对应的端口也关闭,两个进程都消失管道也消失。

二:子进程的创建

一个进程,包括代码、数据和分配给进程的资源。fork()函数通过系统调用创建一个与原来进程几乎完全相同的进程,也就是两个进程可以做完全相同的事,但如果初始参数或者传入的变量不同,两个进程也可以做不同的事。
**

1.fork()函数:

**父进程调用fork()函数后,系统先给新的进程分配资源,例如存储数据和代码的空间。然后把父进程的所有值都复制到新的新进程中,只有少数值与原来的进程的值不同。相当于克隆了一个自己。
(一)贴上实例代码:
在这里插入图片描述运行结果:

    i am the child process, my process id is 5574
    我是爹的儿子
    统计结果是: 1
    i am the parent process, my process id is 5573
    我是孩子他爹
    统计结果是: 1

在语句fpid=fork()之前,只有一个进程在执行这段代码,但在这条语句之后,就变成两个进程在执行了,这两个进程的几乎完全相同,将要执行的下一条语句都是if(fpid<0)
fork调用的一个奇妙之处在于它仅仅被调用一次,却能够返回两次,它可能有三种不同的返回值:
1、在父进程中,fork返回新创建子进程的进程ID;
2、在子进程中,fork返回0;
3、如果出现错误,fork返回一个负值

在fork函数执行完毕后,如果创建新进程成功,则出现两个进程,一个是子进程,一个是父进程。在子进程中,fork函数返回0,在父进程中,fork返回新创建子进程的进程ID。我们可以通过fork返回的值来判断当前进程是子进程还是父进程。

引用一位网友的话来解释fpid的值为什么在父子进程中不同。“其实就相当于链表,进程形成了链表,父进程的fpid(p 意味point)指向子进程的进程id, 因为子进程没有子进程,所以其fpid为0.

(二)贴上实例代码:
在这里插入图片描述运行结果:
i son/pa ppid pid fpid
0 parent 2043 3224 3225
0 child 3224 3225 0
1 parent 2043 3224 3226
1 parent 3224 3225 3227
1 child 1 3227 0
1 child 1 3226 0

第一步:在父进程中,指令执行到for循环中,i=0,接着执行fork,fork执行完后,系统中出现两个进程,分别是p3224和p3225(后面我都用pxxxx表示进程id为xxxx的进程)。可以看到父进程p3224的父进程是p2043,子进程p3225的父进程正好是p3224。我们用一个链表来表示这个关系:
p2043->p3224->p3225
第一次fork后,p3224(父进程)的变量为i=0,fpid=3225(fork函数在父进程中返向子进程id),

第二步:假设父进程p3224先执行,当进入下一个循环时,i=1,接着执行fork,系统中又新增一个进程p3226,对于此时的父进程,p2043->p3224(当前进程)->p3226(被创建的子进程)。
对于子进程p3225,执行完第一次循环后,i=1,接着执行fork,系统中新增一个进程p3227,对于此进程,p3224->p3225(当前进程)->p3227(被创建的子进程)。从输出可以看到p3225原来是p3224的子进程,现在变成p3227的父进程。父子是相对的,这个大家应该容易理解。只要当前进程执行了fork,该进程就变成了父进程了,就打印出了parent。
所以打印出结果是:
1 parent 2043 3224 3226
1 parent 3224 3225 3227
第三步:第二步创建了两个进程p3226,p3227,这两个进程执行完printf函数后就结束了,因为这两个进程无法进入第三次循环,无法fork,该执行return 0;了,其他进程也是如此。
以下是p3226,p3227打印出的结果:
1 child 1 3227 0
1 child 1 3226 0
细心的读者可能注意到p3226,p3227的父进程难道不该是p3224和p3225吗,怎么会是1呢?这里得讲到进程的创建和死亡的过程,在p3224和p3225执行完第二个循环后,main函数就该退出了,也即进程该死亡了,因为它已经做完所有事情了。p3224和p3225死亡后,p3226,p3227就没有父进程了,这在操作系统是不被允许的,所以p3226,p3227的父进程就被置为p1了,p1是永远不会死亡的

三:进程创建试题

#include <unistd.h>
#include <stdio.h>
 
int main()
{
		fork();/*****/
 
		fork() && fork() || fork();/*****/
 
		fork();/*****/
 
		sleep(100);
 
		return 0;
}

问题:不算main函数本身进程,一共有多少个进程被创建?
这道题主要考了两个知识点,一是逻辑运算符运行的特点;二是对fork的理解
表达式:cond1 && cond2 || cond3
1、cond1为假,那就不判断cond2了,接着判断cond3
2、cond1为真,这又要分为两种情况:
a、cond2为真,这就不需要判断cond3了
b、cond2为假,那还得判断cond3
上文提到:fork()函数调用一次会返回两次!!

在fork函数执行完毕后,如果创建新进程成功,则出现两个进程,一个是子进程,一个是父进程。在子进程中,fork函数返回0,在父进程中,fork返回新创建子进程的进程ID。我们可以通过fork返回的值来判断当前进程是子进程还是父进程。

在这里插入图片描述
由此可见:fork() && fork() || fork();这行代码产生4个新进程
而第一行代码: fork()产生一个新进程——————新进程数:1
第二行:fork() && fork() || fork() 产生新进程数——————2*4=8
第三行:第二行产生的新进程数:第一行的父进程与新进程(2)+第二行新进程数(8)然后乘2=10
共计新创建进程数:19

  • 2
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值