首页 > 代码库 > 线程间通信机制posix匿名信号量

线程间通信机制posix匿名信号量

信号量分为两种

一种是简单的信号量,另一种是用于进程间通信的信号量集。

    简单的信号量

属于POSIX标准的信号量;
从信号量的命名来看,信号量又可分为命名信号量和匿名(未命名)信号量;
从信号量的值来看,信号量可分为二进制信号量和计数信号量;

    1、匿名信号量和命名信号量:
    匿名信号量是在内存中分配内存、进行初始化并由系统API进行管理的,它可以在多个线程之间进行资源同步,也可以在多个进程之间进行资源同步,这主要是看在初始化的时候给pshared传递的参数值,为0,则在线程之间同步,非0,则在进程之间同步;
  命名信号量与匿名信号量不同,它一般只用于在进程之间进行资源同步,而且是使用文件全路径来对信号量进行命名的;命名信号量具有属主用户ID、组ID和保护模式等参数;命名信号量的名称是在文件系统的命名空间中定义的;
  
  匿名信号量的操作函数有:sem_init、sem_destroy、sem_wait、sem_trywait、sem_post、sem_getvalue;
  命名信号量的操作函数有:sem_open、sem_close、sem_wait、sem_trywait、sem_post、sem_getvalue、sem_unlink;

     
#include <semaphore.h>
  int sem_init(sem_t *sem, int pshared, unsigned int value);
 
Link with -pthread.
 这个函数的作用是对由sem指定的信号量进行初始化,设置好它的共享选项,并指定一个整数类型的初始值。
pshared参数控制着信号量的类型。如果 pshared的值是0,就表示它是当前里程的局部信号量;否则,其它进程就能够共享这个信号量。
我们现在只对不让进程共享的信号量感兴趣。 (这个参数 受版本影响), 
pshared传递一个非零将会使函数调用失败。

这两个函数控制着信号量的值,它们的定义如下所示:
 #include <semaphore.h>
int sem_wait(sem_t * sem);
int sem_post(sem_t * sem);
   这两个函数都要用一个由sem_init调用初始化的信号量对象的指针做参数。
sem_post函数的作用是给信号量的值加上一个“1”,它是一个“原子操作”---即同时对同一个信号量做加“1”操作的两个线程是不会冲突的;
 sem_wait函数也是一个原子操作,它的作用是从信号量的值减去一个“1”,但它永远会先等待该信号量为一个非零值才开始做减法。也就是说,如果你对 一个值为2的信号量调用sem_wait(),线程将会继续执行,介信号量的值将减到1。如果对一个值为0的信号量调用sem_wait(),这个函数就 会地等待直到有其它线程增加了这个值使它不再是0为止。如果有两个线程都在sem_wait()中等待同一个信号量变成非零值,那么当它被第三个线程增加 一个“1”时,等待线程中只有一个能够对信号量做减法并继续执行,另一个还将处于等待状态。
#include<semaphore.h>
int sem_destroy (sem_t *sem);
    这个函数也使用一个信号量指针做参数,归还自己战胜的一切资源。在清理信号量的时候如果还有线程在等待它,用户就会收到一个错误。 与其它的函数一样,这些函数在成功时都返回“0”。
    
代码示例:
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <pthread.h>
#include <semaphore.h>

sem_t bin_sem;

void *thread_function1(void *arg)
{
 printf("thread_function1--------------sem_wait running\n");
 sem_wait(&bin_sem);
 printf("sem_wait OK\n");
}

void *thread_function2(void *arg)
{
 printf("thread_function2--------------sem_post running\n");
 sem_post(&bin_sem);
 printf("sem_post OK\n");

}

int main()
{
 int res;
 pthread_t a_thread;
 void *thread_result;

 res = sem_init(&bin_sem, 0, 0);
 if (res != 0)
 {
  perror("Semaphore initialization failed");
  }
 printf("sem_init OK\n");
 res = pthread_create(&a_thread, NULL, thread_function1, NULL);
 if (res != 0)
 {
  perror("Thread creation failure");
 }
 printf("thread_function1 OK\n");
 sleep (5);
 printf("sleep 5 seconds...\n");
 res = pthread_create(&a_thread, NULL, thread_function2, NULL);
 if (res != 0)
 {
  perror("Thread creation failure");
 }
 void* retval;
 pthread_join(a_thread,&retval);
 sem_destroy(&bin_sem);
}
  运行结果如下:

ky@ky-S910-X31E:~/libz/72$ gcc sem.c -osem -lpthread
ky@ky-S910-X31E:~/libz/72$ ./sem 
sem_init OK
thread_function1 OK
thread_function1--------------sem_wait running
sleep 5 seconds...
thread_function2--------------sem_post running
sem_post OK
sem_wait OK


     二进制信号量和计数信号量:

二进制信号量的值只有两个:0和1;这个时候信号量就相当于一个互斥锁;它表示可用资源数量只有1个,当一个调用者拥有并访问它的时候(值为0),其它调用者就必须阻塞并等待.知道拥有者释放这个信号量(值为1).

  计数信号量,从概念上来讲,它的值是一个非负整数,表示可用资源的数量;用于协调对共享资源的访问:在线程中操作共享资源的时候,当一个访问者申请使用一个单位的资源时,信号量的值减1,表示剩余的可用资源的数量减少1;当这个访问者使用完这个共享资源并退还这个共享资源给系统的时候,信号量的值增1,表示可用的共享资源数量多1;而申请资源和释放资源的操作是原子操作,即:信号量的值减1和增1操作是原子操作;也就是说,有一个资源申请动作,就必须对应有一个资源释放动作,有一个信号量减1操作,就必须对应有一个信号量增1的动作;如果信号量的值为0,就表示没有资源可用,那么这个时候,如果有后续的调用者来申请可用的共享资源,那么这个调用者就会被阻塞在这个信号量上,直到有一个调用者释放一个共享资源为止;

  二进制信号量的值只有两个:0和1;这个时候信号量就相当于一个互斥锁;它表示可用资源数量只有1个,当一个调用者拥有并访问它的时候(值为0),其它调用者就必须阻塞并等待.知道拥有者释放这个信号量(值为1).
  计数信号量,从概念上来讲,它的值是一个非负整数,表示可用资源的数量;用于协调对共享资源的访问:在线程中操作共享资源的时候,当一个访问者申请使用一个单位的资源时,信号量的值减1,表示剩余的可用资源的数量减少1;当这个访问者使用完这个共享资源并退还这个共享资源给系统的时候,信号量的值增1,表示可用的共享资源数量多1;而申请资源和释放资源的操作是原子操作,即:信号量的值减1和增1操作是原子操作;也就是说,有一个资源申请动作,就必须对应有一个资源释放动作,有一个信号量减1操作,就必须对应有一个信号量增1的动作;如果信号量的值为0,就表示没有资源可用,那么这个时候,如果有后续的调用者来申请可用的共享资源,那么这个调用者就会被阻塞在这个信号量上,直到有一个调用者释放一个共享资源为止;

       当信号量作为互斥锁使用时:


   信号量:相当于是代码周围的卫兵,当卫兵发现共享代码段正在被执行,则卫兵不让后续调用者前去执行;当共享代码段为空闲时,卫兵允许后续调用者去执行;
   互斥量:相当于一把多人共用的钥匙,谁拥有这把钥匙,谁就可以访问受保护的代码段;

信号量集:

它属于System V信号量,是一个集合;
常用于进程间的通讯;System V的IPC要求用于进程间通讯的信号量必须是一个集合;它是系统内核定义的一个数据结构;
信号量集的初始化操作由shmget函数完成;