linux IPC 通信 study 三:system v 信号量semphore

常用于进程间同步

API 介绍

  • 1 semget

    sys/sem.h

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

    执行成功返回信号量ID, 出错返回-1,同时errno标识错误原因

    参数key 通过ftok获得

    参数 nsems 是信号量集合中信号量的个数;

              在创建新集合时(一般在服务器进程中),必须制定该参数,一旦创建了该信号量,就不能更改其信号量个数,只要不删除该信号量,

              即使重新调用该函数创建该键值的信号量,该函数只是返回以前创建的值,不会重新创建;

              如果是引用现有集合(客户进程),只是访问而不创建,则可以设置为0;

    参数flag 标识该信号量的读写权限

             如果key所代表的信号灯已经存在,且semget指定了IPC_CREAT|IPC_EXCL标志,那么即使参数nsems与原来信号灯的数目不 等,返回的也是EEXIST错误;

             如果semget只指定了IPC_CREAT标志,那么参数nsems必须与原来的值一致。

  • 2 semop

    sys/sem.h

    int semop(int semid, struct sembuf semoparray[], size_t nops);

    函数执行成功返回0, 失败返回-1,errno指示错误类型

    参数semid是semget的返回值semid

    参数semoparray是一个指针,指向一个有sembuf结构表示的信号量操作数组

    参数 nops表示信号量操作数组的长度。

    struct sembuf {

        unsigned short sem_num;

        short                   sem_op;

        short                  sem_flg;

    }

    sem_num其实叫做sem_idx更容易理解,表示在信号量数组中的index, 取值范围是[0, nops);

    sem_op表示要对信号量执行申请资源或者归还资源的操作;

            若sem_op大于0, 信号量加上它的值,相当于进程释放信号量控制的资源,释放资源;

            若sem_op小于0,信号量加上它的值,相当于进程申请信号量控制的资源,申请资源,若进程设置IPC_NOWAIT则进程再没有可用资源情况下,进程阻塞,否则直接返回;

            若sem_op等于0,表示调用进程希望等到该信号量值变为0,若没有设置IPC_NOWAIT, 那么调用进程将进入睡眠状态,直到信号量的值为0,否则进程直接返回;

    sem_flg, 可取IPC_NOWAIT SEM_UNDO这两个值

            IPC_NOWAIT,在对信号量的操作不能执行的情况下,该操作立即返回

            SEM_UNDO,当操作的进程推出后,该进程对sem进行的操作将被取消,这是比较重要的一个标志位。如果设置了该标志位,那么在进程没有释放共享资源就退 出时,内核将代为释放。如果为一个信号灯设置了该标志,内核都要分配一个sem_undo结构来记录它,为的是确保以后资源能够安全释放。事实上,如果进 程退出了,那么它所占用就释放了,但信号灯值却没有改变,此时,信号灯值反映的已经不是资源占有的实际情况,在这种情况下,问题的解决就靠内核来完成;

            不过也看到过这样的解释,sem_flg = 0表示“在对信号量的操作不能执行的情况下,该操作阻塞到可以执行为止”;


  • 3 semctl

    sys/sem.h

    int semctl(int semid, int semnum, int cmd, .../*union semun arg */);

    返回值不确定,失败返回-1,成功时的返回值跟具体的cmd有关。

    参数semid 信号量id,通过semget获得

    参数semnum更应该叫sem_idx表示在信号量数组中的index, 取值范围是[0, nops);

    参数cmd表示要进行的操作

