一:信号量简介
信号量本质就是一个计数器,用于统计临界资源数目的计数器,信号量是用来调协进程对共享资源的互斥访问,采用PV原语的原理。它的P(sv)和V(sv)行为是这样的:
P(sv):要访问受保护的资源的进程或者线程试图对信号量的值减1,如果信号量的值现在不可用,即信号量为0,减1操作将被阻塞(休眠),直到信号量大于0时,才会得以继续执行;
V(sv):要释放受保护的资源时,信号量的值将会加1,此时信号量的值大于0,其他请求该资源被信号量阻塞的线程或者进程将被唤醒;
总之,信号量为0(信号量不能小于0),无可用资源,信号量大于0,有可用资源。
二:函数简介
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
int semget(key_t key, int nsems, int semflg);
@semget:新建信号量
@int :成功返回相应的信号量标识符,失败返回-1,errno的值如下:
EACCESS:没有权限
EEXIST:信号量集已经存在,无法创建
EIDRM:信号量集已经删除
ENOENT:信号量集不存在,同时semflg没有设置IPC_CREAT标志
ENOMEM:没有足够的内存创建新的信号量集
ENOSPC:超出限制
@key:整数或者IPC_PRIVATE(0),信号量的标识。
IPC_PRIVATE,表示私有的,意味信号量只能用于亲缘进程通信
@nsems:信号量集的个数,换句话来说,semget创建的信号量集id,可以控制nsems个资源的访问和释放
@semflg:九个权限标志构成,它们的用法和创建文件时使用的mode模式标志是一样的;
如果单独使用IPC_CREAT,则shmget()要么返回一个新创建的信号量的标识符,要么返回具有相同关键字值的标识符;
如果IPC_EXCL和IPC_CREAT一起使用,如果信号量已经存在则返回一个失败值-1,否则创建一个新的信号量,IPC_EXCL单独使用是没有用处的。
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
int semop(int semid, struct sembuf *sops, size_t nsops);
@semop:实现信号量的资源操作(P和V)
@int :成功返回相应的信号量标识符,失败返回-1
@semid:信号量集标识符
@sops:指向进行操作的信号量集结构体数组的首地址
struct sembuf
{
unsigned short sem_num; /* semaphore number */
short sem_op;/* semaphore operation */
short sem_flg;/* operation flags */
}
sem_op:它的值可为正数、负数、0。正数表示释放资源操作(V操作),负数表示要请求资源访问的操作(P操作),0表示等待可用资源。
sem_flg:它的值可为0、IPC_NOWAIT、SEM_UNDO。IPC_NOWAIT表示该操作为非阻塞操作,SEM_UNDO表示当进程退出的时候会还原该进程的信号量操作可以避免死锁。
@nsops:进sops结构变量的个数,需大于或等于1。一般为1,只完成对一个信号量的操作。
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
int semctl(int semid, int semnum, int cmd, union semun arg)
@semctl函数:控制信号量(&删除)
@int:成功返回非负整数,失败返回-1
@semid:信号量集标识符
@semnum:信号量集数组上的下标,表示某一个信号量
@cmd:操作类型,可取值IPC_STAT或者IPC_SET或者IPC_RMID
IPC_STAT:从信号量集上检索semid_ds结构,并存到semun联合体参数的成员buf的地址中
IPC_SET:设置一个信号量集合的semid_ds结构中ipc_perm域的值,并从semun的buf中取出值
IPC_RMID:从内核中删除信号量集合
GETALL:从信号量集合中获得所有信号量的值,并把其整数值存到semun联合体成员的一个指针数组中
GETNCNT:返回当前等待资源的进程个数
GETPID:返回最后一个执行系统调用semop()进程的PID
GETVAL:返回信号量集合内单个信号量的值
GETZCNT:返回当前等待100%资源利用的进程个数
SETALL:与GETALL正好相反
SETVAL:用联合体中val成员的值设置信号量集合中单个信号量的值
@arg:输出输入信号量状态
union semun {
short val; /*SETVAL用的值*/
struct semid_ds* buf; /*IPC_STAT、IPC_SET用的semid_ds结构*/
unsigned short* array; /*SETALL、GETALL用的数组值*/
struct seminfo *buf; /*为控制IPC_INFO提供的缓存*/
} arg;
三:实现父子进程分别向屏幕打印“AA”“BB”
/*
实现父子进程分别向屏幕打印“AA”“BB”
*/
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
using namespace std;
#define ERR_EXIT(m) \
do { \
perror(m); \
exit(EXIT_FAILURE); \
} while(0)
//需要自定义
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;
};
void Print(int semid, char c)
{
struct sembuf sf;
sf.sem_num = 0;
sf.sem_flg = SEM_UNDO;
while (true)
{
sf.sem_op = -1;
semop(semid, &sf, 1); //P操作请求资源
printf("%c", c);
fflush(stdout);
sleep(1);
printf("%c", c);
fflush(stdout);
sf.sem_op = 1;
semop(semid, &sf, 1); //V操作释放资源
}
}
int main(int argc, char *argv[])
{
int semid = semget(IPC_PRIVATE, 1, 0666|IPC_CREAT);
if (semid == -1)
{
ERR_EXIT("semget");
}
//初始化0信号量的val值为1
union semun un;
un.val = 1;
if(semctl(semid, 0, SETVAL, un) < 0)
{
ERR_EXIT("semctl");
}
pid_t pid = fork();
if (pid < 0)
ERR_EXIT("fork");
else if (pid == 0)
{
Print(semid, 'B');
}
else
{
Print(semid, 'A');
}
//删除该信号量
sleep(5);
if(semctl(semid, 0, IPC_RMID) < 0)
{
ERR_EXIT("semctl");
}
return 0;
}
/*
结果:
root@epc:/home/share/project/test/linux/demo# ./server
AABBAABBAABBAABBAABBA^C
*/