四、同步互斥方式 --- 有名信号量
1、同步互斥的方式有哪些?
1)信号量 --- 进程
2)有名信号量 --- 进程
3)无名信号量 --- 线程
4)互斥锁 --- 线程
5)读写锁 --- 线程
2、什么是同步互斥?为什么要进行同步互斥?
同步互斥就是为了解决进程/线程在处理任务时有先后顺序,为了防止多个线程同时去抢占同一个资源。
3、有名信号量函数接口?
1)创建并打开一个有名信号量? --> sem_open() --> man 3 sem_open
功能: initialize and open a named semaphore
//初始化并且打开一个有名信号量
使用格式:
#include <fcntl.h>
#include <sys/stat.h>
#include <semaphore.h>
sem_t *sem_open(const char *name, int oflag); ---> 只打开
sem_t *sem_open(const char *name, int oflag, mode_t mode, unsigned int value); ---> 创建并打开
参数:
name:有名信号量的名字,要求必须以"/"开头,例如"/sem_test" --> 最终这个文件会在/dev/shm/下目录创建
/xxxx -> 在/dev/shm出现 sem.xxxx
oflag:O_CREAT --> 不存在则创建
--> 存在就会忽略
mode:八进制权限,例如:0777
value:有名信号量的起始值
返回值:
成功:有名信号量的地址 sem_t *
失败:SEM_FAILED
2)有名信号量的p操作? --> sem_wait() --> man 3 sem_wait
p操作: 资源数-1操作
#include <semaphore.h>
int sem_wait(sem_t *sem);
参数:
sem:有名信号量的地址
返回值:
成功:0
失败:-1
如果当前的值为2,那么sem_wait()就会马上返回,并且将值设置为1。
如果当前的值为1,那么sem_wait()就会马上返回,并且将值设置为0。
如果当前的值为0,那么sem_wait()就会阻塞。
3)有名信号量的v操作? --> sem_post() --> man 3 sem_post
#include <semaphore.h>
int sem_post(sem_t *sem);
参数:
sem:有名信号量的地址
返回值:
成功:0
失败:-1
如果当前的值为0,那么sem_post()就会马上返回,并且设置为1。
如果当前的值为1,那么sem_post()就会马上返回,并且设置为2。
如果当前的值为2,那么sem_post()就会马上返回,并且设置为3。
4)如何关闭有名信号量? --> sem_close() --> man 3 sem_close
#include <semaphore.h>
int sem_close(sem_t *sem);
参数:
sem:有名信号量的地址
返回值:
成功:0
失败:-1
5)如何删除有名信号量? --> sem_unlink() --> man 3 sem_unlink
#include <semaphore.h>
int sem_unlink(const char *name);
参数:
name:有名信号量的名字
返回值:
成功:0
失败:-1
例题: 已知shm/目录下的代码会出现数据践踏,那么现在请让你把有名信号量添加到该代码中,解决数据践踏的问题。
写端:
#include <sys/shm.h>
#include <string.h>
#include <stdio.h>
#include <semaphore.h>
#include <fcntl.h>
#include <sys/stat.h>
int main(int argc,char *argv[])
{
//1. 申请key值
key_t key = ftok(".",10);
//2. 根据key值去申请共享内存的ID号
int shmid = shmget(key,1024,IPC_CREAT|0666);
//3. 根据ID号去共享内存中申请空间
char *p = shmat(shmid,NULL,0);
if(p == (void *)-1)
printf("shmat error!\n");
//3.5 创建并打开一个有名信号量
sem_t *sem = NULL;
sem = sem_open("/sem_test",O_CREAT,0777,0);
if(sem == SEM_FAILED)
printf("sem_open error!\n");
//4. 不断往共享内存中写入数据
while(1)
{
//开车进去
fgets(p,1024,stdin);
//车的数量自动加+1
sem_post(sem);
if(strncmp(p,"quit",4) == 0)
{
break;
}
}
return 0;
}
读端:
#include <sys/ipc.h>
#include <sys/shm.h>
#include <string.h>
#include <stdio.h>
#include <unistd.h>
#include <semaphore.h>
#include <fcntl.h>
#include <sys/stat.h>
int main(int argc,char *argv[])
{
//1. 申请key值
key_t key = ftok(".",10);
//2. 根据key值去申请共享内存的ID号
int shmid = shmget(key,1024,IPC_CREAT|0666);
//3. 根据ID号去共享内存中申请空间
char *p = shmat(shmid,NULL,0);
if(p == (void *)-1)
printf("shmat error!\n");
//3.5 创建并打开一个有名信号量
sem_t *sem = NULL;
sem = sem_open("/sem_test",O_CREAT,0777,0);
if(sem == SEM_FAILED)
printf("sem_open error!\n");
//4. 每隔2秒就打印一次
while(1)
{
//请问车的数量能不能减1?
sem_wait(sem);
printf("from shm:%s",p);
if(strncmp(p,"quit",4) == 0)
{
break;
}
}
//5. 撤销映射
shmdt(p);
//6. 删除共享内存IPC对象
shmctl(shmid,IPC_RMID,NULL);
//7. 关闭有名信号量
sem_close(sem);
//8. 删除有名信号量
sem_unlink("/sem_test");
return 0;
}
五、同步互斥方式 --- 无名信号量
1、无名信号量作用机制?
无名信号量不是一个文件,而是一个变量,所以无名信号量只能作用于同一个文件中,所以作用范围是线程之间。
机制:当某一个线程能抢占无名信号量的资源时,就可以访问公共资源。
如果抢占不到,就只能阻塞等待。
2、关于无名信号量的函数接口。
由于无名信号量只是一个变量,不是一个文件,所以不能打开,只能初始化。
1)定义一个无名信号量的变量。 --> 数据类型: sem_t
sem_t sem;
2)如何初始化无名信号量? --> sem_init() --> man 3 sem_init
功能: initialize an unnamed semaphore
//初始化一个无名信号量
使用格式:
#include <semaphore.h>
int sem_init(sem_t *sem, int pshared, unsigned int value);
参数:
sem: 无名信号量的地址
pshared: 0 --> 线程之间
非0 --> 亲缘关系进程之间
value: 无名信号量的起始值
返回值:
成功:0
失败:-1
2)有名信号量的p操作? --> sem_wait() --> man 3 sem_wait
p操作: 资源数-1操作
#include <semaphore.h>
int sem_wait(sem_t *sem);
参数:
sem:有名信号量的地址
返回值:
成功:0
失败:-1
如果当前的值为2,那么sem_wait()就会马上返回,并且将值设置为1。
如果当前的值为1,那么sem_wait()就会马上返回,并且将值设置为0。
如果当前的值为0,那么sem_wait()就会阻塞。
3)有名信号量的v操作? --> sem_post() --> man 3 sem_post
#include <semaphore.h>
int sem_post(sem_t *sem);
参数:
sem:有名信号量的地址
返回值:
成功:0
失败:-1
如果当前的值为0,那么sem_post()就会马上返回,并且设置为1。
如果当前的值为1,那么sem_post()就会马上返回,并且设置为2。
如果当前的值为2,那么sem_post()就会马上返回,并且设置为3。
4)如何销毁无名信号量? --> sem_destroy() --> man 3 sem_destroy
#include <semaphore.h>
int sem_destroy(sem_t *sem);
参数:
sem: 无名信号量的地址
返回值:
成功:0
失败:-1