公示栏案例引入
a同学写完“数学课”后出去接电话,b同学进来写了“英语课考试”,a同学后来又进来,写了“取消”,从而导致其他的同学看了产生歧义。
程序逻辑设计:
代码
student1.c
#include <fcntl.h>
#include<unistd.h>
void main(){
int fd = 0;
fd = open("./board.txt",O_RDWR|O_APPEND);
/*写入 “数学课” */
write(fd,"class math ",11);
sleep(10);
/* 写入“取消” */
close(fd);
}
~
~
~
~
~
student2.c
#include<sys/types.h>
#include<sys/stat.h>
#include<unistd.h>
#include<fcntl.h>
void main(){
int fd = 0;
fd = open("./board.txt",O_RDWR|O_APPEND);
/* 写入"英语课考试" */
write(fd,"enlish exam ",20);
close(fd);
}
~
~
运行结果:
什么是信号量:
叙述一下:a进程 想操作文件前,先看 “标志” 是否为1,如果为1,则可以操作文件,【然后进行-1操作】。
如果 “标志” 为0,则等待…
这个标志 其实就是 信号量
信号量的实质:就是一个数字
图示中:获取 就是 减法 操作,释放就是加法操作
信号量的概念:
信号量的分类:
比如数值的初始值为2则为 计数信号量
总体阐述:信号量操作 和文件操作 类比
打开信号量后可以获取到一个标示符,这个标示符和 文件描述符fd相类似。
键值是什么?
类比文件
通过 文件名 可以找到 文件
通过 键值 可以找到 信号量【即 键值和信号量进行关联】
如何指定键值
推荐使用:ftok()函数
ftok()函数的原理:
创建/打开信号量 函数
操作信号量semop()
注意:nsops:要操作几个信号量【之前也说过是信号量集合】
如果nsops>1,则参数sops应该为一个数组。
结构体struct sembuf*的说明:
struct sembuf {
u_short sem_num; /* semaphore # /
short sem_op; / semaphore operation /
short sem_flg; / operation flags */
};
注意:图中的1,2,3,4【0,1,2,3】不要过度关心。
sem_num:之前 创建/打开的是信号量集合,sem_num指的是这个集合中的第几个信号量
sem_op:假如sem_op为负数,获取不到的时候会等待
代码实例:利用信号量互斥 解决公示栏问题:
逻辑设计
student1.c
#include <fcntl.h>
#include<unistd.h>
void main(){
int fd = 0;
key_t key;
int semid;
struct sembuf sops;
//创建键值
key = ftok("/home",1);//第一个参数:文件名 ; 第二个参数:项目id
//创建并打开信号量集合
semid = semget(key,1,IPC_CREAT);//第二个参数:创建的这个信号量集合中包含一个信号量
fd = open("./board.txt",O_RDWR|O_APPEND);
//获取信号量
sops.sem_num = 0;//集合中第几个信号量
sops.sem_op = -1;
semop(semid,&sops,1);//第三个参数为一共要操作多少个信号量
/*写入 “数学课” */
write(fd,"class math ",11);
sleep(10);
/* 写入“取消” */
write(fd,"is cancel ",11);
//释放信号量
sops.sem_num = 0;//集合中第几个信号量
sops.sem_op = 1;
semop(semid,&sops,1);//第三个参数为一共要操作多少个信号量
close(fd);
}
student2.c
问题:b同学怎么找到a的这个信号量那?
#include<fcntl.h>
#include<sys/sem.h>
void main(){
int fd = 0;
key_t key;
int semid;
struct sembuf sops;
//创建键值
key = ftok("/home",1);
//创建/打开信号量集合
semid = semget(key,1,IPC_CREAT); //注意:此处即使加上了IPC_CREAT,但是系统发现key已经关联了一个信号量集合,所以调用semget()
函数不会再去创建一个信号量集合,而是打开已经有的信号量集合
fd = open("./board.txt",O_RDWR|O_APPEND);
//获取信号量
sops.sem_num = 0;
sops.sem_op = -1;
semop(semid,&sops,1);
/* 写入"英语课考试" */
write(fd,"enlish exam ",20);
//释放信号量
sops.sem_num = 0;
sops.sem_op = 1;
semop(semid,&sops,1);
close(fd);
}
运行效果不符合预期:
经过在本机测试:本机中创建信号量集合以后 的信号量初始值是0,【视频中默认初始值好像是2,】究竟默认初始值为多少本人也不清楚,每台机器的现象可能不一致
问题原因:信号量的初始值不为1,在操作信号量前一定要通过semctl()检查信号量的初始值为多少
检查信号量的初始值:使用semctl()函数
修改以后的程序
student1.c
#include <sys/types.h>
#include <sys/ipc.h>
#include<stdio.h>
#include <sys/sem.h>
#include <fcntl.h>
#include<unistd.h>
void main(){
int fd = 0;
key_t key;
int semid;
struct sembuf sops;
int ret;
//创建键值
key = ftok("/home",1);//第一个参数:文件名 ; 第二个参数:项目id
printf("key value is: %d\n",key);
//创建并打开信号量集合
semid = semget(key,1,IPC_CREAT);//第二个参数:创建的这个信号量集合中包含一个信号量
printf("semid is: %d\n",semid);
//检查信号量的初始值
ret = semctl(semid,0,SETVAL,1);
ret = semctl(semid,0,GETVAL);//第二个参数0代表信号量在信号量集合中的序号
printf("semctl_getval ret is %d\n",ret);
fd = open("./board.txt",O_RDWR|O_APPEND);
//获取信号量
sops.sem_num = 0;//集合中第几个信号量
sops.sem_op = -1;
ret = semop(semid,&sops,1);//第三个参数为一共要操作多少个信号量
printf("semop ret is: %d\n",ret);
/*写入 “数学课” */
write(fd,"class math ",11);
sleep(10);
/* 写入“取消” */
write(fd,"is cancel ",11);
//释放信号量
sops.sem_num = 0;//集合中第几个信号量
sops.sem_op = 1;
semop(semid,&sops,1);//第三个参数为一共要操作多少个信号量
close(fd);
}
student2.c
#include<sys/types.h>
#include<sys/stat.h>
#include<unistd.h>
#include<fcntl.h>
#include<sys/sem.h>
void main(){
int fd = 0;
key_t key;
int semid;
struct sembuf sops;
int ret;
//创建键值
key = ftok("/home",1);
//创建/打开信号量集合
semid = semget(key,1,IPC_CREAT); //注意:此处即使加上了IPC_CREAT,但是系统发现key已经关联了一个信号量集合,所以调用semget()函数不会再去创建一个信号量集合,而是打开已经有的信号量集合
fd = open("./board.txt",O_RDWR|O_APPEND);
ret = semctl(semid,0,GETVAL);
printf("semctl_GETVAL ret is: %d\n",ret );
//获取信号量
sops.sem_num = 0;
sops.sem_op = -1;
semop(semid,&sops,1);
/* 写入"英语课考试" */
write(fd,"enlish exam ",20);
//释放信号量
sops.sem_num = 0;
sops.sem_op = 1;
semop(semid,&sops,1);
close(fd);
}
运行结果:
b进程开启后等待,a,b全部结束后,查看board.txt的内容为: