http://www.cnblogs.com/BloodAndBone/archive/2011/01/18/1938552.html

一、Posix有名信号灯

1.posix有名信号灯函数

函数sem_open创建一个新的有名信号灯或打开一个已存在的有名信号灯。有名信号灯总是既可用于线程间的同步,又能用于进程间的同步。

1. sem_open

名称::
sem_open
功能:
创建并初始化有名信号灯
头文件:
#include
函数原形:
sem_t *sem_open(const char *name,int oflag,/*mode_t mode,unsigned int value*/);
参数:
name   信号灯的外部名字
oflag   选择创建或打开一个现有的信号灯
mode 权限位
value 信号灯初始值
返回值:
成功时返回指向信号灯的指针,出错时为SEM_FAILED
      
oflag参数能是0、O_CREAT(创建一个信号灯)或O_CREAT|O_EXCL(如果没有指定的信号灯就创建),如果指定了O_CREAT,那
么第三个和第四个参数是需要的;其中mode参数指定权限位,value参数指定信号灯的初始值,通常用来指定共享资源的书面。该初始不能超过
SEM_VALUE_MAX,这个常值必须低于为32767。二值信号灯的初始值通常为1,计数信号灯的初始值则往往大于1。
  
如果指定了O_CREAT(而没有指定O_EXCL),那么只有所需的信号灯尚未存在时才初始化他。所需信号灯已存在条件下指定O_CREAT不是个错
误。该标志的意思仅仅是“如果所需信号灯尚未存在,那就创建并初始化他”。不过所需信号灯等已存在条件下指定O_CREAT|O_EXCL却是个错误。
   sem_open返回指向sem_t信号灯的指针,该结构里记录着当前共享资源的数目。


/*semopen.c*/
#include
#include
#include
#include
#include int main(int argc,char **argv)
{
sem_t *sem; if(argc!=2)
{
printf(“please input a file name!\n”);
exit(1);
}
sem=sem_open(argv[1],O_CREAT,0644,1);
exit(0);
}

2. sem_close

名称::
sem_close
功能:
关闭有名信号灯
头文件:
#include
函数原形:
int sem_close(sem_t *sem);
参数:
sem 指向信号灯的指针
返回值:
若成功则返回0,否则返回-1。

一个进程终止时,内核还对其上仍然打开着的所有有名信号灯自动执行这样的信号灯关闭操作。不论该进程是自愿终止的还是非自愿终止的,这种自动关闭都会发生。
   但应注意的是关闭一个信号灯并没有将他从系统中删除。这就是说,Posix有名信号灯至少是随内核持续的:即使当前没有进程打开着某个信号灯,他的值仍然保持。

3. sem_unlink

名称::
sem_unlink
功能:
从系统中删除信号灯
头文件:
#include
函数原形:
int sem_unlink(count char *name);
参数:
name   信号灯的外部名字
返回值:
若成功则返回0,否则返回-1。

有名信号灯使用sem_unlink从系统中删除。
每个信号灯有一个引用计数器记录当前的打开次数,sem_unlink必须等待这个数为0时才能把name所指的信号灯从文件系统中删除。也就是要等待最后一个sem_close发生。


/*semunlink.c*/
#include
#include
#include
#include
#include int main(int argc,char **argv)
{
sem_t *sem;
int val;
if(argc!=2)
{
printf(“please input a file name!\n”);
exit(1);
}
if((sem_unlink(argv[1]))!=0)
perror(“sem_unlink”);
else
printf(“success”);
exit(0);
}

4. sem_getvalue

名称::
sem_getvalue
功能:
测试信号灯
头文件:
#include
函数原形:
int sem_getvalue(sem_t *sem,int *valp);
参数:
sem 指向信号灯的指针
返回值:
若成功则返回0,否则返回-1。
sem_getvalue在由valp指向的正数中返回所指定信号灯的当前值。如果该信号灯当前已上锁,那么返回值或为0,或为某个负数,其绝对值就是等待该信号灯解锁的线程数。


/*semgetvalue.c*/
#include
#include
#include
#include
#include int main(int argc,char **argv)
{
sem_t *sem;
int val; if(argc!=2)
{
printf(“please input a file name!\n”);
exit(1);
}
sem=sem_open(argv[1],0);
sem_getvalue(sem,&val);
printf(“getvalue:value=%d\n”,val);
exit(0);
}

5. sem_wait/sem_trywait

