第三季-第16课-信号量互斥编程

第16课-信号量互斥编程

16.1 公示栏问题(问题引入)

1. 问题描述

这里面我们举一个小例子。在一个班级里就有一个公示栏,A同学想写“数学课考试”,B同学想写“英语课取消”。但是有一个时间,A同学只写下了“数学课”三个字,没来得及写后面的内容就出去了,但是这个时候B同学来写下了“英语课取消”。这样让同学们看来就成了“数学课英语课取消”,给班级的其他同学造成了歧义。

这也就是我们说的同时访问一个资源,造成了,数据的混乱。若是有多个进程同时访问一个资源,同样会造成这个问题。

2. 问题程序化

A同学用进程A代替,B同学用进程B代替,公示栏用文件来代替。我们在先关课程的文件夹里面建立student1.c和student2.c。

注:这里加入一个操作---ctil+shift+t可以完成复制一个一样的命令栏窗口。

stduent1程序:

#include<unistd.h>

#include<sys/types.h>

#include<sys/stat.h>

#include<fcntl.h>

#include<stdio.h>

void main()

{

int fd = 0;

//0.打开文件

fd = open("./board.txt",O_RDWR|O_APPEND,0777);

//1.向公告板文件里面写入“数学课”

write(fd,"math class",11);

//2.暂停休息

sleep(5);

//3.向公告板文件里写入“取消”

write(fd,"is cancel",11);

close(fd);

}

stduent2程序:

#include<unistd.h>

#include<sys/types.h>

#include<sys/stat.h>

#include<fcntl.h>

#include<stdio.h>

void main()

{

int fd = 0;

//0.打开文件

fd = open("./board.txt",O_RDWR|O_APPEND,0777);

//1.向公告板文件里面写入“英语课考试”

write(fd,"English exam",20);

//2.关闭文件

close(fd);

}

运行结果:我们开起两个中断在一个终端中运行:./stduent1,5s内在另一个终端中运行./stduent2。我们查看提前创建好的board.txt文件显示为:math class English exam



;is cancel

3. 信号量概念

为了能够是一个程序不会出现访问混乱的情况,我们可以说白一点,像厕所一样,有人进入就有标识牌显示有人,即使时间很长。从而达到了互斥的访问。这一个概念就是信号量。

l  概念:信号量(又名:信号灯)与其他进程间通讯方式不大相同,主要用途是保护临界资源(进程互斥)。进程可以根据它判定是否能够访问某些共享资源。除了用于控制外,还可以用于进程同步。

l  分类:

二值信号灯:信号灯的值只能取0或1;

计数信号灯:信号灯的值可以取任意非负值。

l  信号量使用流程:

1)         打开信号量,获得标示符

2)         利用标示符,进行一系列操作

16.2 函数学习

1. 创建/打开信号量

(1)函数名

semget

(2)函数原型

int semget(key_t key, int nsems, int semflg);

(3)函数功能

获取信号量集合(一个信号或者多个信号)的标示符(get a semaphore set identifier),当key所指的信号量不存在,并且semflg里面包含了IPC_CREAT,就会创建这个信号量集合。

(4)所属头文件

#include<sys/types.h>

#include<sys/ipc.h>

#include<sys/sem.h>

(5)返回值

成功:返回信号量集合的标示符;

失败:-1.

(6)参数说明

l  什么是“键值”:(文件创建有文件名,使用文件的时候有文件的fd(id号);ipc对象创建的时候有键值(一个数字),使用的时候有标示符)对于一个文件,我们只要找到文件名,我们就能找到文件id(fd),打开文件。对于一个信号量(ipc对象)无论使用还是没有使用都是具有键值的,我们要知道他的“键值”我们就能打开它的标示符。标示符是打开这个ipc对象才会有的,键值是无论打开与没打开都有的。无论是A进程还是B进程,我们要找到它的信号量,我们都要知道它的“键值”。在我们创建信号量的时候(创建之后就会产生),我们就为它指定了键值。键值如何选取呢(在ipc对象创建的时候)?这里我们给出两个方法:

(1)任意指定一个数

缺点:这个数已经被别的IPC对象(消息队列,共享内存)所使用了,在于新创建的信

量关联时就会失败。

(2)构造一个尽量不会被别的IPC对象用到的数的方法:使用key_t ftok(char *fname,int id)

