linux进程间通信-信号量

2021-03-14

定义

信号量本质上是一个计数器(不设置全局变量是因为进程间是相互独立的,而这不一定能看到,看到也不能保证++引用计数为原子操作),用于多进程对共享数据对象的读取,它和管道有所不同,它不以传送数据为主要目的,它主要是用来保护共享资源(信号量也属于临界资源),使得资源在一个时刻只有一个进程独享。

信号量的工作原理

由于信号量只能进行两种操作等待和发送信号,即P(sv)V(sv)edg,他们的行为是这样的:

  1. P(sv):如果sv的值大于零,就给它减1;如果它的值为零,就挂起该进程的执行
  2. V(sv):如果有其他进程因等待sv而被挂起,就让它恢复运行,如果没有进程因等待sv而挂起,就给它加1.

在信号量进行PV操作时都为原子操作(因为它需要保护临界资源)

注:原子操作:单指令的操作称为原子的,单条指令的执行是不会被打断的

二元信号量

二元信号量(Binary Semaphore)是最简单的一种锁(互斥锁),它只用两种状态:占用与非占用。所以它的引用计数为1。

进程如何获得共享资源

  1. 测试控制该资源的信号量
  2. 信号量的值为正,进程获得该资源的使用权,进程将信号量减1,表示它使用了一个资源单位
  3. 若此时信号量的值为0,则进程进入挂起状态(进程状态改变),直到信号量的值大于0,若进程被唤醒则返回至第一步。

注:信号量通过同步与互斥保证访问资源的一致性。

与信号量相关的函数

通常使用posix信号量函数比较多

兼容系统v的信号量的函数

semget

创建信号量

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>

int semget(key_t key, int nsems, int semflg);

#define key_t int

参数:

semctl

删除和初始化信号量

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>

int semctl(int semid, int semnum, int cmd, ...);

参数:

semop

改变信号量大的值

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>

int semop(int semid, struct sembuf *sops, size_t nsops);

int semtimedop(int semid, struct sembuf *sops, size_t nsops,
               const struct timespec *timeout);

参数:

通常设置为SEM_UNDO,使操作系统跟踪信号量, 并在进程没有释放该信号量而终止时,操作系统释放信号量 ,例如在二元信号量中,你不释放该信号量 而异常退出,就会导致别的进程一直申请不到信号量,而一直处于挂起状态。

posix信号量函数

sem_init

该函数用于创建信号量,其原型如下

#include <semaphore.h>

int sem_init(sem_t *sem, int pshared, unsigned int value);

Link with -pthread.

该函数初始化由sem指向的信号对象,设置它的共享选项,并给它一个初始的整数值。

返回值

sem_post

释放信号量,让信号量的值加1。相当于V操作。

#include <semaphore.h>

int sem_post(sem_t *sem);

Link with -pthread.

返回值:

sem_wait

等待信号量,如果信号量的值大于0,将信号量的值减1,立即返回。如果信号量的值为0,则线程阻塞,相当于P操作。

int sem_wait(sem_t *sem);  

成功返回0,失败返回-1。

sem_destroy

该函数用于对用完的信号量的清理。它的原型如下:

int sem_destroy(sem_t *sem); 

成功时返回0,失败时返回-1.

例子

#include <stdio.h>
#include <stdlib.h>
#include <semaphore.h>
#include <errno.h>
#include <pthread.h>
#include <unistd.h>

#define total 20

sem_t remain, apple, pear, mutex;
static unsigned int vremain = 20, vapple = 0, vpear = 0;
void *father(void *);
void *mather(void *);
void *son(void *);
void *daughter(void *);
void print_sem();

