当前课程知识点:基于Linux的C++ >  第十三讲 进程编程 >  13.07 进程间通信(一) >  LinuxCPP1307

返回《基于Linux的C++》慕课在线视频课程列表

LinuxCPP1307在线视频

LinuxCPP1307

下一节:LinuxCPP1308

返回《基于Linux的C++》慕课在线视频列表

LinuxCPP1307课程教案、知识点、字幕

接下来一节就是进程间通信

我们要谈到几个机制

一个是管道 一个是进程信号量

一个是共享内存 一个是映射内存

还有消息队列、套接字

有几个

这一讲里面我们会讨论

前面五种——管道、进程信号量、

共享内存、映射内存、消息队列

套接字我们会在网络那一讲里

再详细讨论

我们首先来看管道

管道是什么东西

管道是允许单向通讯的自动同步设备

它是一种半双工的模式

一般情况下我们是以半双工的模式

去使用它的

数据在写入端写入 在读取端读取

就这个意思

管道是一个典型的串型设备

数据的读取和写入

顺序是完全相同的

写入是什么顺序

读取出来 它也是什么顺序

这个就是管道

管道只能应用于有亲缘关系的进程

常常是父进程和子进程之间的通讯

我们使用管道

在使用管道的时候 特别注意

管道的数据容量是有限的

在大部分情况下

它是一个内存页面的大小

如果写入的速度超过了它读取的速度

写入的进程就会被阻塞

一直到管道中有空闲的空间

可以朝里写

那么如果读取的速度

超过了写入速度呢

你会把那个管道里的数据都读完的

读完了以后呢

读进程呢 它也会被阻塞

一直到那个管道里

有新的数据你可以去读取

创建管道那个函数叫pipe()

函数倒很简单

带一个只包含两个元素的整数数组

那是管道的文件描述符

因为一个管道有一个写入端

有一个读取端

每一端都有一个文件描述符

所以它是双文件描述符

那么这个数组保存的就是这个管道的

双文件描述符

0号元是读取端 一号元是写入端

注意这个

这个函数调用的时候

成功的时候会返回0

不成功的时候就会返回-1

设errno的值

调用的方式其实也简单

定义这一个包含两个元素的整数数组

然后调用pipe()

这个pipe()一调用

它就会给你构造一个管道

构造一个管道之后呢

它的文件描述符

就会给你写到这个数组里

pipe_fds这个数组里

它帮你写进去的

然后接下来你就可以用了

所以这个pipe_fds数组的0号元

就是管道读取端的文件描述符

1号元就是写入端的文件描述符

你就可以直接用它

操纵这个管道

我们看管道通讯的一个例子

我实现一个函数Write()

向这个特定的一个流里输出一串信息

这串信息我们会重复输出好几遍

所以这个信息是用message传进来的

流是一个文件指针

FILE * stream传进来的

这个消息写多少遍呢 写count遍

我就一个for循环朝这里面写数据

就完了

读呢 我们就从这个流里边去读这个数据

读这个数据的话我们就蛮贪心地读取

就是当这个流没有结束的时候

当读取这个信息没有出错的时候

当这个流里还有数据可以读的时候

我们就把这个数据全都读出来

然后读出来以后就向标准输出流里

输出这个数据就完了 很典型

这个数据从哪来呢 管道里来

这个数据朝哪里写呢 管道里写

你看我们父子进程

怎么通过这个管道传这个数据的

在主函数里调用pipe()构造一个管道

我们会创建这样的一个管道

然后fork() 创建一个子进程

用pid保存子进程的PID

if( pid== 0 )

OK 子进程close( fds[1] )

关闭管道的写入端

表示在我们的子进程里

我们只从管道里读数据

我们不向管道里写数据

刚才不说了嘛

管道 你创建出来

虽然有一个写入端 有一个读取端

但是对于两个进程

使用管道进行通讯的时候

一个进程只使用写入端

一个进程只使用读取端

所以它实际上是一个半双工的模式

你也可以倒过来

A进程写入 B进程读取

可以倒过来

A进程读取 B进程写入

但是正常情况下

一个管道只能一端读取 一端写入

两个进程一个读取、一个写入

这叫半双工的模式