这个函数有两个参数,第一个是文件名,第二个是项目id。根据这个文件名,我们可以得到一个数字,这个数字,是文件在内核里面保存文件信息的一个文字。将这个数字再和项目id组合起来,就形成我们所要的键值了。这个键值的数字的产生:内核中保存该文件名的结构中的一个数字 + 项目id,这样形成的一个数字。也就是我们知道文件名和项目id可以得到ipc对象的键值,知道ipc对象的键值可以得到ipc对象的标示符。

每个进程的标示符有唯一的键值与之对应。

key:打开的信号量对应的键值。

semflg:标志,可以取IPC_CREAT,当key所指的信号量不存在,并且semflg里面包含了IPC_CREAT,就会创建这个信号量集合。

nsems:创建的这个信号量集合里面包含的信号量数目,是个数字。

2. 操作信号量

(1)函数名

semop

(2)函数原型

int semop(int semid, struct sembuf *sops, unsigned nsopos);

(3)函数功能

操作信号量集合里面的信号量。

(4)所属头文件

#include<sys/types.h>

#include<sys/ipc.h>

#include<sys/sem.h>

(5)返回值

成功:0;

失败:-1.

(6)参数说明

semid:要操作的信号量集合(可以一个可以多个)的标示符。

sembuf:它包含下面的集合,

unsigned short sem_num;  /*semaphore number*/ 操作的信号量集合的编号

short        sem_op;   /*semphore operation*/ 正数表示释放(+1),负数表示获取(-1),不成功导致程序等待

short        sem_flg;   /*operation flags*/

nsops:一共操作了多少的信号量

sops:对信号量执行了什么样的操作,一个指针对应一个操作,若是有三个操作,就是组数为3的数组。

16.3 综合实例编程

A在使用黑板前获取信号量,然后开始使用黑板。使用黑板的时候,信号量的值应该是0。使用完了黑板释放信号量,信号量变成1。由于是A同学率先使用这个信号量的,所以要由A同学来创建这个信号量。

B同学在使用的时候在先获取信号量,能够获取的到,就直接使用。若在获取的过程中,发现这个信号量被A获取了,就等待直到A同学使用完毕。B同学使用完信号量,释放信号量。

stduent1.c

#include<unistd.h>

#include<sys/types.h>

#include<sys/stat.h>

#include<fcntl.h>

#include<stdio.h>

#include<sys/ipc.h>

#include<sys/sem.h>

void main()

{

int fd = 0;

int semid;

key_t key;

struct sembuf sops;

//创建信号量的键值

key = ftok("/home",1); //使用文件名,和数字的方式,可以任意组合。

//创建并打开信号量

semid = semget(key,1,IPC_CREAT);//在系统中没有这个信号量,用参数IPC_CREAT;//就用一个信号量,使用数字1

//0.打开文件

fd = open("./board.txt",O_RDWR|O_APPEND,0777);

//1.1 获取信号量

sops.sem_num = 0;  //我们的信号量集合里面只有一个信号,我们操作的信号的序//号一定是0

sops.sem_op = -1;   //-1表示获取这个信号量

semop(semid,&sops,1); //操作一个信号量,第三个参数用1;第二个参数表示执行什么样的操作

//1.向公告板文件里面写入“数学课”

write(fd,"math class",11);

//2.暂停休息

sleep(10);

//3.向公告板文件里写入“取消”

write(fd,"is cancel",11);

//释放信号量

sops.sem_num = 0; //第一个信号量

sops.sem_op = 1;  //释放信号量,+1的操作

semop(semid,&sops,1);

close(fd);

}

Stduent2.c

#include<unistd.h>

#include<sys/types.h>

#include<sys/stat.h>

#include<fcntl.h>

#include<stdio.h>

#include<sys/ipc.h>

#include<sys/sem.h>

void main()

{

int fd = 0;

int semid;

key_t key;

struct sembuf sops;

//创建信号量的键值

key = ftok("/home",1); //保证两个学生操同一个信号量,键值的组成是一样的。

//打开信号量集合

semid = semget(key,1,IPC_CREAT);

//0.打开文件

fd = open("./board.txt",O_RDWR|O_APPEND,0777);

//获取信号量

sops.sem_num = 0;

sops.sem_op = -1;

semop(semid,&sops,1);

//1.向公告板文件里面写入“英语课考试”

write(fd,"English exam",12);

//释放信号量

sops.sem_num = 0;

sops.sem_op = 1;

semop(semid,&sops,1);

//2.关闭文件

close(fd);

}

