Linux下信号量的使用Word格式文档下载.docx
- 文档编号:20474750
- 上传时间:2023-01-23
- 格式:DOCX
- 页数:20
- 大小:39.29KB
Linux下信号量的使用Word格式文档下载.docx
《Linux下信号量的使用Word格式文档下载.docx》由会员分享,可在线阅读,更多相关《Linux下信号量的使用Word格式文档下载.docx(20页珍藏版)》请在冰豆网上搜索。
注:
这里需要知道是调semget()创建一个新的信号量集并没有对之初始化,需要调用后面要讲的semctl()函数进行初始化。
这样SystemV信号量的创建和初始化就不是一个原子操作,这是一个很大的缺陷。
会出现使用未初始化信号量集的问题,我将在下面的段落讲解如何避免这种情况。
3.SystemV信号量的控制操作
intsemctl(intsemid,intsemnum,intcmd,.../*unionsemunarg*/);
//若失败返回-1,并设置errno。
semctl函数主要是对信号量集的一系列控制操作,根据操作命令cmd的不同,执行不同的操作,依赖于所请求的命令,第四个参数是可选的,
semid:
SystemV信号量的标识符;
semnum:
表示信号量集中的第semnum个信号量。
它的取值范围:
0~nsems-1。
cmd:
操作命令;
arg:
如果使用该参数,该参数的类型为unionsemun,它是多个特定命令的联合。
按照SUS明确规定,这个结构必须有用户自己定义,在Linux2.6.18的系统头文件中也没有该结构的定义,但在<
bits/sem.h>
中有一段对该结构的定义的建议,不过是注释掉的。
include<
unionsemun{
intval;
//ValueforSETVAL
structsemid_ds*buf;
//BufferforIPC_STAT,IPC_SET
unsignedshort*array;
//ArrayforGETALL,SETALL
structseminfo*__buf;
//BufferforIPC_INFO(Linux-specific)
};
semctl中cmd命令有10种,如下:
•IPC_STAT:
获取此信号量集合的semid_ds结构,存放在第四个参数arg的buf中;
•IPC_SET:
通过arg.buf来设定信号量集相关联的semid_ds中信号量集合权限为sem_perm中的uid,gid,mode。
•IPC_RMID:
从系统中删除该信号量集合。
这种删除立即发生,仍在使用该信号量集的其他进程,在下次对该信号量集进行操作的时候,会发生错误并返回EIDRM。
这和POSIX信号量是不一样的。
POSIX信号量sem_unlink只是会立即删除信号量的在文件系统中的文件,而信号量的析构是在最后一个sem_close发生是进行的。
•GETVAL:
返回第semnum个信号量的值;
•SETVAL:
设置第semnum个信号量的值,该值由第四个参数arg中的val指定;
•GETPID:
返回第semnum个信号量的sempid,最后一个操作的pid;
•GETNCNT:
返回第semnum个信号量的semncnt。
等待semval变为大于当前值的线程数;
•GETZCNT:
返回第semnum个信号量的semzcnt。
等待semval变为0的线程数。
•GETALL:
去信号量集合中所有信号量的值,将结果存放到arg中的array所指向的数组。
•SETALL:
按arg.array所指向的数组中的值,设置集合中所有信号量的值。
对于GETALL以外的所有GET命令,semctl都返回相应的值,其他命令的返回值为0;
4.SystemV信号量的信号量操作
intsemop(intsemid,structsembuf*sops,unsignednsops);
intsemtimedop(intsemid,structsembuf*sops,unsignednsops,
structtimespec*timeout);
//成功返回0,出错返回-1
semop函数主要是在已打开的信号量集上,对其中的一个或多个信号量的值进行操作。
SystemV信号量的标识符,用来标识一个信号量集。
sops:
是指向一个structsembuf结构体数组的指针,该数组是一个信号量操作数组。
nsops:
sops所指向sembuf结构体数组中元素的个数。
sembuf结构体的定义如下:
structsembuf
{
unsignedshortintsem_num;
/*信号量的序号从0~nsems-1*/
shortintsem_op;
/*对信号量的操作,>
0,0,<
0*/
shortintsem_flg;
/*操作标识:
0,IPC_WAIT,SEM_UNDO*/
sem_num标识信号量集中的第几个信号量,0表示第1个,1表示第2个,nsems-1表示最后一个。
sem_op标识对信号量的所进行的操作类型。
对信号量的操作有三种类型:
•sem_op>
0,对该信号量执行挂出操作,挂出的值由sem_op决定,系统会把sem_op的值加到该信号量的当前值semval(参考文章开头关于每个信号量结构的定义)上。
如果sem_flag指定了SEM_UNDO(还原)标志,那么相应信号量的semadj值会减掉sem_op的值。
下面会说明semadj的含义。
•sem_op<
0,对该信号量执行等待操作,当信号量的当前值semval>
=-sem_op时,semval减掉sem_op的绝对值,为该线程分配对应数目的资源。
如果指定SEM_UNDO,相应信号量的semadj就加上sem_op的绝对值。
当semval<
-sem_op时,相应信号量的semncnt就加1,调用线程被阻塞,直到semval>
=-sem_op,当此条件满足时,调用线程被唤醒,执行相应的分配操作,然后semncnt减去1.
•sem_op=0,表示调用者希望semval变为0。
如果为0则立即返回,如果不为0,相应信号量的semzcnt加1,调用调用线程被阻塞。
sem_flag:
信号量操作的属性标志,如果为0,表示正常操作,如果为IPC_WAIT,使对信号量的操作时非阻塞的。
即指定了该标志,调用线程在信号量的值不满足条件的情况下不会被阻塞,而是直接返回-1,并将errno设置为EAGAIN。
如果为SEM_UNDO,那么将维护进程对信号量的调整值,以便进程结束时恢复信号量的状态。
semval:
信号量的当前值,在文章开头信号量的结构中已提到。
semncnt:
等待semval变为大于当前值的线程数。
在文章开头信号量的结构中已提到。
semzcnt:
semadj:
指定信号量针对某个特定进程的调整值。
只有sembuf结构的sem_flag指定为SEM_UNDO后,semadj才会随着sem_op而更新。
讲简单一点:
对某个进程,在指定SEM_UNDO后,对信号量semval值的修改都会反应到semadj上,当该进程终止的时候,内核会根据semadj的值,重新恢复信号量之前的值。
5.SystemV信号量的继承和销毁
父进程中打开的信号量在子进程中仍然是保持着打开状态的。
对于一个持有该信号量的进程在没有释放该信号量的情况下就终止了。
那么该进程所占用的信号量内核是不会进行释放的。
当通过semctl传入IPC_RMID对该信号量集进行删除时,会立即将该信号量从系统中彻底删除,不能再对该信号量进行任何访问。
第三个参数cmd为IPC_RMID时,整个信号集被删除,第二个参数semnum被忽略。
这就产生了一个最后退出的进程才可以销毁信号量,否则退出的进程销毁信号量后会产生资源的争夺问题。
在下面的例子中,我会讲如何避免这种情况的发生。
6.信号量使用的一个具体例子
6.1打开信号量并进行初始化:
两种组合IPC_CREAT|IPC_EXCL|00666和IPC_CREAT|00666的不同点,第一个如果没有这个信号则创建一个,如果已经存在则产生一个存在错误。
第二个参数如果没有创建信号,有则返回已经存在的信号ID。
//主要是防止两个进程之间从创建信号量到初始化信号量的竞争
staticintcreatesem(key_tkey,intinitval)
intflag1=IPC_CREAT|IPC_EXCL|00666;
intflag2=IPC_CREAT|00666;
inti=0;
intsemid=semget(key,1,flag1);
if(semid<
0)//如果不存在则自己创建,存在则看是否已经被初始化
{
inttmperrno=errno;
if(tmperrno==EEXIST)//不是自己创建的
{
semid=semget(key,1,flag2);
if(semid<
0)
return-1;
intinit_ok=0;
for(i=0;
i<
3;
i++)
{
unionsemunarg;
structsemid_dssem_info;
arg.buf=&
sem_info;
if(semctl(semid,0,IPC_STAT,arg)==-1)
{
perror("
semctlerror"
);
i=3;
}
else
if(arg.buf->
sem_otime!
=0)
{
i=3;
init_ok=1;
}
else
usleep(300000);
//等300ms
}//for
if(!
init_ok)
//如果待900ms仍然没有初始化,可证明创建的进程没有进行初始化
arg.val=initval;
if(semctl(semid,0,SETVAL,arg)==-1)
{
perror("
semctlsetvalerror"
semaphore_v(semid);
//使用这个用改变sem_otime,以便告诉其它进程已经被初始化可以使用了。
先V后P以保证初值为0时的操作性
semaphore_p(semid);
returnsemid;
}//if(!
else
returnsemid;
}
}//ifexist
else
perror("
semgeterror"
}//esletmperrno==EEXIST
}
else//自己是创建者,则进行初始化
unionsemunarg;
arg.val=initval;
if(semctl(semid,0,SETVAL,arg)==-1)
semaphore_v(semid);
semaphore_p(semid);
returnsemid;
}
return-1;
}
6.2信号量的V和P操作
staticintsemaphore_p(intsem_id)
structsembufsem_b;
sem_b.sem_num=0;
sem_b.sem_op=-1;
sem_b.sem_flg=SEM_UNDO;
if(semop(sem_id,&
sem_b,1)==-1)
fprintf(stderr,"
semaphore_pfailed/n"
return0;
return1;
//semaphore_v函数将sembuf结构的sem_op部分设置为1,从而信号量变得可用。
staticintsemaphore_v(intsem_id)
sem_b.sem_op=1;
semaphore_vfailed/n"
6.3设置初值和删除操作
staticintset_semvalue(intsem_id,intval)
unionsemunsem_union;
sem_union.val=val;
if(semctl(sem_id,0,SETVAL,sem_union)==-1)
return0;
staticvoiddel_semvalue(intsem_id)
if(semctl(sem_id,0,IPC_RMID,sem_union)==-1)
Failedtodeletesemaphore/n"
6.4如何确保最后一个进程删除信号量
1.intsem_iddel=createsem((key_t)1235,0);
//并初始化为val=0
2.semaphore_v(sem_iddel);
每个使用的进程在程序开始时都V这个信号量,请注意一定要用SEM_UNDO标志
3.semaphore_p(sem_iddel);
每个进程在退出的时候都要p这个信号量
4.intval=semctl(sem_iddel,0,GETVAL,arg);
并对信号量的值进行查看,如果和初值相等,则证明大家都已经不用了,可以进行删除操作了。
if(!
val)
del_semvalue(sem_iddel);
del_semvalue(sem_id);
下面这个例子,不能解决创建和初值问题(创建完成,但没初始化时)。
//解决信号量的创建和初始化不是原子操作的一种方案
if((semId=semget(CreateKey(SEM_PATHNAME),1,IPC_CREAT|IPC_EXCL|0666))>
=0)
arg.val=4;
if(semctl(semId,0,SETVAL,arg)<
cout<
<
"
semctlerror"
strerror(errno)<
endl;
return-1;
elseif(errno==EEXIST)
semId=semget(CreateKey(SEM_PATHNAME),1,0666);
else
semgeterror"
7.一个写,多读的例子
使有两信号量解决同一时间只有一个进程可以写,但同一时间可以有很多进程可读的例子,在这里只提供流程图,就不写代码了。
参照上面的代码很容易就可以写出下列流程图的代码。
8.具体代码的封装操作和一个实现的例子
8.1.sunnysem.h的代码
//FileNamesunnysem.h
//author:
sunny.man
//date:
2015-07-28
#ifndef_SUNNYSEM_H_
#define_SUNNYSEM_H_
//---------------------------------------------------------------------------------------------------------------------------
sys/types.h>
sys/ipc.h>
stdio.h>
errno.h>
time.h>
string.h>
#definemax_tries3
unionsemun
{
intval;
structsemid_ds*buf;
unsignedshort*array;
externintcreatesem(key_tkey,intinitval);
externintsemaphore_p(intsem_id);
externintsemaphore_v(intsem_id);
externintdel_semv(intsem_id);
externintsemaphore_waitz(intsem_id);
#endif
8.2.sunnysem.c的代码
//filename:
sunnysem.c
//-------------------------------------------------------------------------------------------------
#include"
sunnysem.h"
staticintsemaphore_pp(intsem_id,unsignedshortindex);
//内部使用
staticintsemaphore_vp(intsem_id,unsignedshortindex);
staticintisinitialization(intsem_id);
//看是否已经被初始化
staticintsetsecondsemotime(intsemid,intinitval);
staticintisinitialization(intsem_id)//看是否已经被初始化
for(i=0;
max_tries;
structsemid_dssem_info;
arg.buf=&
if(semctl(sem_id,1,IPC_STAT,arg)==-1)//取第二个标识信号来操作
{
if(arg.buf->
return1;
usleep(300000);
}//for
return0;
staticintsetsecondsemotime(intsem
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- Linux 信号量 使用