名称::
sem_wait/sem_trywait
功能:
等待共享资源
头文件:
#include
函数原形:
int sem_wait(sem_t *sem);
int sem_trywait(sem_t *sem);
参数:
sem 指向信号灯的指针
返回值:
若成功则返回0,否则返回-1。

我们能用sem_wait来申请共享资源,sem_wait函数能测试所指定信号灯的值,如果该值大于0,那就将他减1并即时返回。我们就能使用申请来的
共享资源了。如果该值等于0,调用线程就被进入睡眠状态,直到该值变为大于0,这时再将他减1,函数随后返回。sem_wait操作必须是原子的。
sem_wait和sem_trywait的差别是:当所指定信号灯的值已是0时,后者并不将调用线程投入睡眠。相反,他返回一个EAGAIN错误。

下面的程式我们先不去运行,稍后再运行。


/*semwait.c*/
#include
#include
#include
#include
#include int main(int argc,char **argv)
{
sem_t *sem;
int val; if(argc!=2)
{
printf(“please input a file name!\n”);
exit(1);
}
sem=sem_open(argv[1],0);
sem_wait(sem);
sem_getvalue(sem,&val);
printf(“pid %ld has semaphore,value=%d\n”,(long)getpid(),val);
pause();
exit(0);
}

6. sem_post

名称::
sem_post
功能:
挂出共享资源
头文件:
#include
函数原形:
int sem_post(sem_t *sem);
int sem_getvalue(sem_t *sem,int *valp);
参数:
sem 指向信号灯的指针
返回值:
若成功则返回0,否则返回-1。

当一个线程使用完某个信号灯时,他应该调用sem_post来告诉系统申请的资源已用完。本函数和sem_wait函数的功能正好相反,他把所指定的信号灯的值加1,然后唤醒正在等待该信号灯值变为正数的任意线程。

下面的程式我们先不去运行,稍后再运行。


/*sempost.c*/
#include
#include
#include
#include
#include int main(int argc,char **argv)
{
sem_t *sem;
int val; if(argc!=2)
{
printf(“please input a file name!\n”);
exit(1);
}
sem=sem_open(argv[1],0);
sem_post(sem);
sem_getvalue(sem,&val);
printf(“value=%d\n”, val);
exit(0);
}

二. 关于posix有名信号灯使用的几点注意

我们要注意以下几点:
1.Posix有名信号灯的值是随内核持续的。也就是说,一个进程创建了一个信号灯,这个进程结束后,这个信号灯还存在,并且信号灯的值也不会改动。
下面我们利用上面的几个程式来证实这点
#./semopen test
#./semgetvalue test
value=1   信号的值仍然是1

2。当持有某个信号灯锁的进程没有释放他就终止时,内核并不给该信号灯解锁。
#./semopen test
#./semwait test&
pid 1834 has semaphore,value=0
#./semgetvalue test
value=0 信号量的值变为0了

3.posix有名信号灯应用于多线程
#include
#include
#include
#include
#include

void *thread_function(void *arg); /*线程入口函数*/
void print(pid_t); /*共享资源函数*/
sem_t *sem; /*定义Posix有名信号灯*/
int val; /*定义信号灯当前值*/

