现在的位置: 首页 > 综合 > 正文

进程间同步之–信号量

2013年04月06日 ⁄ 综合 ⁄ 共 3908字 ⁄ 字号 评论关闭

   信号量分有名和无名信号量。它们的区别和管道及命名管道的区别类似。有名信号量要求创建一个文件,而无名信号量则直接保存在内存中。

一,Posix信号量
Posix信号量接口总结(见下图):
上面一行是有名信号量,可于fifo相类比,其值保存在文件中,可用于进程和线程同步;
下面一行是无名信号量,可与pipe相类比,其值保存在内存中,可用于进程和线程同步;
中间部分,是两者的公用接口。

  1. sem_open()                                sem_close(),sem_unlink()  //有名信号量  
  2.          \ |sem_wait(),sem_post()       |/  
  3.          / |sem_trywait(),sem_getvalue()|\sem_destroy()  //无名信号量  
  4. sem_init()  




1.公共接口

1.1 接口函数说明

#include <semaphore.h>
int sem_wait(sem_t *sem);
    测试所指定信号量的值,它的操作是原子的。
    若sem>0,那么它减1并立即返回。
    若sem==0,则睡眠直到sem>0,此时立即减1,然后返回。   

int sem_trywait(sem_t *sem);
    其他的行为和sem_wait一样,除了:
    若sem==0,不是睡眠,而是返回一个错误EAGAIN。       

int sem_post(sem_t *sem);
    把指定的信号量sem的值加1;
    呼醒正在等待该信号量的任意线程。
   
int sem_getvalue(sem_t *sem, int *sval);
    取回信号量sem的当前值,把该值保存到sval中。
    若有1个或更多的线程或进程调用sem_wait阻塞在该信号量上,该函数返回两种值:
        1) 返回0
        2) 返回阻塞在该信号量上的进程或线程数目
    linux采用返回的第一种策略。

注意:在这些函数中,只有sem_post是信号安全的函数,它是可重入函数。

1.2 接口使用的一般流程

sem_init(&sem);
sem_wait(&sem);
critical area;
sem_post(&sem);
remainder area

2.无名信号量
    无名信号量是保存在变量类型为sem_t的内存中。

int sem_init(sem_t *sem, int pshared, unsigned int value);
    1)pshared==0 用于同一多线程的同步;
    2)若pshared>0 用于多个进程间的同步,此时sem必须放在共享内存中。

int sem_destroy(sem_t *sem);
    只能销毁由sem_init初始化的信号量,否则后果不可预料也。

例1:
    多线程使用信号量的简单例子:

  1. /* 
  2.  * simple_sem_app.c 
  3.  */  
  4. #include "all.h"  
  5.   
  6. /* 每个字符输出的间隔时间 */  
  7. #define TEN_MILLION 5000000L  
  8. #define BUFSIZE 1024  
  9.   
  10. void *threadout(void *args);  
  11.   
  12. int main(int argc, char *argv[])  
  13. {  
  14.     int error;  
  15.        int i;  
  16.        int n;  
  17.     sem_t semlock;  
  18.        pthread_t *tids;  
  19.      
  20.        if (argc != 2) {  
  21.            fprintf (stderr, "Usage: %s numthreads\n", argv[0]);  
  22.               return 1;  
  23.        }    
  24.        n = atoi(argv[1]);  
  25.        tids = (pthread_t *)calloc(n, sizeof(pthread_t));  
  26.        if (tids == NULL) {  
  27.            perror("Failed to allocate memory for thread IDs");  
  28.            return 1;  
  29.        }    
  30.        if (sem_init(&semlock, 0, 1) == -1) {  
  31.            perror("Failed to initialize semaphore");  
  32.            return 1;  
  33.        }    
  34.        for (i = 0; i < n; i++) {  
  35.            if (error = pthread_create(tids + i, NULL, threadout, &semlock)) {  
  36.                fprintf(stderr, "Failed to create thread:%s\n", strerror(error));  
  37.                   return 1;  
  38.           }  
  39.     }  
  40.        for (i = 0; i < n; i++) {  
  41.            if (error = pthread_join(tids[i], NULL)) {  
  42.                fprintf(stderr, "Failed to join thread:%s\n", strerror(error));  
  43.                  return 1;  
  44.               }  
  45.     }  
  46.     return 0;  
  47. }  
  48.   
  49. void *threadout(void *args)  
  50. {  
  51.     char buffer[BUFSIZE];  
  52.        char *c;  
  53.        sem_t *semlockp;  
  54.        struct timespec sleeptime;  
  55.      
  56.        semlockp = (sem_t *)args;  
  57.        sleeptime.tv_sec = 0;  
  58.        sleeptime.tv_nsec = TEN_MILLION;  
  59.      
  60.        snprintf(buffer, BUFSIZE, "This is thread from process %ld\n",  
  61.                (long)getpid());  
  62.        c = buffer;  
  63.        /****************** entry section *******************************/  
  64.        while (sem_wait(semlockp) == -1)  
  65.            if(errno != EINTR) {  
  66.                fprintf(stderr, "Thread failed to lock semaphore\n");  
  67.                  return NULL;  
  68.               }  
  69.        /****************** start of critical section *******************/  
  70.        while (*c != '\0') {  
  71.               fputc(*c, stderr);  
  72.               c++;  
  73.               nanosleep(&sleeptime, NULL);  
  74.        }  
  75.        /****************** exit section ********************************/  
  76.        if (sem_post(semlockp) == -1)  
  77.               fprintf(stderr, "Thread failed to unlock semaphore\n");  
  78.        /****************** remainder section ***************************/  
  79.        return NULL;  
  80. }  



说明:该例子来自于usp。
    可以把sem_wait和sme_post调用去掉,看看效果,可以看到出现了交叉输出的情况。
    nanosleep调用只是为了让输出的效果更明显,没有其他意义。

更多的例子见mypxsem/prodcons2-4.c


3. 有名信号量
有名信号量是把信号量的值保存在文件中,所以它可以用于线程也可以用于进程间的同步。
如下面的形式:

  1. sem_t *mutex;  
  2. ...  
  3. mutex = sem_open(pathname, O_CREAT | O_EXCL, FILE_MODE, 0);     

抱歉!评论已关闭.