信号量集

来源:互联网 发布:大学生旅游攻略 知乎 编辑:程序博客网 时间:2024/09/21 09:29

信号量集的操作

三个IPC对象类型中,信号量集的操作函数相对于其他两个类型的操作函数要复杂得多,当然信号量的应用也比其他两个更广泛些。像共享内存的操作一样,信号量也有自己的专属操作函数semctl,函数原型如下:

#include

int semctl( int sem_id, int semnu,
int cmd [, union semun arg]);

函数中参数sem_id是一个信号量标识符,semnum指定sem_id的信号集中的某一个信号灯,其类似于在信号量集资源数组中的下标,用来对指定资源进行操作。参数cmd定义函数所要进行的操作。其取值以及表达的意义如下表所示。

cmd值详解

cmd的取值

操    作

GETVAL

返回成员semnum的semval值

SETVAL

使用arg.val对该信号量的semnum.sempid赋值

(需要参数arg)

GETPID

返回成员semnum的sempid值

GETNCNT

返回成员semnum的semncnt值

GETZCNT

返回成员semnum的semzcnt值

GETALL

将该信号量集的值赋值到arg.array(需要参数arg)

SETALL

使用arg、array数组中的值对信号量集赋值(需要参数arg)

IPC_RMID

删除信号量集。此操作只能由具有超级用户的进程或

信号量集拥有者的进程执行,这个操作会影响到正

在使用该信号量集的进程

IPC_SET

设置此信号量集的sem_perm.uid、sem_perm.gid以及

sem_perm.mode的值。此操作只能由具有超级用户的

进程或信号量集拥有者的进程执行

SPC_STAT

(需要参数arg)

函数中参数arg为可选参数,根据参数cmd的相关操作来选择使用,其定义如下:

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

函数成功返回值大于等于0(当semctl的操作为GET操作时返回相应的值,其余返回0),失败返回-1并设置错误变量errno。

下面实例演示了如何使用semctl函数。程序中先使用semget函数创建了一个新的信号量集,然后通过shell命令查看系统IPC的状态,再调用一次semctl函数做删除操作,并查看系统IPC的状态

(1)在vi编辑器中编辑该程序如下:

程序清单14-11  ctl_sem.c 使用semctl删除信号量

#include
#include
#include
#include
#include

int main( void )
{
int sem_id ;
int nsems = 1;
int flags = 0666;

semid = semget ( IPC_PRIVATE, nsems, flags ); /*创建一个信号量集*/

if ( sem_id < 0 ){        /* 创建信号量失败 */
perror ( "semget" );
exit ( 1 );
}

printf ( "successfully created a semaphore: %d \n", sem_id );
/*输出创建的信号量的ID */

system ( "ipcs -s" );       /*查看系统IPC状态*/

if ( (semctl (semid, 0, IPC_RMID)) < 0 ) {  /* 删除指定信号量集*/
perror ( "semctl" );
exit (1 );
}
else {
printf ( "semaphore removed \n");
system ( "ipcs -s ");      /*查看系统IPC状态*/
}

exit ( 0 );
}

(2)在shell中编译该程序如下:

$gcc ctl_sem.c-o ctl_sem

(3)在shell中运行该程序如下:

$./ ctl_sem

------ Semaphore Arrays --------
key        semid      owner      perms      nsems
0x00000000 294911     root      666        1
0x0056a4d5 327681     root      600        1
0x00000000 360450     root      666        1

successfully created a semaphore: 360450

------ Semaphore Arrays --------
key        semid      owner      perms      nsems
0x00000000 294911     root      666        1
0x0056a4d5 327681     root      600        1

semaphore removed

在shell中可以调用如下命令来删除已存在的信号量:

$./ ipcrm -s 


说明:上述程序中,使用semget函数创建一个信号量时,有可能系统中已经有了一个跟IPC_PRIVATE键关联的信号量,此时应在shell中先删除该IPC,然后再运行程序。

 

理解主要有4点:

1,信号量其实是一个计数变量,它只能为正整数和0,为0时表示该信号量对应的可用资源数为0;

