1.信号量
- 信号量和P,V原语由Dijkstra提出
- 信号量
(1)互斥问题: P,V在同一个进程中
(2)同步问题:P,V在不同进程中 - 信号量值含义,S表示计数值
(1)S>0:S表示可用资源的个数
(2)S=0:表示无可用资源,无等待进程
(3)S<0:|S|表示等待队列中进程个数,即:若当前资源计数值S=0,此时来一个进程申请资源,计数值减去1为-1,|-1|=1表示等待队列中的进程个数为1 - 信号量数据结构
struct semaphore
{
int value;//信号量计数值S
pointer_PCB queue;//将等待进程的控制块指针放到等待队列中
}
- P原语:进程申请资源
原语:这块代码是原子性的,不会被其它信号所中断,在硬件上,实际是通过关闭中断的方式来实现的。
P(s)
{
s.value=s.value--;
if (s.value<0)
{
该进程状态置为等待状态;
将该进程的PCB插入相应的等待队列s.queue末尾
}
}
- V原语:进程归还资源
若之前s.value<0说明有进程在等待资源,此时s.value++归还一个资源,意味着要唤醒一个进程
V(s)
{
s.value=s.value++;
if (s.vaue <=0)
{
唤醒相应等待队列s.queue中等待的一个进程,
改变其状态为就绪态;
并将其插入就绪队列中;
}
}
2.信号量集结构
- 信号量集结构struct semid_ds
struct semid_ds {
//IPC对象都有的
struct ipc_perm sem_perm; /* Ownership and permissions */
//信号量集所特有
time_t sem_otime; /* Last semop time *///最后一次执行PV操作的时间
time_t sem_ctime; /* Last change time *///信号量集最后一个改变的时间
unsigned long sem_nsems; /* No. of semaphores in set *///信号量集中的信号量的个数
};
3.信号量集函数
- 消息队列和共享内存有4个,而信号量集只有三个,因为semop涵盖了P和V操作
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
//创建或者打开一个信号量集
int semget(key_t key, int nsems, int semflg);
//控制一个信号量集
int semctl(int semid, int semnum, int cmd, ...);
int semop(int semid, struct sembuf *sops, size_t nsops);
- semget函数
功能:用来创建和访问一个信号量集
原型:
int semget(key_t key, int nsems, int semflg);
参数:
key:信号集的名字
nsems:信号集中信号量的个数
semflg:由9个权限标志构成,他们的用法和创建文件时使用的mode模式标志是一样的
返回值:
成功返回一个非负整数,即:该信号集的标识码;
失败返回-1;
- shmctl函数
功能:用于控制信号量集
原型:
int semctl(int semid, int semnum, int cmd, ...);
参数:
semid:由semget返回的信号集标识码
semnum:信号集中信号量的序号
cmd:将要采取的动作(有3个)
最后一个参数根据命令不同而不同
返回值:
成功返回0;
失败返回-1
- cmd
- eg:权限perms是666,信号量集中信号量的个数nsems为1
删除信号量集:
ipcrm -S key,eg:ipcrm -S 0x4d2
ipcs -Q semid
- eg:NetworkProgramming-master (1)\NetworkProgramming-master\P30sem.cpp
/
// Created by wangji on 19-8-13.
//
// p30 system v信号量(一)
#include <unistd.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <sys/sem.h>
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
// https://blog.csdn.net/a1414345/article/details/64513946
#define ERR_EXIT(m) \
do \
{ \
perror(m); \
exit(EXIT_FAILURE); \
} while (0);
//来自man semctl
union semun {
int val; /* Value for SETVAL */
struct semid_ds *buf; /* Buffer for IPC_STAT, IPC_SET */
unsigned short *array; /* Array for GETALL, SETALL */
struct seminfo *__buf; /* Buffer for IPC_INFO
(Linux-specific) */
};
// 仅仅创建信号量集
int sem_creat(key_t key)
{
int semid;
semid = semget(key, 1, IPC_CREAT | IPC_EXCL | 0666);//IPC_EXCL表示只能创建1次
if (semid == -1)
{
ERR_EXIT("semget");
}
return semid;//返回信号量集的id
}
//仅仅打开一个创建的信号量集
int sem_open(key_t key)
{
int semid;
semid = semget(key, 0, 0);//由于信号量集已经存在了,可以不指定信号量的个数;打开选项也可以不必关心
if (semid == -1)
{
ERR_EXIT("semget");
}
return semid;
}
// 设置信号量中信号量的值
int sem_setval(int semid, int val)
{
union semun su;
su.val = val;
int ret;
ret = semctl(semid, 0, SETVAL, su);
if (ret == -1)
{
ERR_EXIT("setval")
}
return 0;
}
// 获取信号量中信号量的值
int sem_getval(int semid)
{
int ret;
ret = semctl(semid, 0, GETVAL,0);//第4个参数忽略了,填写0
if (ret == -1)
{
ERR_EXIT("getval")
}
printf("sem.val = %d\n", ret);
return ret;
}
//只能删除信号量集,不能删除信号量集中的某个信号量
int sem_d(int semid)
{
int ret;
ret = semctl(semid, 0, IPC_RMID, NULL);//信号量个数不知道的话填写0
if (ret == -1)
{
ERR_EXIT("rm_sem")
}
return 0;
}
int main(int argc, char** argv)
{
int opt;
int semid;
semid=sem_creat(1234);
sleep(5);//过5s删除该信号量集
sem_d(semid);
}
- 测试:执行:./sem,过5s后,执行ipcs发现信号量集会被删除
2902