信号量1

 

说明:只供学习交流,转载请注明出处

 

 

一,信号量的基本概念

信号量(又名:信号灯)与其他进程间通信方式不大相同,主要用途是保护临界资源。进程可以根据它判定是否能够访问某些共享资源。除了用于访问控制外,还可用于进程同步。

 

在多进程环境里,运行一个进程或修改其他进程可能正在使用的数据,往往导致数据的数值不可预测。使用信号量避免了这个问题的出现。信号量用于控制多个进程对共享资源或数据的访问,使得在某个时刻,只有一个进程使用资源或访问资源。其他的进程如果希望使用该资源或访问数据,只有在资源可用时才能够实现。在POSIX IPC中,信号量集实现了与信号量相同的功能。

 

二,信号量的分类

1):二值信号灯:信号灯的值只能取01,类似于互斥锁。但两者有不同:信号灯强调共享资源,只要共享资源可用,其他进程同样可以修改信号灯的值;互斥锁更强调进程,占用资源的进程使用完资源后,必须由进程本身来解锁。

 

2):计数信号灯:信号灯的值可以取任意非负值。

 

三,临界区和信号量集

临界区是一段独占某些共享资源访问的代码。如果有多个线程试图同时访问临界区,那么在有一个线程进入后,其他所有试图访问此临界区的线程将被挂起,挂起操作将一直持续到进入临界区的线程离开。临界区在被释放后,其他线程可以继续抢占。以此达到原子方式操作(原子操作是指不可在细分的操作),实现对资源共享的目的。

 

POSIX IPC中,信号量是由一个或多个信号组成的数组构成。进程可以通过信号集实现进程的同步,这一点和前面提到的信号量是相同的。为了区分两者,将POSIX IPC中的信号量称为信号量集。

 

 

四,信号量集的用法

使用信号量进行进程间通讯一般会经历下面几步:

1):建立信号量集:进程通过调用函数semget来创建或者获得一个信号量集。

2):初始化semval:通过semctl使用SETVAL命令来初始化信号量的值(semval)。

3):获取与释放semval:通过semop来获取或者释放信号量,其中获取对应于semval值减1,释放对应于semval值加1

4):删除信号量集:当进程结束使用信号量时,使用semctl通过IPC_RMID命令来删除它。

 

 

五,创建信号量集

 

头文件

#include <sys/types.h>

#include <sys/ipc.h>

#include <sys/sem.h>

函数原型

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

返回值 

成功

失败

是否设置errno

返回信号量标识符

-1

与使用消息队列进行进程间通信前必须创建消息队列类似,使用信号量集同样要创建信号量集资源。semget函数用于创建信号量集,该函数的具体信息如下表所示;

semget函数

说明:semget函数将返回与参数key相关的信号量集标识符。当参数keyIPC_PRIVATE,或参数semflg中未设置IPC_CREAT位,同时,内核中不存在与key相关的信号量集时,将创建新的信号量集。

参数nsems指定了信号量集中信号量元素的个数。当nsems0时,将不创建信号量集。参数nsems的取值必须大于0,且小于信号量集中信号量的最大值(SEMMSL)。

如果参数semflg同时设置了IPC_CREATIPC_EXCL,且与key相关的信号量集存在,semget函数将调用失败,同时,errno被设置为EEXIST

参数semflg中最后9位为信号量集的权限(分别是信号量集所有者权限、所有者所属组权限和其他用户权限)。这些权限与open函数中的权限是相同的,可执行权限对于信号量而言是没有意义的,写权限意味着进程可以修改信号量集中信号量的值。

 

错误信息:

EACCES:与key相关的信号量集存在,调用进程没有权限访问该信号量集。

EEXIST:与key相关的信号量集存在,参数semflg设置了IPC_CREATIPC_EXCL

EINVAL:参数nsems小于0或大于信号量集中信号量的最大值,nsems与信号量集的大小不符。

ENOENT:与key相关的信号量集不存在,且参数semflg没有设置IPC_CREAT

ENOMEM:成功创建信号量集,系统空间不足。

ENOSPC:超出系统最大充许的信号量集限制(SEMMNI),或超出系统最大充许的信号量限制(SEMMNS)。

 