2,semget可以创建多个信号量,而对应的semid所以称为信号量集的id;

#define SEM_KEY 0x11223344

#define NUM_SEM 2

//创建了一个包含2个信号量的信号量集,两个信号量的索引号是0和1

semid=semget(SEM_KEY,NUM_SEM,IPC_CREAT|IPC_EXCL|0666);

3,semop可以一次性进行多个操作,多个操作可以针对同一信号量,也可针对不同信号量,取决于struct sembuf的设置;

struct sembuf sem[2];

sem[0].sem_num = 0;/*对索引号为0的信号量进行操作*/

sem[1].sem_num = 0;/*对索引号为1的信号量进行操作*/

sem[0].sem_op = 0;

sem[1].sem_op = 1;

semop(semid,sem,2); /*最后一个参数nsops=2,所以在函数中同时执行sem[0],sem[1]两个操作,如果为1则执行sem[0]操作*/

4,sem_op数量的含义

信号量的 SysV 实现比上述解决方案更通用。首先,信号量的值不需要是 0 或 1;它可以是 0 或任何正数。其次,可以执行一系列信号量操作,与用于 msgrcv 的 type 参数非常类似。这些操作作为一个指令集提供给内核,并且这些指令要么全部运行,要么一个也不会运行。内核要求将这些指令放在一个名为 sembuf 的结构中,该结构具有以下成员(按顺序):

1.             sem_num:描述正在操作该集合中的哪一个信号量。

2.             sem_op:一个有符号整数,其中包含要执行的指令或测试。

3.             sem_flg:熟悉的 IPC_NOWAIT 标志(它指示测试应该立即返回还是阻塞直至通过)和 SEM_UNDO(它在进程提前退出时导致撤销该信号量操作)的组合。

sem_op 是放置许多配置的地方:

·                     如果 sem_op 为 0,则测试 sem_num 以确定它是否为 0。如果 sem_num 为 0,则运行下一个测试。如果 sem_num 不为 0,则在未设置 IPC_NOWAIT 时,操作将阻塞直至信号量变为 0,而在设置了 IPC_NOWAIT 时,则跳过其他测试。

·                     如果 sem_op 是某个正数,则将信号量的值加上 sem_op 的值。

·                     如果 sem_op 是一个负整数,并且信号量的值大于或等于 sem_op 的绝对值,则从信号量的值减去该绝对值。

·                     如果 sem_op 是一个负整数,并且信号量的值小于 sem_op 的绝对值,则在 IPC_NOWAIT 为 true 时立即停止测试的执行,而在该值为 false 时则阻塞,直至信号量的值变得大于 sem_op 的绝对值。


#include"unp.h"

#define PERMS(S_IRUSR | S_IWUSR)

union semun{
        
int val;
        
struct semid_ds*buf;
        
unsignedshort*array;
}arg;

int main(void)
{
        
int semid, i, j;
        
struct sembuf semwait[1], semsignal[1];
        
char buf[]="hello china\r\n";

        
if((semid= semget(IPC_PRIVATE, 1, PERMS))==-1)
                err_sys
("sem_get");

        
arg.val= 1;
        
if(semctl(semid, 0, SETVAL,arg)==-1)
                err_sys
("semctl");

        semwait
[0].sem_num= 0;
        semwait
[0].sem_op=-1;
        semwait
[0].sem_flg= 0;
        semsignal
[0].sem_num= 0;
        semsignal
[0].sem_op= 1;
        semsignal
[0].sem_flg= 0;

        
for(i= 0; i< 5; i++)
           
if(fork())/* parent process jump out loop */
             
break;

        
if(semop(semid, semwait, 1)==-1)
                err_sys
("semop");

        
for(j= 0; buf[j]!='\0'; j++)
                
fputc(buf[j],stdout);

        
if(semop(semid, semsignal, 1)==-1)
                err_sys
("semop");

        
exit(0);
}

void err_sys(constchar*errmsg)
{
        
perror(errmsg);
        
exit(1);
}

 

原创粉丝点击