转载一篇信号量的文章,通俗易懂
信号量(semaphore)入门
国内的计算机类教材太垃圾了,大多数都是把国外的帮助文档拿来翻译一下就拿去卖钱了,可谓适应知识经济的发展,呵呵。计算机类的书籍不像文学类的书籍,只要识字就能看懂,所以这些教材编得不好,也没有多少人能看得出来(背后N个人在@!%@#%&*)。对于很多计算机知识,入门最好的方法还是从简 单的开始理解,然后那些复杂的东西就会迎刃而解。
--------------------------------------------------------------------------------
今天我来说说Linux/Unix下信号量使用的入门吧。信号量是什么?就是设置在内核当中用户解决资源冲突东东。举个简单的例子,一张凳子一个人已经坐了,第二个人就不能坐,但如果第二个人不知道这张凳子已经给人坐了,还坐上去,就会出现问题,所以如果我们约定凳子被占了就登记到一个本子上,每个人想坐 这张凳子之前都先看看这个本子有没写下东西,就不会出现上述情况了。我说的这些,只要上过操作系统的同学都知道(除非经常逃课,期末考试碰巧又混过去了:P),但是在实际编程当中,新手根本不知道Linux/Unix的信号量怎么使用。信号量从哪里来,怎么用呢?查那些教材又让人一头雾水,FT!
所以我用自己实验的例子来说说,Linux/Unix下怎么使用信号量。
要使用信号量当然就要创建啦,怎么弄?
用semid=semget(KEY,3,0777| IPC_CREAT);
其中KEY就是给信号量的键,系统会根据信号量的键分配一 个信号量ID,一个键一个信号量ID,所以如果你想不同程序都使用同一个信号量的话,只要这个KEY的值相等就行了。第二个是信号量元素的数量,其实我们 创建的是信号量集,里面有包含着元素,我的例子当中是3个元素,0777是信号量的权限,这是一个8进制数字,为什么使用它?因为权限刚好分为写、读、执 行三种,用1、2、4代表刚好,方便快捷。0777就是,根用户、用户组、一般用户都有读、写、执行的权力,IPC_CREAT是FLAG,表示创建这个 信号量,当然,如果KEY对应的信号量已经创建,则会返回已创建信号量的ID.其他的FLAG可以参考man semget.(不要告诉我你不知道man semget是什么哦,FT!就是Linux下的帮助文档啊,man就是manual的简写,记住哦。)。
好了,创建好信号量(我们用semid,一个整型变量保存信号量ID)后又怎么样呢?哦!还没有给这三个信号量元素赋初值!那要怎么做?
用semctl(semid,0,SETALL,semun_init)
semid不用说了吧,第二个参数是从第几个元素开始赋值,这里是0,即从第一个元素开始,SETALL是FLAG,这里的意思是:对所有的元素赋 值,最后一个参数是union semun类型,里面包含每个元素应赋的值,要使用union semun类型,记得在你的程序里面要加上:
#if defined(__GNU_LIBRARY__) &&!defined(_SEM_SEMUN_UNDEFINED)
/* union semun is defined by including */
#else
/* according to X/OPEN we have to define it ourselves */
union semun {
intval; /* value for SETVAL */
struct semid_ds *buf; /* buffer forIPC_STAT, IPC_SET */
unsigned short *array; /* array for GETALL,SETALL */
/* Linux specific part: */
struct seminfo *__buf; /* buffer for IPC_INFO */
};
#endif
注释上说了,根据X/OPEN,即有些系统下我们要自己定义union semun。
有了的定义之后,我们就可以定义变量semun_init
union semun semun_init;
因为我们的信号量有3个元素,所以我们会使用semun_init里面的unsigned short *array。如果只有一个元素,就使用int val。
再定义一个变量数组
unsigned short val[3]={1,1,1};//3个元素的值均为1
然后:
semun_init.array = val;
好了,准备好后就可以调用刚才的semctl(semid,0,SETALL,semun_init),信号量初始化完毕。
将程序拼在一起:
int semid;
semid=semget(KEY,3,0777 | IPC_CREAT);
union semun semun_init;
unsigned short val[3]={1,1,1};
semun_init.array = val;
semctl(semid,0,SETALL,semun_init);
semctl(semid,0,IPC_RMID);//最后不要忘了删除信号量哦。
还有一些零碎的东西,如返回值的处理啊等等,有了上面的基础,大家可以参看信号量的文档就很容易理解了。按我以上的例子,我简单举例说明一下:
int a = semctl(semid,2,GETVAL);获取第三个元素的值到变量a中,元素需要从0开始,刚好2就是第三个元素。
struct sembuf op_down={0,-1,IPC_NOWAIT};//设置信号量操作的结构体变量
semop(semid,&op_down,1);//注意!第二个参数是指针,第三个参数表示该指针指向多少个这种结构体。用这种方法我们可以一次性对几个元素进行操作。
就是将第一个元素进行-1操作,IPC_NIWAIT明显是FLAG啦,表示如果操作不成功程序不等待,而直接返回。
怎样简单否?
国内和国外的学术最大的不同就是,国外把很难的东西变得很简单,国内则把很简单的东西变得很难,以显示作者的高深。呵呵,好在,我这篇东西与学术是一个地一个天,没法比!^_^
原文出处:http://blog.ednchina.com/marshallemon/243738/message.aspx#