双工模式就是双向读写嘛

半双工呢 就是确实它都可读可写

但是每次都只能这边读、那边写

或者这边写、那边读

你如果真让两个进程

同时要互相传递数据

都要读写的话

那就只能创建两个管道

就是这个意思

我们这里面只创建一个管道

创建一个管道 对于子进程来讲呢

我们只负责读取 从管道中读数据

所以写入端对我们没有用

我们直接关闭它的文件描述符

close( fds[1] ) 上来就关掉 不要

然后我们调用一个系统调用fdopen()

传文件描述符 以读模式打开它

得到它的文件指针 FILE *

这样的话就可以和C的函数

完全地吻合起来

因为C的很多文件操作

实际上使用的是文件指针

C标准库使用的是这个东西

我们可以使用FILE * stream

来得到这个管道的读取端的文件指针

fdopen()函数干的就是这个事

然后我们从这个流里读数据

读完了关闭这个管道的读取端

这是子进程

接下来是父进程

父进程呢 我们会构造一个缓冲区

要朝里面写数据

数据在哪呢 数据存在这个缓冲区里 对吧

我们会构造一系列的数据

这个数据我们怎么构造呢

就是定义一个buffer

这是一个缓冲区 buf_size

我定义成4096个字节 一个页面

然后我就朝里面写

我写多少呢 我写4094个字节

都是“abcdefg” 一直到“z”

然后再来一遍“abcdefg”

就26个字符、26个字符不停地往下写

我写了4094个字节

最后两个字节呢 我们封装成“\0”

表示这个串结尾了 就这个意思

我们就生成这些信息

然后关闭管道的读取端

我们只负责写入 我不负责读取

这是父进程

所以要做到这个事

然后我们fdopen( fds[1] )

以写模式打开它的管道写入端

得到FILE * 文件指针

得到文件指针之后

我们就像这个stream里写数据

写这个buffer里面的数据 写三遍

一次一个页面 一次一个页面

我们写三遍

写完了我们close( fds[1] )

把管道的写入端也给它关闭掉

这个就是管道通讯的示例

我们前边用到了一个很特殊的函数 dup()

现在我们就谈它怎么进行

文件描述符的复制

管道可以进行重定向

在这里使用到的一个非常重要的概念

叫等位文件描述符

所谓等位文件描述符

就是共享相同的文件位置

和状态标志设置

它们代表的是同一个文件和设备

这个就叫等位文件描述符

dup()函数就将两个文件描述符等位对待

认为它两者是一样的

这个函数有两个版本

一个叫dup()带单参数的版本oldfd

一个是 dup2() 单双参数的版本

一个是oldfd 一个是newfd

都将创建

oldfd这个老的文件描述符的一份副本

构造它的一个拷贝

第一个就是选择最小的文件描述符

来作为新的文件描述符

第二个呢 就是你传的那个参数

作为新的文件描述符

一旦构造出来这样一个副本之后

在你的文件描述符那个表格里面

那么就会有两个文件描述符

它后面跟着的那个文件

或设备的那个指针

就会变成同样的值

你比如说0号文件描述符

它后面跟着指向的

不是那个标准输入流那地方吗

stdin嘛 它就会指向那个地方

你把它dup( 0 )

就按我刚才那个例子

如果你把三个标准流全关掉了

那么你把哑终端

又构造到了0号文件描述符上

一旦你dup( 0 )

就把0号文件描述符的

那个指向文件或设备的那个指针

也写到了1号描述符的那个地方

它优先占用最小的文件描述符

0号被占用 1号还空闲

所以占用1号

那就意味着1号文件描述符

将会指向那个哑终端

也指向哑终端

再来一个dup( 0 )

它就把0号文件描述符

又写到2号文件描述符后边的那个区域

就意味着2号文件描述符后面那个

文件或设备的指针也指向哑终端

这就意味着

我们的标准输入流、标准输出流、

标准错误流都指向了哑终端嘛

dup()干的就是这个事

dup2()就是把那个新的文件描述符

设定给它 设定给新的文件描述符

你看dup2( fd, STDIN_FILENO ) 什么意思

就是把fd这个文件描述符

所对应的那个文件或设备的

