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

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

LinuxCPP1308在线视频

LinuxCPP1308

下一节:LinuxCPP1309

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

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

接下来一节是进程信号量

我们这里面讲到的是

UNIX System V里面的信号量 system V

可以使用同步机制

来确定进程的先后执行关系

用的就是进程信号量

要讲进程信号量啊

同学们首先就必须清楚什么叫信号量

信号量是什么东西呢

信号量是一类特殊的计数器

它的值是非负的整数

用于进程或线程的同步

最主要的目的就是干这个事情

信号量有两个主要的操作

一个是等待

我们称之为P操作

有时称之为P原语

信号量的值会递减1然后返回

如果它的值为0

那么就会阻塞这个操作

就减不了 就阻塞

直到信号量的值为正了

然后它才能够继续地去减1 才能返回

它的最主要的目的就是为了控制

两个进程或线程之间的同步嘛

它就使用这个信号量的值

来去做到这一点

你可以访问这个值

你如果需要想用 你就减1

就相当于它有一个资源

这个资源上有一个标记

这个标记就是用一个数值来表示的

这个数值就是信号量

你比如说一个标准的二元信号量

就是1或0 这个概念

就表示这个资源在任意一个时刻

只能有一个进程去访问它

一旦一个进程能够访问它

原来值为1

那么一递减 它一等待它

得到了 就会变成0

那么其它的进程就不能够得到它了嘛

这个时候其它的进程就会被阻塞

一直等到第一个进程用完了那个资源

做了一次发布操作

我们称为V操作 也称V原语

这个时候信号量的值就会递增1

它用完了 这个信号量就递增了

递增1以后

原来那个等待这个信号量的一个进程呢

就会被唤醒

它就可以去做减1

就可以访问那个资源了

信号量做的就是这个事

所以你看 这里主要两个操作

一个是等待操作 一个是发布操作

进程信号量呢

它只是一类特殊的信号量

它的操作的原理依然是这个模样

只不过它等待和发布的时候

这个信号量的数值并不固定为1

同时它定义的

也不是一个信号量而是一组信号量

在Linux操作系统下边

信号量有两个实现版本

一个就是我们今天讲的进程信号量

它多用于进程同步

另外一个是POSIX标准

它主要用于线程同步

那个版本要比我们这个进程信号量

稍微简单一些

使用进程信号量的时候

是需要特别注意的

每次创建和管理的这个信号量

它不是一个 而是一个组

一个集合 一个数组

这个集合里可能包含很多个信号量

看你定义的需要

你需要一个 就定义一个

你需要两个 就定义两个

你需要几个 就定义几个

它是一个数组 不是一个单一的信号量

这是第一点要知道的

在进程里边是使用键值

来关联这个对应的进程信号量的

但进程内部操作这个进程信号量的时候

实际上并不使用这个键值

使用的是semid标识

就是这个进程信号量的ID

你可以这么理解

就相当于在进程之间进行同步的时候

semid是对内的 key是对外的

你创建和管理

想获取这个进程信号量

用的就是key

得到这个进程信号量之后

内部想用它 用的就是semid

semget()这个函数

就用于创建或获取进程信号量的集合

semget()这个函数的原型

第一个参数带着key键值

第二个参数就是信号量的元素个数

你想创建几个信号量

第三个就是信号量的创建标志

特别注意的就是第二个参数

它表示这个信号量的集合元素个数

它不是一个数组嘛 数组元素个数

不是这个信号量所对应的信号数

那个是另外一种设置的方式

如果你要获取

已经分配好了的进程信号量 那个集合

那么你就使用原先那个键值去获取它

调用这个函数

用这个查询 它就能够得到它

得到了 就会给你返回

使用一个特定的键值叫IPC_PRIVATE

就可以创建一个当前进程的

私有的信号量

其实说私有并不妥当

并不是真正私有

会创建一个新的

和其它的键值不同的进程信号量

就是创建一个新的信号量

要控制和管理进程信号量集合

就要用semctl()这个函数

传semid 传semnum 传cmd

后面还跟着一系列的可选参数

可以有 也可以没有

semid就是进程信号量的标志符

semnum就是那个信号量集的元素下标

你想控制哪一个

有的时候可能是全部都控制

那就是通过cmd那个指定操作来提供的

第四个参数arg 也许有 也许没有

有 情况也可能不一样

具体它有什么

是和cmd那个命令是有关的

你调用semctl()的时候要特别注意

进程信号量的控制和管理

它是有一个非常明确的权限要求的

如果你的进程没有这个权限

那么你是没有办法

去控制和管理进程信号量的

这一点也是需要注意的

如果你需要释放或者删除这个进程信号量集

那你就使用IPC_RMID这个命令

把cmd这个命令传这个——IPC_RMID

就可以删除那个进程信号量

这个时候它的第二个参数semnum是忽略的

你简单传0 传什么它都不管

第四个 它也不需要