实例:

程序为使用semget函数创建信号量集的实例。在调用semget函数时,使用了不同的参数。用以示范在不同调用参数的情况下,semget函数的返回结果和错误信息。见下表:

semget函数

 

参数key

参数msgflg

semget调用结果

errno信息

IPC_PRIVATE

无要求

成功

不存在相同key

IPC_CREAT|权限

成功

存在相同key

IPC_CREAT|

IPC_EXCL|权限值

失败

EEXIST

存在相同key

IPC_CREAT|权限

成功

 

具体代码如下:

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

int main(void)
{
	key_t key;
	int semid;
	int nsems;
	int proj_id;

	key = IPC_PRIVATE;
	nsems = 2;

	semid = semget(key, nsems, 0666);
	if (semid == -1)
	{
		perror("Cannot create semaphore set");
	}
	else
	{
		printf("1.key = IPC_PRIVATE, semid=%d\n", semid);
	}

	proj_id = 1;
	key = ftok("./program", proj_id);
	if (key == -1)
	{
		perror("Cannot generate IPC key");
	}

	semid = semget(key, nsems, IPC_CREAT|0666);
	if (semid == -1)
	{
		perror("Cannot create semphore set");
	}
	else
	{
		printf("2.key=%d generated by ftok, semphore set semid=%d\n", key, semid);
	}

	semid = semget(key, nsems, IPC_CREAT|IPC_EXCL|0666);
	if (semid == -1)
	{
		perror("Cannot create semphore set");
	}

	semid = semget(key, nsems, IPC_CREAT|0666);
	if (semid == -1)
	{
		perror("Cannot create semphore set");
	}
	else
	{
		printf("Access the existing semphore set\n");
	}


	return (0);

}

运行结果:
[root@localhost test]# ./semget 
1.key = IPC_PRIVATE, semid=0
2.key=16912503 generated by ftok, semphore set semid=32769
Cannot create semphore set: File exists
Access the existing semphore set
[root@localhost test]# ipcs -s

------ Semaphore Arrays --------
key        semid      owner      perms      nsems     
0x00000000 0          root      666        2         
0x01021077 32769      root      666        2   


 

六,信号量集中的semid_ds数据结构

系统为每个信号量集资源维护着对应的结构体semid_ds的实例,该结构体的定义具体如下:

struct semid_ds {

      structipc_perm      sem_perm;         /* permissions .. see ipc.h */

      __kernel_time_t    sem_otime;        /* last semop time */

      __kernel_time_t       sem_ctime;       /* last change time */

      structsem     *sem_base;         /* ptr to first semaphore in array */

      structsem_queue * sem_pending;          /*pending operations to be processed */

      structsem_queue ** sem_pending_last;      /*last pending operation */

      structsem_undo      *undo;                /* undo requests on this array */

      unsignedshort   sem_nsems;        /* no. of semaphores in array */

};

sem_perm:为结构体ipc_perm的一个实例。用于保存信号量集的访问权限及信号量集的创建者等信息。该结构体ipc_perm具体定义如下所示:

struct ipc_perm

{

      __kernel_key_t key; //调用semget时的key

      __kernel_uid_t  uid;//所有者的EUID

      __kernel_gid_t  gid;//所有者所属组的EGID

      __kernel_uid_t  cuid;信号量创建者的EUID

      __kernel_gid_t  cgid;//信号量创建者的EGID

      __kernel_mode_t    mode; //权限

      unsignedshort   seq;//序列号

};

sem_otime:最后一次调用semop的时间。

sem_ctime:最后一次调用semctl的时间。

sem_nsems:信号量中信号量的个数。

 

当调用semget创建新的信号量集时,将修改与信号量集相关的semid_ds结构体中的变量取值,具体如下:

sem_perm.cuidsem_perm.uid设置成调用进程的有效用户ID

sem_perm.cgidsem_perm.gid设置成调用进程的有效组ID

sem_perm.mod中的最后9位设置成semflg参数中的最后9位。

sem_nsems设置成nsems的值。

sem_otime设置成0.

sem_ctime设置成当前系统时间。

 

 

 

 

 

 

 

 

 

展开阅读全文

没有更多推荐了,返回首页