System V信号量

  1. System V信号量不是用来在进程间传输数据的,而是用来同步进程的动作。

  2. 信号量的一个常见用途是同步对一块共享内存的访问以防止出现一个进程在访问共享内存的同时另一个进程更新这块内存的情况,

  3. 在控制进程的动作方面,信号量本身并没有任何意义,它的意义仅由使用信号量的进程赋予其的关联关系来确定。

  4. 进程之间会达成协议将一个信号量与一种共享资源关联起来,如一块共享内存区域。信号量还有其他用途,如在 fork()之后同步父进程和子进程。

一、概述

使用Svstem V信号量的常规操作步骤:

  1. 使用semget()创建或者打开一个信号量集。

  2. 使用semctl() SETVAL 或 SETALL 操作初始化集合中的信号量(只有一个进程需要完成这个任务)。

  3. 使用semop()操作信号量的值。使用信号量的进程通常会使用这些操作来标识一种共享资源的获取和释放。

  4. 当所有进程都不再需要使用信号量集之后semctl() IPC_RMID操作删除这个集合(只有一个进程需要完成这个任务)。

  5. System V信号量是以分配被称为信号量集的组为单位进行的。在使用semget()系统调用创建集合的时候需要指定集合中的信号量数量。

  6. 虽然同一时刻通常只会操作一个信号量,但通过semop()系统调用可以原子的在同一个集合中的多个信号量之上执行一组操作。

二、使用Svstem V信号量

1.信号量数据结构

信号量数据结构是信号量程序设计中经常使用的数据结构,由于在之后的函数经常用到,这里将结构的原型列出来,便于查找:

//每个信号量集,内核维护如下信息结构:<sys/sem.h>
struct semid_ds
{
   
  struct ipc_perm 	sem_perm;		/* 操作权限结构 */
  struct sem		*sem_base;		//指向sem结构数组指针
  time_t 			sem_otime;			/* 上次semop()时间 */
  time_t 			sem_ctime;			/* 上次由semctl()更改的时间 */
  ushort 	 		sem_nsems;		/* 集合中的信号量数量 */
};

//ipc_perm含有当前信号量访问权限
struct ipc_perm{
   
	uid_t uid;
	gid_t gid;
	uid_t cuid;
	gid_t cgid;
	mode_t mode;
	ulong_t seq;
	key_t key;
}

//维护某个信号量的一组的内部数据结构,一个信号量集的每个成员如下这个结构描述:
struct sem{
   
ushort_t 	semval;
short 		sempid;
ushort_t 	semncnt;
ushort_t 	semzcnt;
}

除维护一 个信号量集内每个信号量的实际值之外,内核还给该集合中每个信号量维护另外三个信息:对其值执行最后 一 次操作的进程的进程ID等待其值增长的进程数计数以及等待其值变为0的进程数计数

在这里插入图片描述

2.新建信号量semget()

semget()函数用于创建一个新的信号量集合,或者访问现有的集合。

其原型如下,其中第1个参数key是ftok生成键值,第2个参数nsems参数可以指定新的集合中应该创建的信号量的数目,第3个参数semflsg是打开信号的方式。

#include<sys/types.h>
#include<sys/sem.h>
int semget(key_t key,int nsems,int semflg);
//key是ftok生成键值
//nsems参数可以指定新的集合中应该创建的信号量的数目
//semflsg是打开信号的方式

semflsg属性值:

  1. IPC_CREAT: 如果内核中不存在这样的信号量集合,则把它创建出来。
  2. IPC_EXCL:
    1. 当与IPC_CREAT一起使用时,如果信号量集合早已存在,则操作将失败。
    2. 单独使用IPC _ CREAT, semget()或者返回新创建的信号量集合的信号量集合标识符;或者返回早已存在的具有同 个关键字值的集合的标识符。
    3. 同时使用IPC_EXCL和IPC_CREAT, 那么将有两种可能的结果:如果集合不存在,则创建一个新的集合;如果集合早已存在,则调用失败,并返回-1。
    4. IPC_EXCL本身是没有什么用处的,但当与IPC_CREAT组合使用时,它可以用于防止为了访问而打开现有的信号量集合。

//CreateSem()函数按照用户的键值生成一个信号量,把信号量的初始值设为用户输入的value。
typedef int sem_t;

union semun{
   		//信号量操作的联合结构
		int val;	//整型变量
		struct semid_ds *buf;//semid_ds结构指针
		unsigned	short  *array;//数组类型
		}arg;//全局变量
sem_t CreateSem(key_t key,int value)//建立信号量,魔数key和信号量的初始值value
{
   
	union	semun	sem; //信号量结构变量
	sem_t	semid;//信号量ID
	sem.val = val;//设置初值
	semid = semget(key,0,IPC_CREAT|0666);//获得信号量ID
	if(-1 == semid)
	{
   
		printf("create semaphore error\n");
		return -1;
	}
	semctl(semid,0,SETVAL,sem);//发送命令,建立value个初始值的信号量
	return semid;
}

3.控制信号量参数semctl()函数

与文件操作的ioctl()函数类似,信号量的其他操作是通过函数semctl()来完成。函数semctl()的原型如下:

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

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

//第4个参数指向如下联合体:
union semun{
   