那个指针 那个值传

给STDIN_FILENO 这个文件描述符

让它也指向那个地方

这就意味着标准输入流会被我们关闭

然后会被作为fd的一个副本重新打开

我们的输入就不再从键盘来

而是从fd所表示那个文件设备里边来

dup2()干的就是这个事

我们看这个例子

构造一个管道pipe( fds )

当然还是两个整数的一个数组

fork() 创建一个子进程

if( pid == 0 ) 说明它是子进程里

关闭0号元 我们只写入 不读取

然后把1号元和标准输出流

挂接在一起

这个管道不是写入嘛 对吧

然后就会挂接到我们的标准输出流

所以你一写就向标准输出流里输数据了

就这个意思

然后我们一执行

execvp() 就执行这个程序“ls”

所以这个数据就全写到了管道里

“ls”一运行

向标准输出流里写数据

标准输出流被我挂到哪去了

挂到管道的写入端了呀

所以这个数据就哗啦哗啦

就全写到管道里了

然后父进程就去读这个管道

我们关闭这个管道的写入端

我们就去读

打开这个管道的读取端

得到它的文件指针

然后我们就从这里边

就把这个写入过来的数据就全读出来了

所以你看我们这个程序

子进程就会“ls” 就列目录

把这个信息列出来以后

全扔到管道里

而我们父进程

就从管道里把那个信息就拿出来了

你写了什么

我们这里边就读出什么来

就这个模式

最后当父进程读取完全部的数据之后

关闭我们管道的读取端

最后waitpid()

等待我们的子进程结束

这就把我们前面讲到的几个知识点

都串在一起了

基于Linux的C++课程列表:

第一讲 C/C++基本语法元素

-1.1 提纲

--LinuxCPP0101

-1.2 程序设计的基本概念

--LinuxCPP0102

-1.3 简单C/C++程序介绍

--LinuxCPP0103

-1.4 程序设计的基本流程

--LinuxCPP0104

-1.5 基本语法元素

--LinuxCPP0105

-1.6 程序设计风格

--LinuxCPP0106

-1.7 编程实践

--LinuxCPP0107

-第一讲 C/C++基本语法元素--编程实践提交入口

第二讲 程序控制结构

-2.1 提纲

--LinuxCPP0201

-2.2 结构化程序设计基础

--LinuxCPP0202

-2.3 布尔数据

--LinuxCPP0203

-2.4 分支结构

--LinuxCPP0204

-2.5 break语句

--LinuxCPP0205

-2.6 循环结构

--LinuxCPP0206

-2.7 编程实践

--LinuxCPP0207

-第二讲 程序控制结构--编程实践提交入口

第三讲 函数

-3.1 提纲

--LinuxCPP0301

-3.2 函数声明、调用与定义

--LinuxCPP0302

-3.3 函数调用栈框架

--LinuxCPP0303

-3.4 编程实践

--LinuxCPP0304

-第三讲 函数--编程实践提交入口

第四讲 算法

-4.1 提纲

--LinuxCPP0401

-4.2 算法概念与特征

--LinuxCPP0402

-4.3 算法描述

--LinuxCPP0403

-4.4 算法设计与实现

--LinuxCPP0404

-4.5 递归算法(一)

--LinuxCPP0405

-4.6 递归算法(二)

--LinuxCPP0406

-4.7 容错与计算复杂度

--LinuxCPP0407

-4.8 编程实践

--LinuxCPP0408

-第四讲 算法--编程实践提交入口

第五讲 程序组织与开发方法

-5.1 提纲

--LinuxCPP0501

-5.2 库与接口

--LinuxCPP0502

-5.3 随机数库(一)

--LinuxCPP0503

-5.4 随机数库(二)

--LinuxCPP0504

-5.5 作用域与生存期

--LinuxCPP0505

-5.6 典型软件开发流程(一)

--LinuxCPP0506

-5.7 典型软件开发流程(二)

--LinuxCPP0507

-5.8 编程实践

--LinuxCPP0508

-第五讲 程序组织与开发方法--编程实践提交入口

第六讲 复合数据类型

-6.1 提纲

--LinuxCPP0601

-6.2 字符

--LinuxCPP0602

