22信号和信号量
Posted gd-luojialin
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了22信号和信号量相关的知识,希望对你有一定的参考价值。
信号量:为控制临界资源而产生的一个或一组计数器,本质上是一个整数变量。用于进程的互斥操作
信号量基本操作
P 操作
进程申请临界资源时发出 P 操作
流程:检查信号量取值,
> 0 则分配临界资源,信号值-1;
否则表示无空余资源,进程阻塞直到指定资源被释放
V 操作
进程释放临界资源时发出 V 操作
流程:释放临界资源,信号值+1
注意:P 操作和 V 操作都是原子操作
信号与信号量
进程通过kill,sigqueue函数发送信号,通知另一个进程开始工作,实现进程之间同步
进程通过设置信号量,阻塞另一个进程继续工作,实现进程之间互斥
二值信号量:信号量只有 0 和 1 两种值
多值信号量:信号量取值为临界资源数量
步骤:
信号量使用:
1: ftok 使用某个文件做关键字创建 key
2: semget 使用key创建信号集 semid
3: semctl 初始化信号集(SETALL)
3: semop P V 操作,更新信号量
4: semctl 删除信号量集
创建信号量集
<sys/types.h>
<sys/ipc.h>
<sys/sem.h>
int semget(key_t key, int nsems, int semflg)
nsems 表示信号集中信号量个数,0 则不创建信号量,返回 semid
创建信号量
信号量集操作
<sys/types.h>
<sys/ipc.h>
<sys/sem.h>
int semop(int semid, struct sembuf *sops, unsigned nsops)
参数:
semid 指定信号量集
sops 指定操作集合数组
nsops 需要同时操作的信号量个数( sops 数组长度)
信号量集操作
struct sembuf {
unsigned short sem_num; //信号量序号
short sem_op; //要执行的操作
short sem_flg; //操作相关的标志位
}
P 操作的时候,任何一个资源不够,都会导致阻塞
参数:
sem_op
正数: 释放资源,增加信号量(V操作)
负数: 获取资源,减少信号量(P操作)(资源不够则阻塞)
0 等待当前信号量值变为0 (非0则阻塞) (Z操作)
sem_flg
0 默认,阻塞
IPC_NOWAIT: 资源不够不阻塞,立即返回
SEM_UNDO: 程序异常退出(申请了资源,但是没有释放) 时,系统会自动释放它申请信号量
信号量设置
int semctl(int semid, int semnum, int cmd, *arg)
semid 信号集ID
semnum 修改信号量的编号
cmd 操作类型
arg 根据不同的cmd,参数类型不同
可能的类型为:
union semun{
int val; //信号量值
struct semid_ds *buf; //信号相关信息
unsigned short *array; //信号集
struct seminfo *__buf; //信号相关信息
}
注意:信号集一定是unsigned short类型,不可以是其他、
cmd 参数
IPC_STAT 读取内核中 semid_ds 数据到 arg.buf
IPC_SET 设置 arg.buf 中数据到 semid_ds
IPC_RMID 移除信号量,读写消息队列进程返回 EIDRM
IPC_INFO 获取系统级别的信号量集限制到arg.__buf这里 arg.__buf 指向 seminfo 结构数据返回 内核中信号量集数组的最大索引值
SEM_INFO 获取 seminfo消息,同时获取资源消耗情况:
semusz 获得系统当前的信号量集的数量
semaem 获得系统中信号量的总个数
返回 内核中信号量集数组的最大索引值
SEM_STAT 获取 seminfo 消息
semid 参数置为内核维护信号量集的数组索引
返回 semid指定的信号量标识符
GETALL 获取信号量集的值到 arg.array
GETVAL 返回信号量集中指定信号量的值(单个)
GETNCNT 返回等待信号量增加的进程数(P 阻塞)
GETZCNT 返回等待信号量变为0的进程数(Z 阻塞)
GETPID 返回最后一个调用semop函数操作的进程号
SETALL 设置 arg.array 到信号量集的值
SETVAL 设置 arg.val 到指定信号集中的信号量(单个)
信号量集合 semid_ds
struct semid_ds {
struct ipc_perm sem_perm; //信号量的访问权限
time_t sem_otime; //最近调用semop时间
time_t sem_ctime; //最近调用semctl时间
unsigned short sem_nsems; //信号量集合中成员数目
}
信号量中的数据
struct ipc_perm{
key_t __key; //msgget 的key参数
uid_t uid //消息队列所有者 euid
gid_t gid; //消息队列所有者 egid
uid_t cuid; //消息队列创建者 euid
gid_t cgid; //消息队列创建者 egid
unsigned short mode; //访问权限
unsigned short __seq; //序列号
}
信号量数据
struct seminfo{
int semmap; //信号量层映射里的记录数量(未使用)
int semmni; //信号量集最多个数
int semmns; //所有信号量集中信号量的最多个数
int semmnu; //系统最大信号量撤销数 (未使用)
int semmsl; //一个信号量集中,最多可容纳的信号量数
int semopm; //semop 函数最大操作次数
int semume; //每个进程最大信号量撤销数(未使用)
int semusz; //结构体 sem_undo 的个数(信号集个数)
int semvmx; //最大可取的信号量值
int semaem; // 可以被调整的信号量个数(总信号量)
}
信号量数组 sem
struct sem {
unsigned short semval; //信号量取值
pid_t sempid; //最近访问的进程ID
unsigned short semncnt; // P 阻塞的进程数
unsigned short semzcnt; // Z 阻塞的进程数
}
例子:
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<sys/ipc.h>
#include<sys/sem.h>
#define SEM_NUM 5
//PV操作
void testPV(int semid)
{
/*
struct sembuf sops;
sops.sem_num = 0; //operator semphore-index-0
//sops.sem_op = -2; //P - operation
sops.sem_op = 2; //V - operation
sops.sem_flg = 0;
semop(semid, &sops, 1);
*/
struct sembuf semArr[SEM_NUM];
int i;
int val;
int iRet;
while(1)
{
//初始化信号集
fprintf(stderr, "Input %d num for PV:", SEM_NUM);
for (i=0; i<SEM_NUM; i++)
{
scanf("%d", &val);
semArr[i].sem_num = i;
semArr[i].sem_op = (short)val;
//semArr[i].sem_flg = 0;
if (i==0)
{
semArr[i].sem_flg = IPC_NOWAIT;
}
semArr[i].sem_flg = SEM_UNDO;
}
//更新PV操作
iRet = semop(semid, semArr, SEM_NUM);
if (iRet)
{
perror("semop failed!");
continue;
}
else
{
printf("semop success! ");
}
}
return ;
}
// struct semid_ds结构信息
void printSemInfo(struct semid_ds* pstSem)
{
printf("----------------- SEMID_DS ----------------- ");
printf("Key : %#x ", pstSem->sem_perm.__key);
printf("Ownner uid : %d ", pstSem->sem_perm.uid);
printf("Ownner gid : %d ", pstSem->sem_perm.gid);
printf("Creater uid: %d ", pstSem->sem_perm.cuid);
printf("Creater gid: %d ", pstSem->sem_perm.cgid);
printf("Mode : %#o ", pstSem->sem_perm.mode);
printf("Seque : %d ", pstSem->sem_perm.__seq);
printf(" ");
printf("Last PV time :%d ", (int)pstSem->sem_otime);
printf("Last Ctrl time :%d ", (int)pstSem->sem_ctime);
printf("Sem-Num :%d ", (int)pstSem->sem_nsems);
printf("--------------------------------------------- ");
}
//提示
void Usage()
{
printf(" setall : init semphore-set value ");
printf(" getall : print semphore-set value ");
printf(" ipcstat: print semphore-set info ");
printf(" ipcset : set mode for semphore-set ");
printf(" exit : exit process ");
return;
}
//信号量设置
void testSemCtl(int semid)
{
Usage();
struct semid_ds stSem;
int i;
int iRet;
unsigned short arr[SEM_NUM] = {};
char szCmd[100] = {};
while(1)
{
fprintf(stderr, "->");
scanf("%s", szCmd);
//初识信号集
if (!strcmp(szCmd, "setall"))
{
int val;
fprintf(stderr, "Input %d num for init semphore-set:", SEM_NUM);
for (i=0; i<SEM_NUM; i++)
{
scanf("%d", &val);
if (val < 0 || val > 0xffff)
{
printf("Semphore-value must bigger than 0 ");
break;
}
arr[i] = (unsigned short)val;
}
if (i != SEM_NUM)
{
continue;
}
//更新
iRet = semctl(semid, SEM_NUM, SETALL, arr);
if (iRet)
{
perror("Fail to SETALL!");
continue;
}
}
//获取信号集
else if (!strcmp(szCmd, "getall"))
{
iRet = semctl(semid, SEM_NUM, GETALL, arr);
if (iRet)
{
perror("Fail to GETALL!");
continue;
}
printf("Sem-Value: ");
for (i=0; i<SEM_NUM; i++)
{
printf("%d ", arr[i]);
}
printf(" ");
}
//ipc状态
else if (!strcmp(szCmd, "ipcstat"))
{
iRet = semctl(semid, SEM_NUM, IPC_STAT, &stSem);
if (iRet)
{
perror("Fail to IPC_STAT!");
continue;
}
printSemInfo(&stSem);
}
//修改ipc模式
else if (!strcmp(szCmd, "ipcset"))
{
int mode;
iRet = semctl(semid, SEM_NUM, IPC_STAT, &stSem);
if (iRet)
{
printf("Fail to IPC_STAT!");
continue;
}
printf("Current Mode: %#o ", stSem.sem_perm.mode);
fprintf(stderr, "New Mode(eg:600):");
scanf("%o", &mode);
if (mode < 0 || mode > 0777)
{
printf("Mode is invalid(range 0 to 0777). ");
continue;
}
stSem.sem_perm.mode = mode;
iRet = semctl(semid, SEM_NUM, IPC_SET, &stSem);
if (iRet)
{
perror("Fail to IPC_SET!");
continue;
}
printf("Set mode success! ");
}
//退出
else if (!strcmp(szCmd, "exit"))
{
break;
}
else
{
Usage();
}
}
//删除信号集
fprintf(stderr, "Delete Semphore-set [%d]?(y/n):", semid);
scanf("%s",szCmd);
if (!strcmp(szCmd, "y"))
{
iRet = semctl(semid, SEM_NUM, IPC_RMID, NULL);
if (iRet)
{
perror("Fail to IPC_RMID!");
return;
}
printf("Delete success! ");
}
return;
}
int main(int argc, char ** argv)
{
if (argc != 2 || (strcmp(argv[1], "pv") && strcmp(argv[1], "c" )))
{
printf("Usage: %s [pv | c] ", argv[0]);
printf(" pv: For PV operation ");
printf(" c : For Ctrl semphore-set ");
return 0;
}
char szFile[] = "123";
//创建文件关键字
key_t key = ftok(szFile, 321);
if (key==-1)
{
perror("Fail to ftok!");
return -1;
}
printf("KEY: %#x ", key);
//创建信号集
int semid = semget(key, SEM_NUM, IPC_CREAT | 0660);
if (semid < 0)
{
perror("fail to semget!");
return -1;
}
printf("semid: %d ", semid);
/*
//semid = semget(key, SEM_NUM, IPC_CREAT | 0666);
//semid = semget(key, SEM_NUM, IPC_CREAT|IPC_EXCL| 0666);
//semid = semget(key, SEM_NUM - 1, IPC_CREAT | 0666);
semid = semget(key, SEM_NUM+1, IPC_CREAT | 0666);
if (semid < 0)
{
perror("[2]fail to semget!");
return -1;
}
printf("[2]semid: %d ", semid);
//*/
if (argv[1][0] == ‘p‘)
{
testPV(semid);
}
else
{
testSemCtl(semid);
}
return 0;
}
以上是关于22信号和信号量的主要内容,如果未能解决你的问题,请参考以下文章
贪玩巴斯数字信号处理Digital Signal Processing(DSP)——第二节「路与维信号的分类频率与时间数/模与模/数转换模拟信号」 2021-09-22