IPC_STAT获取信号灯信息,信息由arg.buf返回
IPC_SET设置信号灯信息,待设置信息保存在arg.buf中
IPC_RMID将信号量集从系统中删除
GETVAL返回semnum所代表信号灯的值
SETVAL设置semnum所代表信号灯的值为arg.val
GETPID返回最后一个对semnum所代表信号灯执行semop操作的进程ID
GETNCNT返回等待semnum所代表信号灯的值增加的进程数,相当于目前有多少进程在等待semnum代表的信号灯所代表的共享资源
GETZCNT返回等待semnum所代表信号灯的值变成0的进程数
GETALL返回所有信号灯的值,结果保存在arg.array中,参数sennum被忽略
SETALL通过arg.array更新所有信号灯的值;同时,更新与本信号集相关的semid_ds结构的sem_ctime成员

     第4个参数根据cmd的不同,设置不同的值。

     注意semun的定义要在你的程序中自己定义,不然会报错"stoarget size of xx is unknown"

 

sample, 实现pv操作

#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>

#define SEM_PATH "/home"
#define MAX_ITER 3 

/*
 * 如果不把semun的定义在此重新定义一下,编译时会报错误
 * union semun storage size of xx is unknown
 * */
union semun {
	int						val;
	struct		semid_ds	*buf;
	unsigned	short		*array;
	struct		seminfo		*__buf;
};

static int sem_p(int semid)
{
	struct sembuf pv_op;

	/*now ask for available resource: p operation */
	pv_op.sem_num = 0;
	pv_op.sem_op = -1;
	pv_op.sem_flg = SEM_UNDO;    
    
	if (semop(semid, &pv_op, 1) == -1)
		printf("semop ask for resource  error\n");
  
}

static int sem_v(int semid)
{
	struct sembuf pv_op;

	/*now free resource v operation */ 
	pv_op.sem_num = 0;
	pv_op.sem_op = 1;
	pv_op.sem_flg = SEM_UNDO;

	if (semop(semid, &pv_op, 1) == -1)
		if (errno == EIDRM)
			printf("the semaphore set was removed\n");

}

int main(int argc,  char **argv)
{
	int semid;
	int i = 0, init_ok = 0, tmp_e = 0;

	struct semid_ds sem_info;
	union semun arg;

	key_t key = ftok(SEM_PATH, 'a');

	//create a semaphore set that only includes one semphore.
	semid = semget(key, 1, IPC_CREAT | IPC_EXCL | 00666);
	if (semid < 0) {
		tmp_e = errno;
		perror("semget");

		if (tmp_e == EEXIST) {
			/*
				errno is undefined after a successful library call( including perror call) 
				so it is saved  in tmp_e.
			*/
			/*
				只包含了IPC_CREAT标志,  参数nsems(这里为1)必须与原来的信号灯数目一致
			*/
			semid = semget(key, 1, IPC_CREAT | 00666);

			arg.buf = &sem_info;

			for (i = 0; i < MAX_ITER; i++) {
				if (semctl(semid,  0, IPC_STAT, arg) == -1) {
					perror("semctl error");
					i = MAX_ITER;
				} else {
					if (arg.buf->sem_otime != 0) {
						i = MAX_ITER;
						init_ok = 1;
					} else {
						sleep(1);
					}
				}
			}

			if (!init_ok) {
				arg.val = 1;
				if (semctl(semid, 0, SETVAL, arg) == -1)
					perror("semctl setval error");
			} 
		} else {
			perror("semget error,  process exit");
			exit(1);  
		}
	} else {
		arg.val = 1;
		if (semctl(semid, 0, SETVAL, arg) == -1)
			perror("semctl setval error");
	}

	/*STAT infomation */
	arg.buf = &sem_info;
	if (semctl(semid,  0, IPC_STAT, arg) == -1)
		printf("semctl IPC STAT");    

	printf("owner's uid is %d\n", arg.buf->sem_perm.uid);
	printf("owner's gid is %d\n", arg.buf->sem_perm.gid);
	printf("creater's uid is %d\n", arg.buf->sem_perm.cuid);
	printf("creater's gid is %d\n", arg.buf->sem_perm.cgid);

	
	sem_p(semid);

	sleep(3); 

	sem_v(semid);

	if (semctl(semid,  0, IPC_RMID) == -1)
		perror("semctl IPC_RMID");
	else
		printf("remove sem ok\n");
}




  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值