int main(int argc,char *argv[])
{
int n=0;

if(argc!=2)
{
printf(“please input a file name!\n”);
exit(1);
}
sem=sem_open(argv[1],O_CREAT,0644,3); /*打开一个信号灯*/

while(n++循环创建5个子线程,使他们同步运行*/
{
if((pthread_create(&a_thread,NULL,thread_function,NULL))!=0)
       {
         perror(“Thread creation failed”);
         exit(1);
      }
}
pthread_join(a_thread,NULL);
sem_close(bin_sem);
sem_unlink(argv[1]);
}

void *thread_function(void *arg)
{
sem_wait(sem); /*申请信号灯*/
print(); /*调用共享代码段*/
sleep(1);
sem_post(sem); /*释放信号灯*/
printf(“I’m finished,my tid is %d\n”,pthread_self());
}

void print()
{
printf(“I get it,my tid is %d\n”,pthread_self());
sem_getvalue(sem,&val);
printf(“Now the value have %d\n”,val);
}

程式用循环的方法建立5个线程,然后让他们调用同一个线程处理函数thread_function,在函数里我们利用信号量来限制访问共享资源的线程数。共享资源我们用print函数来代表,在真正编程中他有能是个终端设备(如打印机)或是一段有实际意义的代码。

运行结果为:
#gcc ?lpthread ?o 8_1 8_1.c
#./8_1 test
I get it,my tid is 1082330304
Now the value have 2
Iget it,my pid is 1894
Now the value have 1
Iget it,my pid is 1895
Now the value have 0
I’m finished,my pid is 1893
I’m finished,my pid is 1894
I’m finished,my pid is 1895
I get it,my pid is 1896
Now the value have 2
I get it,mypid is 1897
Now the value have 1
I’m finished,my pid is 1896
I’m finished,my pid is 1897

4.posix有名信号灯应用于多进程
下面就是应用Posix有名信号灯的一个小程序。用它来限制访问共享代码的进程数目。
#include
#include
#include
#include

void print(pid_t);
sem_t *sem; /*定义Posix有名信号灯*/
int val; /*定义信号灯当前值*/

int main(int argc,char *argv[])
{
int n=0;

if(argc!=2)
{
printf(“please input a file name!\n”);
exit(1);
}
sem=sem_open(argv[1],O_CREAT,0644,2); /*打开一个信号灯, 初值设为2*/

while(n++循环创建5个子进程,使它们同步运行*/
{
if(fork()==0)
{
       sem_wait(sem); /*申请信号灯*/
       print(getpid()); /*调用共享代码段*/
       sleep(1);
       sem_post(sem); /*释放信号灯*/
       printf(“I’m finished,my pid is %d\n”,getpid());
       return 0;
}
}
wait(); /*等待所有子进程结束*/
sem_close(sem);
sem_unlink(argv[1]);
exit(0);
}

void print(pid_t pid)
{
printf(“I get it,my pid is %d\n”,pid);
sem_getvalue(sem,&val);
printf(“Now the value have %d\n”,val);
}

程序编译后运行会得到如下结果:
#./8_2 8_2.c
I get it,my tid is 1082330304
Now the value have 1
I get it,my tid is 1090718784
Now the value have 0
I finished,my pid is 1082330304
I finished,my pid is 1090718784
I get it,my tid is 1099107264
Now the value have 1
I get it,my tid is 1116841120
Now the value have 0
I finished,my pid is 1099107264
I finished,my pid is 1116841120
I get it,my tid is 1125329600
Now the value have 1
I finished,my pid is 1125329600

三、基于内存的信号灯

前面已经介绍了Posix有名信号灯。这些信号灯由一个name参数标识,它通常指代文件系统中的某个文件。然而Posix也提供基于内存的信号灯,它们由应用程序分配信号灯的内存空间,然后由系统初始化它们的值。

7.
名称::
sem_init/sem_destroy
功能:
初始化/关闭信号等
头文件:
#include
函数原形:
int sem_init(sem_t *sem,int shared,unsigned int value);
int sem_getvalue(sem_t *sem);
参数:
sem 指向信号灯的指针
shared   作用范围
value 信号灯初始值
返回值:
若成功则返回0,否则返回-1。

基于内存的信号灯是由sem_init初始化的。sem参数指向必须由应用程序分配的sem_t变量。如果shared为0,那么待初始化的信号灯是在同
一进程的各个线程共享的,否则该信号灯是在进程间共享的。当shared为零时,该信号灯必须存放在即将使用它的所有进程都能访问的某种类型的共享内存
中。跟sem_open一样,value参数是该信号灯的初始值。
   使用完一个基于内存的信号灯后,我们调用sem_destroy关闭它。
除了sem_open和sem_close外,其它的poisx有名信号灯函数都可以用于基于内存的信号灯。

注意:posix基于内存的信号灯和posix有名信号灯有一些区别,我们必须注意到这些。
1.sem_open不需要类型与shared的参数,有名信号灯总是可以在不同进程间共享的。
2.sem_init不使用任何类似于O_CREAT标志的东西,也就是说,sem_init总是初始化信号灯的值。因此,对于一个给定的信号灯,我们必须小心保证只调用一次sem_init。
3.sem_open返回一个指向某个sem_t变量的指针,该变量由函数本身分配并初始化。但sem_init的第一个参数是一个指向某个sem_t变量的指针,该变量由调用者分配,然后由sem_init函数初始化。
4.posix有名信号灯是通过内核持续的,一个进程创建一个信号灯,另外的进程可以通过该信号灯的外部名(创建信号灯使用的文件名)来访问它。
posix基于内存的信号灯的持续性却是不定的,如果基于内存的信号灯是由单个进程内的各个线程共享的,那么该信号灯就是随进程持续的,当该进程终止时它
也会消失。如果某个基于内存的信号灯是在不同进程间同步的,该信号灯必须存放在共享内存区中,这要只要该共享内存区存在,该信号灯就存在。
5.基于内存的信号灯应用于线程很麻烦(待会你会知道为什么),而有名信号灯却很方便,基于内存的信号灯比较适合应用于一个进程的多个线程。

下面是posix基于内存的信号灯实现一个进程的各个线程间的互次。
#include
#include
#include
#include
#include
#incude

void *thread_function(void *arg); /*线程入口函数*/
void print(void); /*共享资源函数*/

sem_t bin_sem; /*定义信号灯*/
int value; /*定义信号量的灯*/

int main()
{
int n=0;
pthread_t a_thread;

if((sem_init(&bin_sem,0,2))!=0) /*初始化信号灯,初始值为2*/
{
perror(“sem_init”);
exit(1);
}
while(n++循环创建5个线程*/
{
if((pthread_create(&a_thread,NULL,thread_function,NULL))!=0)
{
perror(“Thread creation failed”);
exit(1);
}
}
pthread_join(a_thread,NULL);/*等待子线程返回*/
}

void *thread_function(void *arg)
{
sem_wait(&bin_sem); /*等待信号灯*/
print();
sleep(1);
sem_post(&bin_sem); /*挂出信号灯*/
printf(“I finished,my pid is %d\n”,pthread_self());
pthread_exit(arg);
}

void print()
{
printf(“I get it,my tid is %d\n”,pthread_self());
sem_getvalue(&bin_sem,&value); /*获取信号灯的值*/
printf(“Now the value have %d\n”,value);
}

posix基于内存的信号灯和有名信号灯基本是一样的,上面的几点区别就可以了。
下面是运行结果:
#gcc –lpthread –o seminitthread seminitthread.c
#./seminitthread
I get it,my tid is 1082330304
Now the value have 1
I get it,my tid is 1090718784
Now the value have 0
I finished,my pid is 1082330304
I finished,my pid is 1090718784
I get it,my tid is 1099107264
Now the value have 1
I get it,my tid is 1116841120
Now the value have 0
I finished,my pid is 1099107264
I finished,my pid is 1116841120
I get it,my tid is 1125329600
Now the value have 1
I finished,my pid is 1125329600

下面的程序并不能得到我们想要的结果。
#include
#include
#include
#include

void print(pid_t);
sem_t *sem; /*定义Posix有名信号灯*/
int val; /*定义信号灯当前值*/

int main(int argc,char *argv[])
{
int n=0;

sem=sem_open(argv[1],O_CREAT,0644,3); /*打开一个信号灯*/
sem_getvalue(sem,&val); /*查看信号灯的值*/
printf(“The value have %d\n”,val);

while(n++循环创建5个子进程,使它们同步运行*/
{
if(fork()==0)
{
       sem_wait(sem); /*申请信号灯*/
       print(getpid()); /*调用共享代码段*/
       sleep(1);
       sem_post(sem); /*释放信号灯*/
       printf(“I’m finished,my pid is %d\n”,getpid());
       return 0;
}
wait(); /*等待所有子进程结束*/
return 0;
}

void print(pid_t pid)
{
printf(“I get it,my pid is %d\n”,pid);
sem_getvalue(sem,&val);
printf(“Now the value have %d\n”,val);
}

下面是运行结果:
#cc –lpthread –o sem sem.c
#./sem
The value have 3
I get it,my pid is 2236
Now the value have 2
I get it,my pid is 2237
Now the value have 2
I get it,my pid is 2238
Now the value have 2
I get it,my pid is 2239
Now the value have 2
Iget it,my pid is 2240
Now the value have 2
I’m finished,my pid is 2236
I’m finished,my pid is 2237
I’m finished,my pid is 2238
I’m finished,my pid is 2239
I’m finished,my pid is 2240

问题在于sem信号灯不在共享内存区中。fork出来的子进程通常不共享父进程的内存空间。子进程是在父进程内存空间的拷贝上启动的,它跟共享内存不是一回事。

 

POSIX semaphore: sem_open, sem_close, sem_post, sem_wait的更多相关文章

  1. 线程相关函数(7)-sem_post(), sem_wait() 信号量

    sem_tsem_initsem_waitsem_trywaitsem_timedwaitsem_postsem_destroy 生产者消费者实例: #include <stdlib.h> ...

  2. 线程同步之信号量(sem_init,sem_post,sem_wait)

    信号量和互斥锁(mutex)的区别:互斥锁只允许一个线程进入临界区,而信号量允许多个线程同时进入临界区. 不多做解释,要使用信号量同步,需要包含头文件semaphore.h. 主要用到的函数: int ...

  3. System IPC 与Posix IPC(semaphore信号灯)

    POSIX下IPC主要包括三种: posix message queue posix semaphores posix shared memory sysytem v IPC包括: system v ...

  4. Linux进程通信 之 信号灯(semphore)(System V && POSIX)

    一. 信号灯简介 信号灯与其他进程间通信方式不大相同,它主要提供对进程间共享资源访问控制机制. 相当于内存中的标志,进程可以根据它判定是否能够访问某些共享资源,同时,进程 也可以修改该标志.除了用于访 ...

  5. 进程间通信之POSIX信号量

    POSIX信号量接口,意在解决XSI信号量接口的几个不足之处: POSIX信号量接口相比于XSI信号量接口,允许更高性能的实现. POSIX信号量接口简单易用:没有信号量集,其中一些接口模仿了我们熟悉 ...

  6. Linux互斥和同步应用程序(四):posix互斥信号和同步

           [版权声明:尊重原创.转载请保留源:blog.csdn.net/shallnet 要么 .../gentleliu,文章仅供学习交流,请勿用于商业用途]          在前面讲共享内 ...

  7. Linux进程同步之POSIX信号量

    POSIX信号量是属于POSIX标准系统接口定义的实时扩展部分.在SUS(Single UNIX Specification)单一规范中,定义的XSI IPC中也同样定义了人们通常称为System V ...

  8. Linux多线程实践(5) --Posix信号量与互斥量解决生产者消费者问题

    Posix信号量 Posix 信号量 有名信号量 无名信号量 sem_open sem_init sem_close sem_destroy sem_unlink sem_wait sem_post ...

  9. POSIX信号量

    DESCRIPTION POSIX 信号量允许进程间和线程间同步他们的操作. 一个信号量是一个整型(integer),其值不能小于0. 信号量允许2中操作:给信号量的值加1(sem_post); 给信 ...

随机推荐

  1. oracle 文件导出

    $ cat me.sqlset echo offset term offset line 1000 pages 0set feedback offset heading offset trimspoo ...

  2. setEllipsize(TruncateAt where)

    void android.widget.TextView.setEllipsize(TruncateAt where) public void setEllipsize (TextUtils.Trun ...

  3. Sql Server数据库

    1.表分区 http://www.cnblogs.com/huangxincheng/p/3565755.html 2.MVP教程地址:http://www.cnblogs.com/lyhabc/p/ ...

  4. ios系统 处理内存警告

    iPhone下每个app可用的内存是被限制的,如果一个app使用的内存超过20M,则系统会向该app发送Memory Warning消息.收到此消息后,app必须正确处理,否则可能出错或者出现内存泄露 ...

  5. iOS-音频格式转换-b

    iOS处理音频过程中有时候需要不同格式的音频进行转换,最近需要将m4a格式的音频转换成wav,在网上搜索之后代码整理如下: - (void)convetM4aToWav:(NSURL *)origin ...

  6. MITK Tutorial (三)

    Step 2: Use the template with the plugins to read a image 在exampleplugin插件中QmitkAwesomeView.cpp中添加头文 ...

  7. libreoffice转换文档的方法(支持各平台各版本的libreoffice)

    前段时间完成了一个利用libreoffice转换文档进行预览的资源管理系统,用的是jodconvert这个多年未更新的转换项目,由于版本不兼容等原因,导致最新版的libreoffice不能用,浪费了许 ...

  8. 文字沟通工具使用SignalR,跨域例子源代码

    其他网站已经有很多关于SignalR的介绍了.这里不多介绍. 安装:Install-Package Microsoft.AspNet.SignalR -Version 1.1.4 参考自:http:/ ...

  9. win7下Chrome有两个图标的解决方法

    摘抄自:http://www.sevenforums.com/browsers-mail/238406-windows-7-taskbar-creating-double-google-chrome- ...

  10. sql2008 计划自动创建数据库分区【转】

    本文转自:http://jingyan.baidu.com/article/6b97984d9a26ec1ca3b0bf77.html sql2008 计划自动创建数据库分区 固定增量的数据,自动创建 ...