int main()
{  
    pthread_t fa, ma, so, da;  
    sem_init(&remain, 0, total);//总数初始化为20 
    sem_init(&apple, 0, 0);//盆子中苹果数, 开始为0  
    sem_init(&pear, 0, 0);//盆子中梨子数, 开始为0   
    sem_init(&mutex, 0, 1);//互斥锁, 初始为1 
    pthread_create(&fa, NULL, &father, NULL);  
    pthread_create(&ma, NULL, &mather, NULL);  
    pthread_create(&so, NULL, &son, NULL); 
    pthread_create(&da, NULL, &daughter, NULL);    
    for(;;);
}
void *father(void *arg)
{  
    while(1)
    {      
        sem_wait(&remain);     
        sem_wait(&mutex);      
        printf("父亲: 放苹果之前, 剩余空间=%u, 苹果数=%u\n", vremain--, vapple++);
        printf("父亲: 放苹果之后, 剩余空间=%u, 苹果数=%u\n", vremain, vapple);
        sem_post(&mutex);      
        sem_post(&apple);  
        sleep(1);  
    }
}
void *mather(void *arg)
{  
    while(1)
    {      
        sem_wait(&remain);     
        sem_wait(&mutex);      
        printf("母亲: 放梨子之前, 剩余空间=%u, 梨子数=%u\n", vremain--, vpear++);
        printf("母亲: 放梨子之后, 剩余空间=%u, 梨子数=%u\n", vremain, vpear);
        sem_post(&mutex);  
        sem_post(&pear);   
        sleep(2);  
    }
}
void *son(void *arg)
{  
    while(1)
    {      
        sem_wait(&pear);   
        sem_wait(&mutex);   
        printf("儿子: 吃梨子之前, 剩余空间=%u, 梨子数=%u\n", vremain++, vpear--);
        printf("儿子: 吃梨子之后, 剩余空间=%u, 梨子数=%u\n", vremain, vpear);
        sem_post(&mutex);  
        sem_post(&remain);     
        sleep(3);
    }
}
void *daughter(void *arg)
{  
    while(1)
    {  
        sem_wait(&apple);  
        sem_wait(&mutex);
        printf("女儿: 吃苹果之前, 剩余空间=%u, 苹果数=%u\n", vremain++, vapple--);
        printf("女儿: 吃苹果之前, 剩余空间=%u, 苹果数=%u\n", vremain, vapple);   
        sem_post(&mutex);  
        sem_post(&remain); 
        sleep(3);  
    }
}
void print_sem()
{  
    int val1, val2, val3;
    sem_getvalue(&remain, &val1);  
    sem_getvalue(&apple, &val2);   
    sem_getvalue(&pear, &val3);
    printf("Semaphore: remain:%d, apple:%d, pear:%d\n", val1, val2, val3);
}

运行结果

母亲: 放梨子之前, 剩余空间=20, 梨子数=0
母亲: 放梨子之后, 剩余空间=19, 梨子数=1
儿子: 吃梨子之前, 剩余空间=19, 梨子数=1
儿子: 吃梨子之后, 剩余空间=20, 梨子数=0
父亲: 放苹果之前, 剩余空间=20, 苹果数=0
父亲: 放苹果之后, 剩余空间=19, 苹果数=1
女儿: 吃苹果之前, 剩余空间=19, 苹果数=1
女儿: 吃苹果之前, 剩余空间=20, 苹果数=0
父亲: 放苹果之前, 剩余空间=20, 苹果数=0
父亲: 放苹果之后, 剩余空间=19, 苹果数=1
母亲: 放梨子之前, 剩余空间=19, 梨子数=0
母亲: 放梨子之后, 剩余空间=18, 梨子数=1
父亲: 放苹果之前, 剩余空间=18, 苹果数=1
父亲: 放苹果之后, 剩余空间=17, 苹果数=2
儿子: 吃梨子之前, 剩余空间=17, 梨子数=1
儿子: 吃梨子之后, 剩余空间=18, 梨子数=0
女儿: 吃苹果之前, 剩余空间=18, 苹果数=2
女儿: 吃苹果之前, 剩余空间=19, 苹果数=1
父亲: 放苹果之前, 剩余空间=19, 苹果数=1
父亲: 放苹果之后, 剩余空间=18, 苹果数=2
母亲: 放梨子之前, 剩余空间=18, 梨子数=0
母亲: 放梨子之后, 剩余空间=17, 梨子数=1
父亲: 放苹果之前, 剩余空间=17, 苹果数=2
父亲: 放苹果之后, 剩余空间=16, 苹果数=3
父亲: 放苹果之前, 剩余空间=16, 苹果数=3
父亲: 放苹果之后, 剩余空间=15, 苹果数=4
儿子: 吃梨子之前, 剩余空间=15, 梨子数=1
儿子: 吃梨子之后, 剩余空间=16, 梨子数=0
女儿: 吃苹果之前, 剩余空间=16, 苹果数=4
女儿: 吃苹果之前, 剩余空间=17, 苹果数=3
母亲: 放梨子之前, 剩余空间=17, 梨子数=0
母亲: 放梨子之后, 剩余空间=16, 梨子数=1
父亲: 放苹果之前, 剩余空间=16, 苹果数=3
父亲: 放苹果之后, 剩余空间=15, 苹果数=4
父亲: 放苹果之前, 剩余空间=15, 苹果数=4
父亲: 放苹果之后, 剩余空间=14, 苹果数=5

原文:https://www.cnblogs.com/wuyepeng/p/9748552.html

words: 3269 tags: linux进程间通信