zoukankan      html  css  js  c++  java
  • linux POSIX 信号量介绍

        信号量
    一.什么是信号量
    信号量的使用主要是用来保护共享资源,使得资源在一个时刻只有一个进程(线程)使用。
    多线程可以同时运行多个线程函数完成功能,但是对于共享数据如果不加以锁定,随意改变共享数据的值会发生期望之外的结果!

    本文主要介绍Linux下的 POSIX信号量:有名信号量和无名信号量
    有名信号量,其值保存在文件中, 所以它可以用于线程也可以用于进程间的同步;
    无名信号量,其值保存在内存中,故只用于线程的同步,多用共享内存。

    POSIX信号量包含一个非负整型变量,并且带有两个原子操作wait 和signal。wait 还可以被称为down、P 或lock,signal 还可以被称为up、V、unlock 或post。在Uinx的API中用的是wait和post。

    如果信号量的非负整形变量S大于零,wait就将其减1,如果S 等于0,wait 就将调用线程阻塞。对于post 操作,如果有线程在信号量上阻塞(此时S 等于0),signal就会解除对某个等待线程的阻塞,使其从wait 中返回,如果没有线程阻塞在信号量上,post 就将S加1。

    简单来说即:当S大于0时,线程正常运行不会阻塞;当S=0时,线程阻塞直到有post操作将其+1。假设S值设为1,信号量可实现互斥的功能。

    信号量的一般操作是 创建-->PV操作-->删除

    二. 无名信号量
    头文件: <semaphore.h>
    int sem_init(sem_t *sem, int pshared,unsigned value);
    功能:初始化指定的信号量
    返回值:成功 0 ; 错误 错误号。
    参数说明:sem:信号量变量名;参数value为信号量的初始值。
    参数pshared用于说明信号量的共享范围,如果pshared为0,那么该信号量只能由初始化这个信号量的进程中的线程使用,如果pshared非零,任何可以访问到这个信号量的进程都可以使用这个信号量。
    必须保证只调用sem_init()进行初始化一次,对于一个已初始化过的信号量调用sem_init()的行为是未定义的

    int sem_destroy(sem_t *sem);
    功能:销毁一个指定的信号量
    返回值:成功 0 ; 错误 错误号。
    参数说明:sem:信号量变量名(sem_init内值)
    摧毁一个有线程阻塞在其上的信号量的行为是未定义的。

    P操作:
    int sem_trywait(sem_t *sem);
    int sem_wait(sem_t *sem);
    int sem_timedwait(sem_t * sem, const struct timespec time);

    减1信号量的值。信号量值为0时,sem_wait会阻塞,直到成功使信号量减1或者被信号中断时才返回;
    sem_trywait为非阻塞版本,当信号量为0时,该函数返回-1并且将errno置为EAGAIN
    sem_timedwait带有超时功能,超时到期并且信号量计数没能减1,sem_timedwait将返回-1且将errno设置为ETIMEDOUT。(毕竟一直阻塞也不是办法。。)

    V操作:
    int sem_post(sem_t *sem);
    信号量加1,若此时有sem_wait正在阻塞则唤醒执行。

    取值操作:
    int sem_t getvalue(sem_t sem, int *val);
    获取信号量的值,一般只用来调试,打印val查看。

    三. 有名信号量
    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参数可以为:0,O_CREAT,O_EXCL。如果为0表示打开一个已存在的信号量,如果为O_CREAT,表示如果信号量不存在就创建一个信号量,如果存在则打开被返回。此时mode和value需要指定。如果为O_CREAT | O_EXCL,表示如果信号量已存在会返回错误。
    mode参数用于创建信号量时,表示信号量的权限位,和open函数一样包括:S_IRUSR,S_IWUSR,S_IRGRP,S_IWGRP,S_IROTH,S_IWOTH。

    int sem_close(sem_t *sem);
    功能:sem_close用于关闭打开的信号量。当一个进程终止时,内核对其上仍然打开的所有有名信号量自动执行这个操作。
    返回值:成功返回0,失败返回-1
    调用sem_close关闭信号量并没有把它从系统中删除它,POSIX有名信号量是随内核持续的。即使当前没有进程打开某个信号量它的值依然保持。直到内核重新自举或调用sem_unlink()删除该信号量。

    int sem_unlink(const char *name);
    功能:将有名信号量立刻从系统中删除,但信号量的销毁是在所有进程都关闭信号量的时候(只close是不够的!)
    返回值:成功返回0,失败返回-1

    有名信号量的PV操作与无名信号量类似。

    参考网上代码,自测了使用信号量同步线程的功能:

     1 sem_t sem_g, sem_p;
     2 int g_i_res=1;
     3 
     4 void *pid_g(void)
     5 {
     6     while(1)
     7     {
     8         sem_wait(&sem_g);
     9         g_i_res++;
    10         Sleep(1);
    11         if(g_i_res>50)
    12             system("pause");
    13         sem_post(&sem_p);
    14     }
    15 
    16 }
    17 
    18 void *pid_p(void)
    19 {
    20     while(1)
    21     {
    22         sem_wait(&sem_p);
    23         printf("%d ", g_i_res);
    24         fflush(stdout);
    25         sem_post(&sem_g);
    26 
    27     }
    28 
    29 }
    30 
    31 int main()
    32 {
    33 
    34     pthread_t tid1, tid2;
    35     sem_init(&sem_g, 0, 0);
    36     sem_init(&sem_p, 0, 1);
    37 
    38     pthread_create(&tid1, NULL, pid_g, NULL);
    39     pthread_create(&tid2, NULL, pid_p, NULL);
    40 
    41     pthread_join(tid1, NULL);
    42     pthread_join(tid2, NULL);
    43 
    44 
    45     return 0;
    46 }

    说明:头文件按需添加,一般有<pthread.h>  <semaphore.h>  ,由于虽然是gcc编译但在Windows环境,所以用了<windows.h>的Sleep()

    自己运行可以看到数字依次递增的打印过程,若不加system(pause)会无限刷屏的。。。

    遇到新内容再更新吧~

  • 相关阅读:
    C编译: 动态连接库 (.so文件)
    C调Lua
    gcc -l参数和-L参数
    Lua和C之间的交互
    c语言 struct 的初始化
    Ubuntu下,清屏等终端常用命令
    Luci流程分析(openwrt下)
    Ubuntu下轻松安装virtualbox
    luci范例
    luci框架-LUA的一个web框架使用
  • 原文地址:https://www.cnblogs.com/chenzhefan/p/8298951.html
Copyright © 2011-2022 走看看