当你在真正管理和控制进程信号量的时候

当你需要第四个参数的时候

那么这第四个参数的型

就是union semun这个型

这个型 你的操作系统中

可能没有给你定义

那么你就必须补充它的定义

这个定义必须是这个模样

union semun { int val; struct semid_ds * buf;

unsigned short int * array; struct seminfo * __buf; }

固定的

union是一个非常特殊的结构

我们此前没有讲过

它和struct那个结构体的定义

是非常非常类似的

唯一的差别就是union中定义的所有的字段

共享同样的存储空间

也就是说 在任何时候

它就只有一个字段是有意义的

其它字段表达的是同样的存储区

但是那个型的解释对我们是没有意义的

所以特别注意这一点

union是共用同样存储区的一个字段的集合

它这个字段的序列和struct是不一样的

struct每一个字段是连续存储的

而union所有的字段

是存在同样一个地址区的 同一个位置

任何时候我们只能使用其中一个字段

初始化进程信号量

就要使用SETALL这个命令

第一个参数

当然传的是进程信号量集的标识符

第二参数 你设为0就完了

第三个参数 就是它的cmd要设成SETALL

它就会全部给你设定

初始化这个进程信号量

设定每一个进程信号量它的值是多少

设定它的初始值是多少

这个时候你就要传第四个参数arg

必须设定好那个union的array那个字段

标记这些元素的

每一个进程信号量的信号数是多少

然后把那个数组基地址传进去

就按照这个模式初始化进程信号量集

还有一些常用的命令我们就不解释了

你比如讲 我们看这样的一段代码

我们可以实现一个函数AcquireBinarySemaphore()

获取一个二元信号量

所谓二元信号量就是它的值

信号量的值只能是0或者1

要么是0要么是1 就叫二元信号量

我们可以获取它

传一个键值进去

它的信号量标记进去

我们调用semget()就能获取这个信号量

如果一开始没有 它就给你创建

如果有了

它就会给你返回那个信号量的ID

ReleaseBinarySemaphore()

就会释放这个二元信号量

我们会调用semctl() 传IPC_RMID

把它清除掉

初始化这个二元信号量

就必须定义这样一个短整型数组

我们这里面就1个信号量

这个数组里就1个元素

所以就传了一个1

如果有2个 你就得传2个元素

特别注意这一点

然后把union这个字段array

初始化成我们数组的基地址

这是初始化记号 就是“.array”

就只初始化union的这个array字段

其它字段我们不初始化

你这里有两个进程信号量

那么数组上就要包括两个信号量值

如果是一个 就只写一个

把这个数组基地址传进去

semun那个联合体、共用体

不就被我们定义好了吗

然后我们调用semctl()

发送SETALL命令

就会把所有的进程信号量的初始值

都给你设定好

它的信号数就会被你设定好

就使用array这里边的数据设定好

刚才不说了嘛

一个进程信号量 它是一个集合

里面肯定好多个元素

每个元素的进程信号数就会一一设定好

这就是信号量的获取、释放

包括初始化

进程信号量最重要的两个操作——等待和发布

被合并成了一个函数 semop()

第一个参数semid

进程信号量的ID

第二个参数是指向sembuf

这样结构体的指针 sops

成功的时候

就会返回这个进程信号量的标识符

失败的时候就会返回-1

设errno的值

struct sembuf 这个型

它有两个最重要的成员

一个是sem_num

你需要操作的进程信号集中的

那个信号量的元素下标 这是一个

第二个sem_op 就是你想做的操作

它就用一个简单的整数来表达

你传1就表示把信号量加1

那就是V操作

你减1就表示把信号量值减1

信号数减1

那就是一个P操作

同时你还可以减2、加2 这都可以去做

如果不满足信号量操作的要求

进程就会被阻塞 注意这一点

第三个参数就是sem_flg

调用这个函数的时候

它的那个标记

访问操作的操作标志

就是调用这个semop()这个函数的时候

它的操作标志

sem_flg一般情况下

你指定成IPC_NOWAIT

它就会不阻塞我们的进程

它就不等待了嘛

如果你指定SEM_UNDO

那就在进程退出的时候

它就会做取消操作

就把它的P操作、V操作都给取消掉

就这个意思

我们看实际的例子

我们可以写一个函数

叫WaitBinarySemaphore()

传semid进去

我们设定好它的sembuf这个结构体数组

设定好包括它的操作

我们是要减1 当然是P操作

标记是SEM_UNDO 然后调用semop()

调用这个函数去执行它 就OK了

如果是一个post操作

那么PostBinarySemaphore()这个函数的实现

具体的代码非常非常类似

就是一个 sem_op那个字段

从-1变成了1 这就是一个post

所以等待也好 发布也好

它的操作的逻辑是一样的

仅仅是一个字段的差别

基于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文件

LinuxCPP1308笔记与讨论

也许你还感兴趣的课程:

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