linuxC多进程通讯systemv---信号量

信号量概念

•英文:semaphore,简称SEM,主要用来进程间同步
•本质:内核维护的一个正整数,可对其进行各种操作+/-操作
•分类:system V 信号量、POSIX 有名信号量、POSIX 无名信号量
•用途:用来标识系统中可用的共享资源的个数,协调各进程有序地使用这些资源,防止发生冲突
•信号量类似于酒店房间的房卡,房间资源是有限的、房卡也是有限的
•P操作:程序在进入临界区之前要先对资源进行申请
•V操作:程序离开临界区后要释放相应的资源,如房卡交给房东

通信原理

•类似于房卡,不是单个值,而是一组(实际上是数组)信号量元素构成
•将信号量设置成一个绝对值
•在信号量当前值的基础上加上一个数量
•在信号量当前值的基础上减去一个数量,降到0以下可能会引起阻塞
•阻塞进程一直等待其它进程修改信号量的值,直到恢复正常运行
•信号量本身无意义,通常会与一块临界资源(如共享内存)关联使用

相关API

•获取信号量ID:int semget (key_t key, int nsems, int semflg);
•P/V操作:int semop (int semid, struct sembuf *sops, size_t nsops);
–操作术语:荷兰语中的首字母,由荷兰计算机科学家Edsger Dijkstra确定
–其它操作术语:down(减小信号量)、up(增大信号量)
–POSIX标准:wait、post
•信号量设置:int semctl (int semid, int semnum, int cmd, …);

semget
含义

创建或打开一个信号量

函数原型

int semget (key_t key, int nsems, int semflg);

头文件

sys/ipc.h
sys/sem.h

函数参数

–key:用来表示信号量的键,通常使用值IPC_PRIVATE或由ftok创建
–nsems:信号的数量,所有的信号放在一个数组内
–semflg:位掩码,用来设置信号量的权限或检查一个已有信号量的权限
>>IPC_CREAT:如果找不到指定key相关联的信号量,创建一个新信号量集合
>>IPC_EXCL:若指定了IPC_CREAT且指定key关联的信号量存在,报EEXIST错误

函数返回值

–成功:返回用于操作信号量的句柄ID
–失败:-1,并设置errno全局变量

semctl
含义

信号量设置

函数原型

int semctl (int semid, int semnum, int cmd, …);

头文件

– #include <sys/ipc.h>
– #include <sys/sem.h>

函数参数

–semid :用于操作信号量的句柄ID、标识符
–semnum :信号的数量,所有的信号放在一个数组内
–cmd:
»IPC_RMID:删除信号量集及相关联的内核semid_ds数据结构
»IPC_STAT:获取 semid_ds 副本
»IPC_SET:设置 semid_ds 数据结构
»GETVAL:获取信号集中第semnum个信号量的值
»GETALL:获取所有的信号量的值
»SETVAL:设置信号集中的第semnum个信号量的值

函数返回值

–成功:根据cmd命令,返回不同的值
–失败:-1,并设置errno全局变量

semop
含义

信号量P/V操作

函数原型

int semop(int semid, struct sembuf *sops, size_t nsops);

头文件

– #include <sys/ipc.h>
– #include <sys/sem.h>

函数参数

–semid :用于操作信号量的IPC标识符
–sops :指向数组的指针,数组中包含了需要执行的操作
–nsops :数组的大小

函数返回值

–成功:根据cmd命令,返回不同的值
–失败:-1,并设置errno全局变量

sembuf结构体

• sem_num:用来标识要操作的信号集中的信号量
•sem_op:
–若大于0:将sem_op的值加到信号量值上
–若等于0:对信号量值进行检查,确定其当前值是否为0,若为0操作结束,若不为0,则一直阻塞,直到信号量的值变为0为止
–若小于0:将信号量值减去sem_op。最后结果大于或等于0,操作立即结束;若最后结果小于0,则当前进程会阻塞
•sem_flag:
–SEM_UNDO
–IPC_NOWAIT

sem_flag:
	–SEM_UNDO
	–IPC_NOWAIT
struct sembuf {
	unsigned short sem_num; /* semaphore index in array */
	short sem_op; /* semaphore operation */
	short sem_flg; /* operation flags */
};
使用流程

•使用semget创建或打开一个信号量集
•使用semctl SETVAL或SETALL操作初始化集合中的信号量(其中一个进程操作即可,内核中维护,对其它进程是全局可见的)
•使用semop操作信号量值。多个进程通过多信号量值的操作来表示一些临界资源的获取和释放
•当所有进程不再需要信号量集时,使用semctl IPC_RMID操作删除这个信号量集(其中一个进程操作即可)

举例

创建一个信号量,并对其进行p/v操作

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

#include <sys/ipc.h>
#include <sys/sem.h>
#include <sys/types.h>

#define handle_error(msg) \
    {perror(msg);exit(EXIT_FAILURE);}

// creat a semphore
// print and set semphore
// P/V sem value
// delete sem

union semun
{
	int val;
	struct semid_ds *buf;
	unsigned short int *array;
	struct seminfo *__buf;
};

/*
struct sembuf
{
	unsigned short sem_num;//semaphore index in array
	short sem_op;          //sepaphore operation
	short sem_flg;         //operation flags
};
*/

