insularisland 2019-11-05
在unix环境高级编程中提到了两种信号量,两种信号量作用和应用场景基本相同,但是posix信号量更常用,跨平台能力强,性能也更优越,以下主要说posix信号量。
简介见《unix环境高级编程》第15.10小节。
信号量和互斥锁(mutex)的区别:互斥锁只允许一个线程进入临界区,而信号量允许多个线程同时进入临界区。
POSIX信号量有两种形式:命名的和未命名的。它们的差异在于创建和销毁的形式上,但其他工作一样。未命名信号量只存在于内存中,并要求能使用信号量的进程必须可以访问内存。这意味着他们只能应用在同一进程的线程,或者不同进程中已经映射相同内存内容到它们的地址空间中的线程。相反,命名信号量可以通过名字访问,因此可以被任何已知它们名字的进程中的线程使用。
程序退出时,未命名信号量创建在进程空间中,随进程消亡,命名信号量需要手动清理,可以使用信号处理函数处理异常退出情况。
主要函数:
#include <semaphore.h> //创建一个新的命名信号量或者使用一个现有信号量 sem_t *sem_open(const char *name, int oflag, .../* mode_t mode, unsigned int value*/); //释放任何信号量相关的资源 int sem_close(sem_t *sem); //销毁一个命名信号量 int sem_unlink(const char *name); //初始化未命名的信号量 int sem_init(sem_t *sem, int pshared, unsigned int value); //销毁未命名的信号量,只有用sem_init初始化的信号量才能用sem_destroy销毁。 int sem_destroy(sem_t *sem); //信号量的减1操作(p操作) int sem_trywait(sem_t *sem);//非阻塞,p失败,返回-1 int sem_wait(sem_t *sem);//阻塞 int sem_timedwait(sem_t *restrict sem, //p失败,阻塞一定时间 const struct timespec * restrict tsptr); //信号量的加1操作(v操作) int sem_post(sem_t *sem); //检索信号量的值 int sem_getvalue(sem_t *restrict sem, int *restrict valp);
“生产者——消费者”问题是Linux多线程编程中的经典问题,主要是利用信号量处理线程间的同步和互斥问题。(以下使用posix信号量)
“生产者——消费者”问题描述如下: 有一个有限缓冲区(这里用有名管道实现 FIFO 式缓冲区)和两个线程:生产者和消费者,它们分别不停地把产品放入缓冲区和从中拿走产品。一个生产者在缓冲区满的时候必须等待,一个消费者在缓冲区空的时候也不得不等待。另外,因为缓冲区是临界资源,所以生产者和消费者之间必须互斥进行。它们之间的关系如下:
这里要求使用有名管道来模拟有限缓冲区,并用信号量来解决“生产者——消费者”问题中的同步和互斥问题。
这里使用3个信号量,其中两个信号量 avail 和 full 分别用于解决生产者和消费者线程之间的互斥问题。其中avail 表示缓冲区的空单元数,初始值为N;full 表示缓冲区非空单元数,初始值为 0 ; mutex 是互斥信号量 ,初始值为 1(当然也可以用互斥锁来实现互斥操作)。
编写代码
本实验的代码中缓冲区拥有3个单元,每个单元为5个字节。为了尽量体现每个信号量的意义,在程序中生产过程和消费过程是随机(采取0~5s 的随机事件间隔)进行的,而且生产者的速度比消费者的速度平均快两倍左右。生产者一次生产一个单元的产品(放入hello字符串),消费者一次消费一个单元的产品。
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <pthread.h> #include <errno.h> #include <unistd.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <semaphore.h> #define MYFIFO "myfifo" #define BUFFER_SIZE 3 #define UNIT_SIZE 5 #define RUN_TIME 30 #define DELAY_TIME_LEVELS 5 void *producer(void *arg);//生产者 void *customer(void *arg);//消费者 int fd; time_t end_time; sem_t mutex, full, avail; int main(int argc, const char *argv[]) { int ret; pthread_t thrd_prd_id,thrd_cst_id; srand(time(NULL)); end_time = time(NULL) + RUN_TIME; //创建管道 if ((mkfifo(MYFIFO, 0644) < 0) && (errno != EEXIST)) { perror("mkfifo error!"); exit(-1); } //打开管道 fd = open(MYFIFO, O_RDWR); if (fd == -1) { perror("open fifo error"); exit(-1); } //初始化信号量 ret = sem_init(&mutex, 0, 1); ret += sem_init(&avail, 0, BUFFER_SIZE); ret += sem_init(&full, 0, 0); if (ret != 0) { perror("sem_init error"); exit(-1); } //创建两个线程 ret = pthread_create(&thrd_prd_id, NULL, producer, NULL); if (ret != 0) { perror("producer pthread_create error"); exit(-1); } ret = pthread_create(&thrd_cst_id, NULL, customer, NULL); if (ret != 0) { perror("customer pthread_create error"); exit(-1); } pthread_join(thrd_prd_id, NULL); pthread_join(thrd_cst_id, NULL); sem_destroy(&mutex); sem_destroy(&avail); sem_destroy(&full); close(fd); unlink(MYFIFO); return 0; } void *producer(void *arg) { int real_write; int delay_time; while (time(NULL) < end_time) { delay_time = rand()%DELAY_TIME_LEVELS; sleep(delay_time); //p操作 sem_wait(&avail); sem_wait(&mutex); printf("\nproducer have delayed %d seconds\n", delay_time); //生产者写入数据 执行的操作 if (-1 == (real_write = write(fd, "hello", UNIT_SIZE))) { if (errno == EAGAIN) { printf("The buffer is full, please wait for reading!\n"); } } else { printf("producer writes %d bytes to the FIFO\n", real_write); printf("Now,the buffer left %d spaces!\n", avail); } //v操作 sem_post(&full); sem_post(&mutex); } pthread_exit(NULL); } void *customer(void *arg) { unsigned char read_buffer[UNIT_SIZE]; int real_read; int delay_time; while (time(NULL) < end_time) { delay_time = rand()%DELAY_TIME_LEVELS; sleep(delay_time); sem_wait(&full); sem_wait(&mutex); memset(read_buffer, 0, UNIT_SIZE); printf("\nCustomer have delayed %d seconds\n", delay_time); //消费 操作 if (-1 == (real_read = read(fd, read_buffer, UNIT_SIZE))) { if (errno == EAGAIN) { printf("The buffer is empty, please wait for writing!\n"); } } else { printf("customer reads %d bytes from the FIFO\n", real_read); } sem_post(&avail); sem_post(&mutex); } pthread_exit(NULL); }
执行结果如下:
root@jonathan-pc:~/test# gcc posixsem.c -lpthread -o posixsme root@jonathan-pc:~/test# ./posixsme producer have delayed 3 seconds producer writes 5 bytes to the FIFO Now,the buffer left 0 spaces! Customer have delayed 2 seconds customer reads 5 bytes from the FIFO producer have delayed 1 seconds producer writes 5 bytes to the FIFO Now,the buffer left 0 spaces! Customer have delayed 2 seconds customer reads 5 bytes from the FIFO producer have delayed 4 seconds producer writes 5 bytes to the FIFO Now,the buffer left 0 spaces! producer have delayed 0 seconds producer writes 5 bytes to the FIFO Now,the buffer left 0 spaces! producer have delayed 0 seconds producer writes 5 bytes to the FIFO Now,the buffer left 0 spaces! Customer have delayed 2 seconds customer reads 5 bytes from the FIFO Customer have delayed 0 seconds customer reads 5 bytes from the FIFO producer have delayed 0 seconds producer writes 5 bytes to the FIFO Now,the buffer left 0 spaces! producer have delayed 0 seconds producer writes 5 bytes to the FIFO Now,the buffer left 0 spaces! Customer have delayed 1 seconds customer reads 5 bytes from the FIFO Customer have delayed 1 seconds customer reads 5 bytes from the FIFO Customer have delayed 0 seconds customer reads 5 bytes from the FIFO producer have delayed 4 seconds producer writes 5 bytes to the FIFO Now,the buffer left 0 spaces! Customer have delayed 1 seconds customer reads 5 bytes from the FIFO producer have delayed 2 seconds producer writes 5 bytes to the FIFO Now,the buffer left 0 spaces! Customer have delayed 1 seconds customer reads 5 bytes from the FIFO producer have delayed 2 seconds producer writes 5 bytes to the FIFO Now,the buffer left 0 spaces! producer have delayed 1 seconds producer writes 5 bytes to the FIFO Now,the buffer left 0 spaces! Customer have delayed 4 seconds customer reads 5 bytes from the FIFO producer have delayed 1 seconds producer writes 5 bytes to the FIFO Now,the buffer left 0 spaces! Customer have delayed 1 seconds customer reads 5 bytes from the FIFO Customer have delayed 3 seconds customer reads 5 bytes from the FIFO producer have delayed 4 seconds producer writes 5 bytes to the FIFO Now,the buffer left 0 spaces! Customer have delayed 2 seconds customer reads 5 bytes from the FIFO producer have delayed 4 seconds producer writes 5 bytes to the FIFO Now,the buffer left 0 spaces! producer have delayed 0 seconds producer writes 5 bytes to the FIFO Now,the buffer left 0 spaces! Customer have delayed 0 seconds customer reads 5 bytes from the FIFO producer have delayed 3 seconds producer writes 5 bytes to the FIFO Now,the buffer left 0 spaces! Customer have delayed 4 seconds customer reads 5 bytes from the FIFO producer have delayed 4 seconds producer writes 5 bytes to the FIFO Now,the buffer left 0 spaces!