	int val;//当执行SETVAL命令时将用到这个成员,它用于指定要把信号量设置成什么值。
	struct semid_ds *buf;//在命令IPC_STAT/IPC_SET中使用。它代表内核中所使用的内部信号量数据结构的一 个复制。
	ushort *array;//用在GETALL/SET ALL命令中的 一 个指针。它应当指向整数值的一 个数组。在设置或获取集合中所有信号量的值的过程中,将会用到该数组。
	struct seminfo *buf;//信号量内部结构
};
  • 函数semctl()用于在信号量集合上执行控制操作。这个调用类似于函数msgctl(), msgctl() 函数是用于消息队列上的操作。
    • 第1个参数是关键字的值(在我们的例子中它是调用semget()函数所返回的值)。
      • 第2个参数(semun)是将要执行操作的信号量的编号,它是信号量集合的 一 个索引值,对于集合中的第 1个信号量 (有可能只有这一 个信号量)来说,它的索引值将是 一 个为0 的值。

cmd 参数代表将要在集合上执行的命令。其取值如下所述:

  1. IPC_STAT: 获取某个集合的semid_ds结构,并把它存储在semun联合体的buf参数所指定的地址中。

  2. IPC_SET:设置某个集合的semid_ds结构的ipc_perm成员的值。该命令所取的值是从semun联合体的buf参数中取到的。

  3. IPC_RMID : 从内核删除该集合。

  4. GETALL: 用于获取集合中所有信号量的值。整数值存放在无符号短整数的一个数组中,该数组由联合体的array成员所指定。

  5. GETNCNT: 返回当前正在等待资源的进程的数目。

  6. GETPID: 返回最后一 次执行 semop 调用的进程的PID 。

  7. GETVAL: 返回集合中某个信号量的值。

  8. GETZCNT: 返回正在等待资源利用率达到百分之百的进程的数目。

  9. SETALL: 把集合中所有信号量的值,设置为联合体的array成员所包含的对应值。

  10. SETVAL: 把集合中单个信号量的值设置为联合体的val成员的值。

例:利用semctl()函数设置和获得信号量的值构建同用的函数:

void SetvalueSem(sem_t semid,int value)//设置信号的值,通过SETVAL实现,所设置的值通过联合变量sem的val域实现
{
   
	union semun sem;//信号量操作的结构
	sem.val = value;//初始化
	semctl(semid,0,SETVAL,sem);//设置信号量的值
}


int GetvalueSem(sem_t semid)//获得信号量的值
{
   
	union semun sem;//信号量操作的结构
	return semctl(semid,0,GETVAL,sem);//获得信号量的值,通过GETVAL会是其返回给定信号量的当前值
}


void DestroySem(sem_t semid)</
  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
System V信号是一种进程间通信机制,主要用于进程之间的同步和互斥操作。下面是使用System V信号的基本步骤: 1. 创建或获取一个信号集:可以通过semget函数创建一个新的信号集,也可以通过semget函数获取一个已经存在的信号集。 2. 初始化信号:可以通过semctl函数设置初始值。 3. 对信号进行P操作:可以通过semop函数执行P操作,即获取信号的操作。如果信号的值大于0,则将其减1并返回;否则,阻塞等待。 4. 对信号进行V操作:可以通过semop函数执行V操作,即释放信号的操作。将信号的值加1并返回。 5. 删除信号集:可以通过semctl函数删除信号集。 注意事项: 1. 在使用信号前,需要定义一个semun结构体,用于设置信号集的属性。 2. 在使用信号时,需要保证所有进程都能够访问到信号集,可以通过将信号集的ID保存在共享内存中,或者使用特殊的关键字来访问。 3. 在使用信号时,需要注意信号的值的范围,不能超过信号的最大值。 下面是一个简单的使用System V信号的示例程序: ```c #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <sys/types.h> #include <sys/ipc.h> #include <sys/sem.h> union semun { int val; struct semid_ds *buf; unsigned short *array; }; int main() { int semid; union semun arg; struct sembuf sb; // 创建信号集 semid = semget(IPC_PRIVATE, 1, IPC_CREAT | 0666); if (semid == -1) { perror("semget"); exit(EXIT_FAILURE); } // 初始化信号 arg.val = 1; if (semctl(semid, 0, SETVAL, arg) == -1) { perror("semctl"); exit(EXIT_FAILURE); } // P操作 sb.sem_num = 0; sb.sem_op = -1; sb.sem_flg = SEM_UNDO; if (semop(semid, &sb, 1) == -1) { perror("semop"); exit(EXIT_FAILURE); } printf("Enter critical section\n"); sleep(5); printf("Leave critical section\n"); // V操作 sb.sem_num = 0; sb.sem_op = 1; sb.sem_flg = SEM_UNDO; if (semop(semid, &sb, 1) == -1) { perror("semop"); exit(EXIT_FAILURE); } // 删除信号集 if (semctl(semid, 0, IPC_RMID, arg) == -1) { perror("semctl"); exit(EXIT_FAILURE); } exit(EXIT_SUCCESS); } ``` 这个程序创建了一个信号集,初始化了一个信号的值为1,然后执行了一个P操作,进入临界区并睡眠5秒钟,然后执行一个V操作,离开临界区,最后删除了信号集。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值