【Linux系統編程】POSIX無名信號量

00. 目錄

01. 信號量概述

信號量廣泛用於進程或線程間的同步和互斥,信號量本質上是一個非負的整數計數器,它被用來控制對公共資源的訪問。

編程時可根據操作信號量值的結果判斷是否對公共資源具有訪問的權限,當信號量值大於 0 時,則可以訪問,否則將阻塞。PV 原語是對信號量的操作,一次 P 操作使信號量減1,一次 V 操作使信號量加1。

信號量主要用於進程或線程間的同步和互斥這兩種典型情況。

信號量用於互斥:
在這裏插入圖片描述

信號量用於同步:
在這裏插入圖片描述

在 POSIX 標準中,信號量分兩種,一種是無名信號量,一種是有名信號量。無名信號量一般用於線程間同步或互斥,而有名信號量一般用於進程間同步或互斥。它們的區別和管道及命名管道的區別類似,無名信號量則直接保存在內存中,而有名信號量要求創建一個文件。

注意:

編譯信號量操作函數時,需要加上參數**-lpthread**。

信號量數據類型爲:sem_t

02. 相關函數

2.1 初始化信號量

#include <semaphore.h>

int sem_init(sem_t *sem, int pshared, unsigned int value);
功能:
	創建一個信號量並初始化它的值。一個無名信號量在被使用前必須先初始化。
參數:
	sem:信號量的地址。
	pshared:等於 0,信號量在線程間共享(常用);不等於0,信號量在進程間共享。
	value:信號量的初始值。
返回值:
	成功:0
	失敗: - 1

2.2 銷燬信號量

#include <semaphore.h>

int sem_destroy(sem_t *sem);
功能:
	刪除 sem 標識的信號量。
參數:
	sem:信號量地址。
返回值:
	成功:0
	失敗: - 1

2.3 信號量P操作(減1)

#include <semaphore.h>

int sem_wait(sem_t *sem);
功能:
	將信號量的值減 1。操作前,先檢查信號量(sem)的值是否爲 0,若信號量爲 0,
    此函數會阻塞,直到信號量大於 0 時才進行減 1 操作。
參數:
	sem:信號量的地址。
返回值:
	成功:0
	失敗: - 1

int sem_trywait(sem_t *sem);
	以非阻塞的方式來對信號量進行減 1 操作。
	若操作前,信號量的值等於 0,則對信號量的操作失敗,函數立即返回。

int sem_timedwait(sem_t *sem, const struct timespec *abs_timeout);
	限時嘗試將信號量的值減 1
	abs_timeout:絕對時間

abs_timeout補充說明

struct timespec {
	time_t tv_sec;		/* seconds */ // 秒
	long   tv_nsec;	/* nanosecondes*/ // 納秒
}

time_t cur = time(NULL); 		//獲取當前時間。
struct timespec t;				//定義timespec 結構體變量t
t.tv_sec = cur + 1;				// 定時1秒
sem_timedwait(&cond, &t);

2.4 信號量V操作(加1)

#include <semaphore.h>

int sem_post(sem_t *sem);
功能:
	將信號量的值加 1 併發出信號喚醒等待線程(sem_wait())。
參數:
	sem:信號量的地址。
返回值:
	成功:0
	失敗:-1

2.5 獲取信號量的值

#include <semaphore.h>

int sem_getvalue(sem_t *sem, int *sval);
功能:
	獲取 sem 標識的信號量的值,保存在 sval 中。
參數:
	sem:信號量地址。
	sval:保存信號量值的地址。
返回值:
	成功:0
	失敗:-1

03. 信號量示例

信號量用於互斥實例

#include <stdio.h>
#include <pthread.h>
#include <unistd.h>
#include <semaphore.h>
 
sem_t sem; //信號量
 
void printer(char *str)
{
	sem_wait(&sem);//減一
	while(*str)
	{
		putchar(*str);	
		fflush(stdout);
		str++;
		sleep(1);
	}
	printf("\n");
	
	sem_post(&sem);//加一
}
 
void *thread_fun1(void *arg)
{
	char *str1 = "hello";
	printer(str1);
}
 
void *thread_fun2(void *arg)
{
	char *str2 = "world";
	printer(str2);
}
 
int main(void)
{
	pthread_t tid1, tid2;
	
	sem_init(&sem, 0, 1); //初始化信號量,初始值爲 1
	
	//創建 2 個線程
	pthread_create(&tid1, NULL, thread_fun1, NULL);
	pthread_create(&tid2, NULL, thread_fun2, NULL);
	
	//等待線程結束,回收其資源
	pthread_join(tid1, NULL);
	pthread_join(tid2, NULL); 
	
	sem_destroy(&sem); //銷燬信號量
	
	return 0;
}

執行結果:

deng@itcast:/mnt/hgfs/LinuxHome/code.bak2$ gcc 1.c -pthread 
deng@itcast:/mnt/hgfs/LinuxHome/code.bak2$ ./a.out  
hello
world
deng@itcast:/mnt/hgfs/LinuxHome/code.bak2$ 

信號量用於同步

#include <stdio.h>
#include <unistd.h>
#include <pthread.h>
#include <semaphore.h>
 
sem_t sem_g,sem_p;   //定義兩個信號量
char ch = 'a';
 
void *pthread_g(void *arg)  //此線程改變字符ch的值
{
	while(1)
	{
		sem_wait(&sem_g);
		ch++;
		sleep(1);
		sem_post(&sem_p);
	}
}
 
void *pthread_p(void *arg)  //此線程打印ch的值
{
	while(1)
	{
		sem_wait(&sem_p);
		printf("%c",ch);
		fflush(stdout);
		sem_post(&sem_g);
	}
}
 
int main(int argc, char *argv[])
{
	pthread_t tid1,tid2;
	sem_init(&sem_g, 0, 0);   //初始化信號量
	sem_init(&sem_p, 0, 1);
	
	pthread_create(&tid1, NULL, pthread_g, NULL);
	pthread_create(&tid2, NULL, pthread_p, NULL);
	
	pthread_join(tid1, NULL);
	pthread_join(tid2, NULL);
	
	return 0;
}

測試結果:

deng@itcast:/mnt/hgfs/LinuxHome/code.bak2$ gcc 1.c -pthread 
deng@itcast:/mnt/hgfs/LinuxHome/code.bak2$ ./a.out  
abcdefghijklmnopqr^C
deng@itcast:/mnt/hgfs/LinuxHome/code.bak2$ 

04. 附錄

發佈了640 篇原創文章 · 獲贊 2484 · 訪問量 84萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章