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

  • 相关阅读:
    WordPress 常用的动作钩子
    wordpress的过滤器
    实战haslayout(实战篇)!
    wordpress函数描述之一——WordPress add_theme_support() 函数
    一些闲言碎语,好记星不如烂笔头(一)
    实战haslayout(理论篇)!
    WordPress的钩子函数之一——do_action()
    IE6变态bug总结非常好!没有错误
    Javascript参考博客
    SilverLight遍历父子控件的通用方法
  • 原文地址:https://www.cnblogs.com/xiaoshiwang/p/11065569.html
Copyright © 2011-2022 走看看