运行结果:当我们在两个终端中分别运行./student1和./student2时,我们按道理应该看到的情况是,第一个程序在等待,第二个程序也在等待,直到第一个程序完成。并且写入的数据不混乱。但是,我们这两种期望都没看到。运行B程序,瞬间就结束了;写入的数据也不对。这里我们忽略了一个东西,我们前面一直在做一个假设,就是信号量的初始值是1。

实际的信号量的值不一定是1,可能是4、5、6等其他值。

这里我们引入另一个函数:semctl

函数原型

int semctl(int semid , int sennum, int cmd,.../*union semum arg*/);  //有10种,我们用到GETVAL这个命令

下面在student.c加入这两行命令:    int ret;

ret = semctl(semid,1,GETVAL);

发现它的初始值是:-1;

下面是更改后的程序

student1.c

#include<unistd.h>

#include<sys/types.h>

#include<sys/stat.h>

#include<fcntl.h>

#include<stdio.h>

#include<sys/ipc.h>

#include<sys/sem.h>

void main()

{

int fd = 0;

int semid;

key_t key;

int ret;

struct sembuf sops;

//创建信号量的键值

key = ftok("/home",1);

//创建并打开信号量集合

semid = semget(key,1,IPC_CREAT);

ret = semctl(semid,0,SETVAL,1);  //给信号量赋初值:1

printf("init value of sem is %d\n",ret);

//0.打开文件

fd = open("./board.txt",O_RDWR|O_APPEND,0777);

//1.1 获取信号量

sops.sem_num = 0;

sops.sem_op = -1;

semop(semid,&sops,1);

//1.向公告板文件里面写入“数学课”

write(fd,"math class",11);

//2.暂停休息

sleep(10);

//3.向公告板文件里写入“取消”

write(fd,"is cancel",11);

//释放信号量

sops.sem_num = 0;

sops.sem_op = 1;

semop(semid,&sops,1);

close(fd);

}

student2:

#include<unistd.h>

#include<sys/types.h>

#include<sys/stat.h>

#include<fcntl.h>

#include<stdio.h>

#include<sys/ipc.h>

#include<sys/sem.h>

void main()

{

int fd = 0;

int semid;

key_t key;

struct sembuf sops;

int ret;

//创建信号量的键值

key = ftok("/home",1);

//打开信号量集合

semid = semget(key,1,IPC_CREAT);

//0.打开文件

fd = open("./board.txt",O_RDWR|O_APPEND,0777);

ret = semctl(semid,0,GETVAL); //看看信号量的值是多少

printf("ret is %d\n",ret);

//获取信号量

sops.sem_num = 0;

sops.sem_op = -1;

semop(semid,&sops,1);

//1.向公告板文件里面写入“英语课考试”

write(fd,"English exam",12);

//释放信号量

sops.sem_num = 0;

sops.sem_op = 1;

semop(semid,&sops,1);

//2.关闭文件

close(fd);

}

运行结果:在两个终端中分别运行./student1和./student2。我们能看到等待的出现。board.txt中的内容是:math class is cancel English exam

这就是我们要的结果。

原文地址:https://www.cnblogs.com/free-1122/p/11351438.html

时间: 2024-08-01 00:25:45

第三季-第16课-信号量互斥编程的相关文章

第三季-第17课-信号量同步编程

第17课-信号量同步编程 17.1 核心概念--进程同步 一组并发进程进行互相合作.互相等待,使得各进程按一定的顺序执行的过程称为进程间的同步. 17.2 生产者消费者问题 1. 问题描述 这里面有两个角色:生产者和消费者.假设生产者生产的产品需要两步才能完成并且使用.但是,当生产者刚刚完成了对产品的第一步加工的时候,产品就被消费者买走了.可是他们之间并没有相应的沟通,这就导致,消费者会以为他买到了完成的产品. 2. 程序化 在文件夹里面创建producer.c和customer.c文件,同时创

Linux 信号量互斥编程