-6.3 数组(一)

--LinuxCPP0603

-6.4 数组(二)

--LinuxCPP0604

-6.5 结构体

--LinuxCPP0605

-6.6 编程实践

--LinuxCPP0606

-第六讲 复合数据类型--编程实践提交入口

第七讲 指针与引用

-7.1 提纲

--LinuxCPP0701

-7.2 指针基本概念

--LinuxCPP0702

-7.3 指针与函数

--LinuxCPP0703

-7.4 指针与复合数据类型(一)

--LinuxCPP0704

-7.5 指针与复合数据类型(二)

--LinuxCPP0705

-7.6 字符串

--LinuxCPP0706

-7.7 动态存储管理(一)

--LinuxCPP0707

-7.8 动态存储管理(二)

--LinuxCPP0708

-7.9 引用

--LinuxCPP0709

-7.10 编程实践

--LinuxCPP0710

-第七讲 指针与引用--编程实践提交入口

第八讲 链表与程序抽象

-8.1 提纲

--LinuxCPP0801

-8.2 数据抽象(一)

--LinuxCPP0802

-8.3 数据抽象(二)

--LinuxCPP0803

-8.4 链表(一)

--LinuxCPP0804

-8.5 链表(二)

--LinuxCPP0805

-8.6 链表(三)

--LinuxCPP0806

-8.7 链表(四)

--LinuxCPP0807

-8.8 函数指针(一)

--LinuxCPP0808

-8.9 函数指针(二)

--LinuxCPP0809

-8.10 抽象链表(一)

--LinuxCPP0810

-8.11 抽象链表(二)

--LinuxCPP0811

-8.12 编程实践

--LinuxCPP0812

-第八讲 链表与程序抽象--编程实践提交入口

第九讲 类与对象

-9.1 提纲

--LinuxCPP0901

-9.2 程序抽象与面向对象

--LinuxCPP0902

-9.3 类类型

--LinuxCPP0903

-9.4 对象(一)

--LinuxCPP0904

-9.5 对象(二)

--LinuxCPP0905

-9.6 类与对象的成员(一)

--LinuxCPP0906

-9.7 类与对象的成员(二)

--LinuxCPP0907

-9.8 类与对象的成员(三)

--LinuxCPP0908

-9.9 继承(一)

--LinuxCPP0909

-9.10 继承(二)

--LinuxCPP0910

-9.11 继承(三)

--LinuxCPP0911

-9.12 多态(一)

--LinuxCPP0912

-9.13 多态(二)

--LinuxCPP0913

-9.14 编程实践

--LinuxCPP0914

-第九讲 类与对象--编程实践提交入口

第十讲 操作符重载

-10.1 提纲

--LinuxCPP1001

-10.2 四则运算符重载(一)

--LinuxCPP1002

-10.3 四则运算符重载(二)

--LinuxCPP1003

-10.4 关系与下标操作符重载

--LinuxCPP1004

-10.5 赋值操作符重载(一)

--LinuxCPP1005

-10.6 赋值操作符重载(二)

--LinuxCPP1006

-10.7 赋值操作符重载(三)

--LinuxCPP1007

-10.8 赋值操作符重载(四)

--LinuxCPP1008

-10.9 赋值操作符重载(五)

--LinuxCPP1009

-10.10 流操作符重载(一)

--LinuxCPP1010

-10.11 流操作符重载(二)

--LinuxCPP1011

-10.12 流操作符重载(三)

--LinuxCPP1012

-10.13 操作符重载总结

--LinuxCPP1013

-10.14 编程实践

--LinuxCPP1014

-第十讲 操作符重载--编程实践提交入口

第十一讲 泛型编程

-11.1 提纲

--LinuxCPP1101

-11.2 泛型编程概览

--LinuxCPP1102

-11.3 异常处理机制(一)

--LinuxCPP1103

-11.4 异常处理机制(二)

--LinuxCPP1104

-11.5 运行期型式信息(一)

--LinuxCPP1105

-11.6 运行期型式信息(二)

--LinuxCPP1106

-11.7 模板与型式参数化

--LinuxCPP1107

-11.8 题外话:术语翻译

--LinuxCPP1108

