zoukankan      html  css  js  c++  java
  • Linux 信号量之Posix有名字的信号量

    信号量(semaphore),也和互斥锁一样提供了线程间或者进程间的同步功能。

    信号量有三种:

    信号量比互斥锁高级,互斥锁只允许一个线程访问临界区,信号量可以多个,可以把信号量看作成互斥锁的升级版,但是如果能用互斥锁解决,就用互斥锁,互斥锁比信号量节省资源。

    这篇文章只介绍Posix有名字的信号量

    1,创建有名字的信号量,创建成功后,会在ubuntu的/dev/shm目录下,生成一个文件,名字为【sem.name】。【sem】是固定的,【name】是函数sem_open的第一个参数。

    名字的信号量的生命周期和内核一样,只要系统不重启,它就一直存在。

    #include <fcntl.h>           /* For O_* constants */
    #include <sys/stat.h>        /* For mode constants */
    #include <semaphore.h>
    
    sem_t *sem_open(const char *name, int oflag);
    sem_t *sem_open(const char *name, int oflag,
                    mode_t mode, unsigned int value);
    
    • name:任意名字,当不能包含【/】
    • oflag:和open函数一样,O_RDWR,O_CREAT,O_EXCL等
    • mode:和open函数一样,比如0664
    • value:可以同时访问临界区的线程或者进程的数量。如果设置为1,功能就和互斥锁一样了。
    • 返回值:成功0;失败:SEM_FAILED(这个宏的实际值是-1)。

    2,删除有名字的信号量,并删除文件。

    #include <semaphore.h>
    int sem_unlink(const char *name);
    
    • 返回值:成功0

      • 失败:-1,设置errno

        EACCES:没有权限访问这个信号量对应的文件
        ENAMETOOLONG:信号量的名字长了
        ENOENT:信号量不存在
        

    3,取得信号量的value值

    #include <semaphore.h>
    int sem_getvalue(sem_t *sem, int *sval);
    
    • sem:信号量指针
    • sval:返回的信号量的value值
    • 返回值:成功0;失败:-1,设置errno。(EINVAL :不是一个有效的信号量)。

    4,如果信号量的value值大于0,把信号量的value值-1;如果信号量的value值小于1,阻塞等待,直到信号量的value值大于0。注意:在ubuntu下,如果sem_wait执行前,value值为0,sem_wait执行后,value也不会变成-1,再次执行sem_wait,value还是0。但是有的unix系统value会变成负数。即使value不变成负数,内核也会准确记录它的值。

    #include <semaphore.h>
    int sem_wait(sem_t *sem);
    int sem_trywait(sem_t *sem);
    int sem_timedwait(sem_t *sem, const struct timespec *abs_timeout);
    
    
    • sem:信号量指针

    • 返回值:成功0;失败:

      • EINTR :阻塞的过程种被信号终止了。
      • EINVAL:不是一个有效的信号量

      sem_trywait():不阻塞等待。

      • EAGAIN: 信号量的value值为0。

      sem_timedwait():

      • EINVAL: Tabs_timeout.tv_nsecs 小于0,或者大于等于1000毫秒。
      • ETIMEDOUT: 超时了。

    5,把信号量的value值+1。

    #include <semaphore.h>
    int sem_post(sem_t *sem);
    
    • 返回值:成功0;失败:
      • EINVAL:不是一个有效的信号量
      • EOVERFLOW:超过了信号量的value。

    用下面5个程序观察有名字信号量的特性。

    semcreate.c

    #include <unistd.h>
    #include <fcntl.h>          
    #include <sys/stat.h>       
    #include <semaphore.h>
    #include <stdlib.h>
    #include <stdio.h>
    
    int main(int argc, char** argv){
      int c, flags;
      unsigned int val = 1;
      sem_t* sem;
    
      flags = O_RDWR | O_CREAT;
    
      while((c = getopt(argc, argv, "ei:")) != -1){
        switch(c){
        case 'e':
          flags |= O_EXCL;
          break;
        case 'i':
          val = atoi(optarg);
          break;
        }
      }
    
      if(optind != argc - 1){
        printf("usage error
    ");
        return -1;
      }
    
      sem = sem_open(argv[optind], flags, 0664, val);
      if(sem == SEM_FAILED){
        perror("sem");
        return -1;
      }
      sem_close(sem);
      exit(0);
    }
    
    

    semunlink.c

    #include <semaphore.h>
    #include <stdlib.h>
    #include <stdio.h>
    
    int main(int argc, char** argv){
      if(argc != 2){
        printf("usage err
    ");
        exit(1);
      }
    
      if(sem_unlink(argv[1]) == -1){
        perror("sem_unlink");
      }
    }
    
    

    semgetvalue.c

    #include <semaphore.h>
    #include <stdlib.h>
    #include <stdio.h>
    
    int main(int argc, char** argv){
      sem_t* sem;
      int val;
    
      if(argc != 2){
        printf("usage error
    ");
        exit(1);
      }
    
      sem = sem_open(argv[1], 0);
      sem_getvalue(sem, &val);
      printf("value = %d
    ", val);
      exit(0);
    }
    
    

    semwait.c

    #include <semaphore.h>
    #include <stdlib.h>
    #include <stdio.h>
    #include <unistd.h>
    #include <sys/types.h>
    
    int main(int argc, char** argv){
      sem_t* sem;
      int val;
    
      if(argc != 2){
        printf("usage error
    ");
        exit(1);
      }
    
      sem = sem_open(argv[1], 0);
      sem_wait(sem);
      sem_getvalue(sem, &val);
      printf("pid %ld has semaphore, value = %d
    ", (long) getpid(), val);
    
      pause();
      exit(0);
    }
    
    

    sempost.c

    #include <semaphore.h>
    #include <stdlib.h>
    #include <stdio.h>
    #include <unistd.h>
    #include <sys/types.h>
    
    int main(int argc, char** argv){
      sem_t* sem;
      int val;
    
      if(argc != 2){
        printf("usage error
    ");
        exit(1);
      }
    
      sem = sem_open(argv[1], 0);
      sem_post(sem);
      sem_getvalue(sem, &val);
      printf("value = %d
    ", val);
      
      exit(0);
    }
    
    

    步骤1:创建有名字信号量。发现在/dev/shm/下生成了sem.test1,mode是0664,但是umask是0022,所以sem.test1mode是0644

    ubuntu$ ./semcreate test1
    ubuntu$ ls -l /dev/shm/
    total 4
    -rw-r--r-- 1 ys ys 32 6月  21 16:25 sem.test1
    

    步骤2:查看有名字信号量的value值

    ubuntu$ ./semgetvalue test1
    value = 1
    

    步骤3:等待信号量,发现执行完sem_wait函数后,信号量的value值变更为0了。

    ubuntu$ ./semwait test1
    pid 2995 has semaphore, value = 0
    ^C
    ubuntu$
    

    步骤4:查看有名字信号量的value值,信号量的value值还是0。

    ubuntu$ ./semgetvalue test1
    value = 0
    

    步骤5:等待信号量2次,由于执行前value值已经是0了,按理来说执行了2次sem_wait后,应该变成-2,但是发现还行0。不要惊慌,内核是记住了value的值为-2的。

    ubuntu$ ./semwait test1 &
    [8] 3000
    ubuntu$ ./semgetvalue test1
    value = 0
    ubuntu$ ./semwait test1 &
    [9] 3002
    ubuntu$ ./semgetvalue test1
    value = 0
    

    步骤6:把信号量的value值+1

    ubuntu$ ./sempost test1
    pid 3000 has semaphore, value = 0 //来之第一个sem_wait程序的输出
    value = 0
    

    步骤7:把信号量的value值+1

    ubuntu$ ./sempost test1
    pid 3002 has semaphore, value = 0  //来之第二个sem_wait程序的输出
    value = 0
    

    步骤8:把信号量的value值+1,发现内核正确的记录的value的值。

    ubuntu$ ./sempost test1
    value = 1
    

    用信号量实现生产者和消费者

    #include <pthread.h>
    #include <stdlib.h>
    #include <stdio.h>
    #include <fcntl.h>       
    #include <sys/stat.h>    
    #include <semaphore.h>
    
    #define NBUFF 10
    #define SEM_MUTEX   "mutex"
    #define SEM_NEMPTY  "nempty"
    #define SEM_NSTORED "nstored"
    
    int nitems;
    struct {
      int buff[NBUFF];
      sem_t *mutex, *nempty, *nstored;
    } shared;
    
    void* produce(void *args){
      int i;
      for(i = 0; i < nitems; ++i){
        sem_wait(shared.nempty);
        sem_wait(shared.mutex);
        shared.buff[i % NBUFF] = i;
        sem_post(shared.mutex);
        sem_post(shared.nstored);
      }
    
      return NULL;
    }
    
    void* consume(void* args){
      int i;
      for(i = 0; i < nitems; ++i){
        sem_wait(shared.nstored);
        sem_wait(shared.mutex);
        shared.buff[i % NBUFF] = i;
        sem_post(shared.mutex);
        sem_post(shared.nempty);
      }
    
      return NULL;
    }
    
    int main(int argc, char** argv){
    
      pthread_t tid_produce, tid_consume;
    
      if(argc != 2){
        printf("usage error
    ");
        exit(1);
      }
    
      nitems = atoi(argv[1]);
    
      //create 3 semaphore
      shared.mutex = sem_open(SEM_MUTEX, O_CREAT | O_EXCL, 0664, 1);
      shared.nempty = sem_open(SEM_NEMPTY, O_CREAT | O_EXCL, 0664, NBUFF);
      shared.nstored = sem_open(SEM_NSTORED, O_CREAT | O_EXCL, 0664, 0);
    
      //create produce and consume thread
      pthread_create(&tid_produce, NULL, produce, NULL);
      pthread_create(&tid_consume, NULL, consume, NULL);
    
      //wait for 2 thread
      pthread_join(tid_produce, NULL);
      pthread_join(tid_consume, NULL);
    
      sem_unlink(SEM_MUTEX);
      sem_unlink(SEM_NEMPTY);
      sem_unlink(SEM_NSTORED);
      exit(0);
    }
    
    

    c/c++ 学习互助QQ群:877684253

    本人微信:xiaoshitou5854

  • 相关阅读:
    1104 Sum of Number Segments (20 分)(数学问题)
    1092 To Buy or Not to Buy (20 分)(hash散列)
    1082 Read Number in Chinese (25 分)(字符串处理)【背】
    1105 Spiral Matrix (25 分)(模拟)
    初识网络安全及搭建网站(内网)
    HTML5开发者需要了解的技巧和工具汇总(转)
    native+web开发模式之web前端经验分享
    移动平台3G手机网站前端开发布局技巧汇总(转)
    Asp.net 中图片存储数据库以及页面读取显示通用方法详解附源码下载
    使用H3Viewer来查看VS2010的帮助文档
  • 原文地址:https://www.cnblogs.com/xiaoshiwang/p/11065569.html
Copyright © 2011-2022 走看看