進程間通信——信號量
目錄
一、基本概念?
1.1 概念
1.2 基本操作
1.3?相關函數
1.3.1 semget創建/獲取
1.3.2 semop操作信號量
1.3.3 semctl初始化/刪除
二、代碼操作
2.1 不用PV的
2.2 用PV 的
2.2.1 a.c
2.2.2 b.c
2.2.3 sem.h
?2.2.4 sem.c
一、基本概念?
1.1 概念
信號量本質上是一個計數器,用于記錄可用資源的數量。它有兩種類型的操作:P 操作(也稱為 wait 操作)和 V 操作(也稱為 signal 操作)。P 操作會將信號量的值減 1,表示請求一個資源;V 操作會將信號量的值加 1,表示釋放一個資源。
當信號量的值為 0 時,表示沒有可用資源,此時進程在請求資源時會被阻塞,直到其他進程釋放資源。
臨界資源:同一時刻只允許一個進程訪問的資源
臨界區:訪問臨界資源的代碼段。這段代碼執行了就會訪問例如打印機之類的臨界資源
1.2 基本操作
初始化:在使用信號量之前,需要對其進行初始化,設置初始值。這個值通常表示系統中某種資源的初始數量。
P 操作:當一個進程需要訪問共享資源時,它首先執行 P 操作。如果信號量的值大于 0,那么 P 操作會成功執行,進程可以繼續執行并使用資源,同時信號量的值減 1。如果信號量的值為 0,那么進程會被阻塞,放入與該信號量相關的等待隊列中,直到其他進程執行 V 操作釋放資源。
V 操作:當進程使用完共享資源后,它執行 V 操作。V 操作會將信號量的值加 1,如果有其他進程在等待該資源(即信號量的等待隊列不為空),那么系統會從等待隊列中喚醒一個進程,讓它繼續執行 P 操作以獲取資源。
1.3?相關函數
1.3.1 semget創建/獲取
semget
?函數用于創建一個新的信號量集或獲取一個已存在的信號量集的標識符
#include <sys/sem.h>int semget(key_t key, int nsems, int semflg);
key
:一個鍵值,用于標識信號量集。可以使用?ftok
?函數生成一個唯一的鍵值,也可以使用特殊值?IPC_PRIVATE
?來創建一個私有的信號量集,該信號量集只能由創建它的進程及其子進程訪問。nsems
:指定信號量集中信號量的數量。如果是創建新的信號量集,必須指定該值;如果是獲取已存在的信號量集,該值通常為 0。semflg
:標志位,用于指定創建信號量集的模式和權限等。常見的標志有:
IPC_CREAT
:如果信號量集不存在,則創建它。IPC_EXCL
:與?IPC_CREAT
?一起使用,若信號量集已存在,則返回錯誤。- 權限標志,如?
0666
?表示所有用戶都有讀寫權限。- 成功時,返回一個非負整數,即信號量集的標識符(semid)。
- 失敗時,返回 -1,并設置?
errno
?來指示錯誤類型。
1.3.2 semop操作信號量
semop
?函數用于對信號量集中的一個或多個信號量進行操作,如 P 操作(申請資源)和 V 操作(釋放資源)
#include <sys/sem.h>int semop(int semid, struct sembuf *sops, unsigned nsops);
semid
:信號量集的標識符,由?semget
?函數返回。sops
:指向一個?struct sembuf
?結構體數組的指針,每個?struct sembuf
?結構體描述了對一個信號量的操作。struct sembuf
?結構體的定義如下:struct sembuf {unsigned short sem_num; // 信號量在信號量集中的索引(從 0 開始)short sem_op; // 操作類型,正數表示釋放資源(V 操作),負數表示申請資源(P 操作),0 表示等待信號量值為 0short sem_flg; // 標志位,常見的有 IPC_NOWAIT 表示非阻塞操作 };
nsops
:sops
?數組中元素的數量,即要執行的操作數量。
1.3.3 semctl初始化/刪除
semctl
?函數用于對信號量集進行控制操作,如初始化信號量的值、獲取信號量的狀態信息、刪除信號量集等。
#include <sys/sem.h>int semctl(int semid, int semnum, int cmd, ...);
semid
:信號量集的標識符,由?semget
?函數返回。semnum
:信號量在信號量集中的索引(從 0 開始)。如果?cmd
?不需要指定特定的信號量,則該值通常為 0。cmd
:要執行的控制命令,常見的命令有:
SETVAL
:設置指定信號量的值,需要傳遞一個?int
?類型的參數作為新值。GETVAL
:獲取指定信號量的值,返回值即為信號量的當前值。IPC_RMID
:刪除信號量集,不需要指定?semnum
,也不需要額外的參數。...
:可變參數,根據?cmd
?的不同而有所不同。例如,當?cmd
?為?SETVAL
?時,需要傳遞一個?int
?類型的參數作為新的信號量值。
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include "sem.h"int main()
{sem_init(); // 創建并初始化信號量for(int i=0; i<5; i++){sem_p();printf("A");fflush(stdout);int n = rand() % 3; // 隨機睡眠一段時間sleep(n);printf("A");fflush(stdout);sem_v();n = rand() % 3;sleep(n);}sleep(10);sem_destroy(); // 刪除信號量
}
二、代碼操作
2.1 不用PV的
a.c
#include <stdio.h>
#include <string.h>
#include <unistd.h>int main()
{for(int i=0;i<5;i++){printf("A");fflush(stdout);int n=rand()%3;sleep(n);printf("A");fflush(stdout);}
}
b.c
#include <stdio.h>
#include <string.h>
#include <unistd.h>int main()
{for(int i=0;i<5;i++){printf("B");fflush(stdout);int n=rand()%3;sleep(n);printf("B");fflush(stdout);}
}
?
/a &./b&
?:在后臺同時運行可執行文件?a
?和?b
?,[1] 10916
?和?[2] 10917
?分別是這兩個后臺進程的作業編號和進程號。
2.2 用PV 的
2.2.1 a.c
a.c使用信號量進行進程同步的C語言程序示例。
它演示了如何使用信號量來控制對共享資源的訪問,以防止數據競爭和條件競爭
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include "sem.h"int main()
{sem_init();//創建并初始化信號量//pfor(int i = 0; i < 5; i++ ){sem_p();printf("A");fflush(stdout);int n = rand() % 3;sleep(n);printf("A");fflush(stdout);sem_v();n = rand()%3;sleep(n);}//vsleep(10);sem_destroy();//刪除信號量
}
2.2.2 b.c
?b.c它演示了如何使用信號量來控制對共享資源的訪問。
在這個例子中,程序通過信號量來確保在打印字符'B'時不會有多個線程或進程同時執行,從而避免輸出混亂。
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include "sem.h"
int main()
{sem_init();for(int i = 0; i < 5; i++ ){sem_p();printf("B");fflush(stdout);int n = rand() % 3;sleep(n);printf("B");fflush(stdout);sem_v();n = rand()%3;sleep(n);}}
2.2.3 sem.h
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/sem.h>union semun
{int val;
};void sem_init();
void sem_p();
void sem_v();
void sem_destroy();
定義了一組與信號量操作相關的函數原型和一個用于信號量操作的聯合體
semun
這個聯合體用于在信號量操作中傳遞信號量的值
這些函數原型定義了信號量操作的接口,下面是這些函數可能的實現方式:
sem_init
:初始化信號量。
sem_p
:執行信號量的P操作(wait操作),減少信號量的值,如果信號量的值小于0,則進程將被阻塞。
sem_v
:執行信號量的V操作(signal操作),增加信號量的值,如果有進程因等待此信號量而被阻塞,則它們可能會被喚醒。
sem_destroy
:銷毀信號量,釋放相關資源。
?2.2.4 sem.c
信號量操作實現
#include "sem.h"
static int semid = -1;void sem_init()
{semid = semget((key_t)1234,1,IPC_CREAT|IPC_EXCL|0600);//嘗試創建一個新的信號了if( semid == -1 )//信號量可能存在{semid = semget((key_t)1234,1,0600);if( semid == -1){printf("sem err\n");return;}}else//初始化 1{union semun a; a.val = 1;if( semctl(semid,0,SETVAL,a) == -1){printf("semctl err\n");}}
}
void sem_p()
{struct sembuf buf;buf.sem_num = 0;buf.sem_op = -1;//pbuf.sem_flg = SEM_UNDO;//if( semop(semid,&buf,1) == -1)//可能阻塞{printf("p err\n");}}
void sem_v()
{struct sembuf buf;buf.sem_num = 0;buf.sem_op = 1;//vbuf.sem_flg = SEM_UNDO;//if( semop(semid,&buf,1) == -1){printf("v err\n");}
}
void sem_destroy()
{if( semctl(semid,0,IPC_RMID) == -1){printf("rm semid err\n");}
}
sem_init
?函數?初始化一個信號量