所谓信号量,其实就是一个数字.内核给这个数字赋予一定的含义,让它等于不同的值时所表示的意义不同.这样就可以用它来标示某种资源是否正被使用.信号的分类其实挺多的,主要还是二值和计数器.这里讨论二值 现在有个文件,有两个进程要同时访问它.进程A 要往里面写入 "Math class is cancel",进程B 要往里面写入“English test”.正常情况下这两个信息会被完整的写入文件中.但是如果进程A写到"Math class" 就暂停,接着B进程就开始写“En

linux应用开发-信号量互斥编程

linux应用开发-信号量互斥编程 一 相应的函数 1 创建/打开信号量集合 函数名 semget 函数原形 int semget(key_t key, int nsems, int semflg) 函数功能 获取信号量集合的标识符 当key所指的信号量不存在的时候,且semflg里包含了IPC_CREAT,就会创建一个信号量的集合 所属头文件 <sys/types.h> <sys/ipc.h> <sys/sem.h> 返回值 成功返回信号量的标识符 失败返回-1 参数

信号量互斥编程

在程序中利用信号量互斥来解决公示板问题 背景知识 1.信号灯的概念 信号灯,又被称为信号量(semaphore),是IPC(进程间通信)的方式之一.它可以用来保证两个或多个关键代码段不被多个进程并发调用.每个信号灯都有个semval,用于记录信号灯的值.在进入一个关键代码段之前,进程必须获取一个信号量,使semval减1:一旦该关键代码段完成了,那么该进程必须释放信号量,使semval加1.其它想进入该关键代码段的进程,如果semval是0,就必须等待直到第一个进程释放信号量. 2.信号灯的用法

第三季-第15课-信号通讯编程

第15课-信号通讯编程 15.1 核心理论 1. 信号 在古老的战场上,信号是最有效,最直接的通讯方式:在linux系统中,信号(signal)同样也是最古老的进程间通讯机制. 2. 信号处理流程 进程A/内核---(1)选择信号-----(2)发送信号-----(3)处理信号----进程B. 3. 信号类型 Linux系统支持的所有信号均定义在/usr/include/asm/signal.h(展示),其中常见的信号有: SIGKILL:杀死进程 SIGSTOP:暂停进程 SIGCHLD:子进

16.信号量互斥编程

我们先来看一个例子.就是两个进程访问同一个文件,由于线程的先后,导致内容的异常.即是数据内容的混乱. Student1.c: #include <unistd.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> ? void main(){ ????//open file ????int fd = 0; ????fd = open("/home/wen",O

第15课-信号量同步编程

1.进程同步:各进程按照一定的顺序执行的过程.异步就不一定有顺序,而是随机的执行.一组并发进程进行互相合作,互相等待,使得各进程按照一定的顺序执行的过程.2.system系统调用可以按照字符串方式调用系统命令.3.生产者只需要释放信号量:消费量只需要获取而不需要释放4.信号量初始值等于0,在互斥通讯中大于05.生产者:(1)创建信号量集合的键值(ftok函数)(2)创建信号量(semget函数)(3)设置信号量初始值为0(semctl(信号量ID,第几个信号量,什么操作[,欲设置的值])函数)(

第14课-信号互斥编程

1.产生背景:有些资源呗多个进程同时访问的时候,可能出现数据混乱的现象2.定义:信号量有名信号灯.主要是用来保护临界资源(进程互斥).进程可以根据他判定是否可以访问临界资源.还可以用于进程同步.实质:一个数字操作:获取和释放.利用信号量的值判断是否可以操作,大于0可以.0不可以.所以在创建操作之前一般要保证初始值是1.可以使用semctl函数来获取和设置初始值.3.分类:(1)二值信号量:信号灯的值只能是0和1(2)计数信号量:信号灯的值可以取任意非负值4.和文件的区别(1)打开信号量得到标识符

Linux 信号量同步编程

前一篇文章概述了Linux 系统中信号量互斥编程,这篇文章正好是前一篇的姊妹篇----信号量同步.说它们是姊妹篇是因为它们都是利用了内核的信号量机制实现了进程间的通信.因为两者所解决的问题不同,因此它们使用的场景就会有所区别. 信号量互斥主要解决的问题是:进程间需要同时访问某种资源,但是它们对资源的操作会互相影响对方的操作结果,因此需要一种机制实现让进程在访问资源时能禁止其他进程访问相同的资源.而信号量同步则解决了另一个经典问题:生产者和消费者之间的协同工作问题. 首先描述一下生产者和消费者问题