-11.9 泛型编程实践(一)

--LinuxCPP1109

-11.10 泛型编程实践(二)

--LinuxCPP1110

-11.11 泛型编程实践(三)

--LinuxCPP1111

-11.12 泛型编程实践(四)

--LinuxCPP1112

-11.13 泛型编程实践(五)

--LinuxCPP1113

-11.14 泛型编程实践(六)

--LinuxCPP1114

-11.15 泛型编程实践(七)

--LinuxCPP1115

-11.16 泛型编程实践(八)

--LinuxCPP1116

-11.17 泛型编程实践(九)

--LinuxCPP1117

-11.18 泛型编程实践(十)

--LinuxCPP1118

-11.19 编程实践

--LinuxCPP1119

-第十一讲 泛型编程--编程实践提交入口

第十二讲 Linux系统编程基础

-12.1 提纲

--LinuxCPP1201

-12.2 程序执行环境(一)

--LinuxCPP1202

-12.3 程序执行环境(二)

--LinuxCPP1203

-12.4 程序执行环境(三)

--LinuxCPP1204

-12.5 程序执行环境(四)

--LinuxCPP1205

-12.6 输入输出(一)

--LinuxCPP1206

-12.7 输入输出(二)

--LinuxCPP1207

-12.8 文件系统

--LinuxCPP1208

-12.9 设备

--LinuxCPP1209

-12.10 库(一)

--LinuxCPP1210

-12.11 库(二)

--LinuxCPP1211

-12.12 makefile文件(一)

--LinuxCPP1212

-12.13 makefile文件(二)

--LinuxCPP1213

-12.14 makefile文件(三)

--LinuxCPP1214

-12.15 编程实践

--LinuxCPP1215

-第十二讲 Linux系统编程基础--编程实践提交入口

第十三讲 进程编程

-13.01 提纲

--LinuxCPP1301

-13.02 进程基本概念

--LinuxCPP1302

-13.03 信号

--LinuxCPP1303

-13.04 进程管理(一)

--LinuxCPP1304

-13.05 进程管理(二)

--LinuxCPP1305

-13.06 进程管理(三)

--LinuxCPP1306

-13.07 进程间通信(一)

--LinuxCPP1307

-13.08 进程间通信(二)

--LinuxCPP1308

-13.09 进程间通信(三)

--LinuxCPP1309

-13.10 进程间通信(四)

--LinuxCPP1310

-13.11 进程池

--LinuxCPP1311

-13.12 编程实践

--LinuxCPP1312

-第十三讲 进程编程--编程实践提交入口

第十四讲 线程编程

-14.1 提纲

--LinuxCPP1401

-14.2 线程基本概念

--LinuxCPP1402

-14.3 线程管理(一)

--LinuxCPP1403

-14.4 线程管理(二)

--LinuxCPP1404

-14.5 线程管理(三)

--LinuxCPP1405

-14.6 线程管理(四)

--LinuxCPP1406

-14.7 线程同步机制(一)

--LinuxCPP1407

-14.8 线程同步机制(二)

--LinuxCPP1408

-14.9 C++11线程库(一)

--LinuxCPP1409

-14.10 C++11线程库(二)

--LinuxCPP1410

-14.11 C++11线程库(三)

--LinuxCPP1411

-14.12 C++11线程库(四)

--LinuxCPP1412

-14.13 C++11线程库(五)

--LinuxCPP1413

-14.14 编程实践

--LinuxCPP1414

-第十四讲 线程编程--编程实践提交入口

第十五讲 网络编程

-15.1 提纲

--LinuxCPP1501

-15.2 Internet网络协议

--LinuxCPP1502

-15.3 套接字(一)

--LinuxCPP1503

-15.4 套接字(二)

--LinuxCPP1504

-15.5 编程实践

--LinuxCPP1505

-第十五讲 网络编程--编程实践提交入口

课程文档

-课程PDF文件

LinuxCPP1307笔记与讨论

也许你还感兴趣的课程:

© 柠檬大学-慕课导航 课程版权归原始院校所有,
本网站仅通过互联网进行慕课课程索引,不提供在线课程学习和视频,请同学们点击报名到课程提供网站进行学习。