欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

Linux 信号量之Posix有名字的信号量

程序员文章站 2024-01-20 19:42:22
信号量(semaphore),也和互斥锁一样提供了线程间或者进程间的同步功能。 信号量有三种: Posix有名字的信号量 Posix基于内存的信号量 System V信号量 信号量比互斥锁高级,互斥锁只允许一个线程访问临界区,信号量可以多个,可以把信号量看作成互斥锁的升级版,但是如果能用互斥锁解决, ......

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

信号量有三种:

  • posix有名字的信号量
  • posix基于内存的信号量
  • system v信号量

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

这篇文章只介绍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\n");
    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\n");
    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\n");
    exit(1);
  }

  sem = sem_open(argv[1], 0);
  sem_getvalue(sem, &val);
  printf("value = %d\n", 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\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);
}

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\n");
    exit(1);
  }

  sem = sem_open(argv[1], 0);
  sem_post(sem);
  sem_getvalue(sem, &val);
  printf("value = %d\n", 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

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

Linux 信号量之Posix有名字的信号量

本人微信:xiaoshitou5854