int main (void)
{
	int sem_id;
	key_t key;

	if ((key = ftok(".", 514))== -1)
        handle_error("ftok");

	if ((sem_id = semget (key, 3, IPC_CREAT | 0770)) == -1)
        handle_error("semget");
	printf ("sem id: %d\n", sem_id);

	// get sem value
	int sem_value;
	sem_value = semctl (sem_id, 0, GETVAL);
	printf ("sem value: %d\n", sem_value);

	// set sem value
	union semun sem_union;
	sem_union.val = 2;
	semctl (sem_id, 0, SETVAL, sem_union);
	sem_value = semctl (sem_id, 0, GETVAL);
	printf ("sem value: %d\n", sem_value);


	//sem p/v operations
	struct sembuf sops;
	sops.sem_num = 0;
	sops.sem_op = -2;// if sem value=0, blocked
	sops.sem_flg = SEM_UNDO;
	if (semop (sem_id, &sops, 1) == -1)
        handle_error("sem_op");
	sem_value = semctl (sem_id, 0, GETVAL);
	printf ("sem value: %d\n", sem_value);
	
	sleep (30);
	semctl (sem_id, 0, IPC_RMID);

	return 0;
}
应用:对共享内存的同步访问
write
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <strings.h>
#include <sys/stat.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <sys/shm.h>

union semun
{
	int val;
	struct semid_ds *buf;
	unsigned short int *array;
	struct seminfo *__buf;
};

int sem_id;

void sem_init (int semid, int nsignum, int sem_value)
{
	union semun sem_union;
	sem_union.val = sem_value;
	if (semctl (semid, nsignum, SETVAL, sem_union) == -1)
	{
		perror ("semctl");
		exit (EXIT_FAILURE);
	}
}
 void sem_p (int semid, int nsignum)
{
	struct sembuf sops;
	sops.sem_num = nsignum;
	sops.sem_op  = -1;
	sops.sem_flg = SEM_UNDO;
	if (semop (sem_id, &sops, 1) == -1)
	{
		perror ("semop");
		exit (EXIT_FAILURE);
	}
}
 void sem_v (int semid, int nsignum)
{
	struct sembuf sops;
	sops.sem_num = nsignum;
	sops.sem_op  = 1;
	sops.sem_flg = SEM_UNDO;
	if (semop (sem_id, &sops, 1) == -1)
	{
		perror ("semop");
		exit (EXIT_FAILURE);
	}
}

void sem_print (int sem_id, int nsignum)
{
	int sem_value;
	sem_value = semctl (sem_id, nsignum, GETVAL);
	printf ("sem[%d] = %d\n", nsignum, sem_value);
}
int main (void)
{
	int shm_id;
	key_t shm_key = ftok ("./", 5151);
	key_t sem_key = ftok ("./", 5152);

	shm_id = shmget (shm_key, 1028, IPC_CREAT | 0644);
	char *shm_addr = shmat (shm_id, NULL, 0);

	sem_id = semget (sem_key, 2, IPC_CREAT | 0644);
	if (sem_id == -1)
	{
		sem_id = semget (sem_key, 2, 0644);
	}
	else
	{
		sem_init (sem_id, 0, 0); //sem[0]: for read
		sem_init (sem_id, 1, 1); //sem[1]: for write
	}
	while (1)           //write
	{
		sem_p (sem_id, 1);
		fgets (shm_addr, 1028, stdin);
		sem_v (sem_id, 0);
	}
	return 0;
}

read
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <strings.h>
#include <sys/stat.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <sys/shm.h>
#include <sys/types.h>

union semun
{
	int val;
	struct semid_ds *buf;
	unsigned short int *array;
	struct seminfo *__buf;
};

int sem_id;

void sem_init (int semid, int nsignum, int sem_value)
{
	union semun sem_union;
	sem_union.val = sem_value;
	if (semctl (semid, nsignum, SETVAL, sem_union) == -1)
	{
		perror ("semctl");
		exit (EXIT_FAILURE);
	}
}
 void sem_p (int semid, int nsignum)
{
	struct sembuf sops;
	sops.sem_num = nsignum;
	sops.sem_op  = -1;
	sops.sem_flg = SEM_UNDO;
	if (semop (sem_id, &sops, 1) == -1)
	{
		perror ("semop");
		exit (EXIT_FAILURE);
	}
}
 void sem_v (int semid, int nsignum)
{
	struct sembuf sops;
	sops.sem_num = nsignum;
	sops.sem_op  = 1;
	sops.sem_flg = SEM_UNDO;
	if (semop (sem_id, &sops, 1) == -1)
	{
		perror ("semop");
		exit (EXIT_FAILURE);
	}
}

void sem_print (int sem_id, int nsignum)
{
	int sem_value;
	sem_value = semctl (sem_id, nsignum, GETVAL);
	printf ("sem[%d] = %d\n", nsignum, sem_value);
}
int main (void)
{
	int shm_id;
	key_t shm_key = ftok ("./", 5151);
	key_t sem_key = ftok ("./", 5152);

	shm_id = shmget (shm_key, 1028, IPC_CREAT | 0644);
	char *shm_addr = shmat (shm_id, NULL, 0);

	sem_id = semget (sem_key, 2, IPC_CREAT | 0644);
	if (sem_id == -1 )
	{
		perror ("semget");
		exit (EXIT_FAILURE);
	}
	else
	{
		sem_init (sem_id, 0, 0);//sem[0]: for read
		sem_init (sem_id, 1, 1);//sem[1]: for write
	}
	while (1)            //read
	{
		sem_p (sem_id, 0);
		printf ("from shm:%s", shm_addr);
		sem_v (sem_id, 1);
	}
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值