一、与信号量有关的几个知识点
临界资源:同一时刻,只能被一个进程访问的资源
临界区:访问临界资源的代码区
原子操作:任何情况下都不能被打断的操作
内核对象:对通讯值的记录,类似管道
二、信号量的实质和作用
信号量的实质是记录资源同时能被多少个进程访问,作用于进程间的同步控制,不同于锁,它可以有N个有限值。
举个例子,信号量类似一个停车场,而锁类似停车场中的一个停车位,当车辆进入停车场时,停车位数量减一,但是在有其它空余停车位时,车辆依然可以进入停车场。而一个停车位有车辆时,其它车辆不可以进入这个停车位。
总的来说,信号量的通讯信息是资源的可访问数量,不同于管道的数据。
三、信号量的操作
1. 创建或获取:Int semget((key_t)key, int nsems, int flag);
Key是键值,是一个唯一的非零整数,类似于一个“身份证号”,其它进程通过这个“身份证号”来使用这组信号量;
Nsems 用来确定内核创建的信号量数组大小;
Flag 用来确定操作和权限,可以通过与上PC_CREAT在不存在该信号量时创建,存在时获取;
返回值:非零的信号量标识符,其它函数根据信号量标识符来操作,而非键值;
栗子:int sem_id = semget( (key_t)1234, 1, 0666|IPC_CREAT );
2. P/V操作:int semop( int semid , struct sembuf * buf , int lenth);
Semid: semget返回的信号量标识符
struct sembuf * buf:一个结构体
struct sembuf
{
Short sem_num;//除非使用一组信号量,否则它为0
short sem_op;//信号量在一次操作中需要改变的数据,通常是两个数,
一个是-1,即P(等待)操作,//一个是+1,即V(发送信号)操作。
shortsem_flg;//通常为SEM_UNDO,使操作系统跟踪信号
};
3. 初始化和删除:int semctl(int sem_id, int sem_num, int command, uniousemun);
Semid: semget返回的信号量标识符
sem_num:信号量数组的下标,如果只有一个元素,则为零
command:操作,SETVAL为初始化,IPC_RMID为删除
uniou semun:在初始化时,需要用到
unionsemun
{
int val; //初始化资源的个数
struct semid_ds *buf;
unsigned short *arry;
};
四、通过一个简单实例来掌握信号量,进程A负责读取键盘输入,直到读取“OK”,进程B打印出100以内的素数。
需要注意的是,在多个进程之间使用信号量通讯,可以将P操作之后的代码认为是临界区,即P操作不是简单的减一,而是要理解为:
如果能做P操作之后临界区的事件,那么减一,否则等待
sem.h 为头文件
sem.c 将信号量操作进行了简单的封装
maina.c和mainb.c是一个简单的示例:
maina读取键盘输入,直到输入OK,mainb打印100以内的素数
sem.h:
#ifndef _SEM_H
#define _SEM_H
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <assert.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
int semid;
union semun
{
int val;
//struct semid_ds *buf;
//unsigned short * arry;
};
void sem_get(int key,int len,int val);
void sem_p();
void sem_v();
void sem_del();
#endif
sem.c:
#include "sem.h"
void sem_get(int key,int len,int val)
{
semid = semget((key_t)key,len,0666);
if(semid == -1)
{
semid = semget((key_t)key,len,0666|IPC_CREAT);
assert(semid != -1);
union semun v;
v.val = val;
if(semctl(semid,0,SETVAL,v)==-1)
{
perror("error");
exit(0);
}
}
}
void sem_p()
{
struct sembuf sem;
sem.sem_num = 0;
sem.sem_op = -1;
sem.sem_flg = SEM_UNDO;
if(semop(semid, &sem, 1)==-1)
{
perror("perror");
exit(0);
}
}
void sem_v()
{
struct sembuf sem;
sem.sem_num = 0;
sem.sem_op = 1;
sem.sem_flg = SEM_UNDO;
if(semop(semid, &sem, 1)==-1)
{
perror("perror");
exit(0);
}
}
void sem_del()
{
if(semctl(semid,0,IPC_RMID)==-1)
{
perror("delperror");
exit(0);
}
}
进程A:
#include "sem.h"
int main()
{
sem_get(111,1,0);
while(1)
{
char buff[128]={0};
fgets(buff,127,stdin);
if(strncmp(buff,"ok",2)==0)
{
sem_v();
break;
}
}
sem_del();
return 0;
}
进程B:
#include "sem.h"
void prime()
{
int i,j;
for(i=2;i<=100;i++)
{
for(j=2;j<=i-1;j++)
{
if(i%j==0)
break;
}
if(j>=i)
printf("%d\n",i);
}
}
int main()
{
sem_get(111,1,0);
sem_p();
prime();
return 0;
}
本文介绍了信号量的基本概念,包括临界资源、临界区及原子操作等,并详细解析了信号量的操作方法,如创建、P/V操作及初始化和删除等。通过一个具体的示例程序,展示了如何使用信号量实现进程间同步。
809

被折叠的 条评论
为什么被折叠?



