unix程式設計——訊號量
訊號量我就不介紹了,因為這個之前學習作業系統的時候已經介紹過了
我們引入訊號量的主要目的就是為了解決共享資源在訪問時的同步問題
主要就是PV
操作,這兩個字母均來自於希臘文字passeren
和vrijgeven
LINUX提供了一套操作訊號量的API
,都定義在標頭檔案sys/sem.h
中
訊號量的建立
我們可以使用函式semget
來建立訊號量,原型如下:(sem
是訊號量semaphore
的縮寫)
#include <sys/types.h> #include <sys/ipc.h> #include <sys/sem.h> int semget(key_t key, int nsems, int semflg);
和之前的訊息佇列的建立函式都是一個德行,存在則訪問,否則建立
引數nsems
指定建立的訊號量的個數
引數semflg
的低9
位確定了訊號量屬組以及其他使用者的訪問許可權,還有一些位指定了訊號量的建立方式,其取值與含義如下所示:
引數 | 描述 |
---|---|
IPC_CREAT | 建立訊號量,如果訊號量已經存在,就開啟該訊號量 |
IPC_EXCL | 與巨集IPC_CREAT一起使用,單獨使用無意義,此時只能建立一個核心中不存在的訊號量,如果要建立的訊號量已經存在,則會建立失敗 |
semget
函式的一個例子:
int semid;
semid = semget(0x1234, 10, 0666|IPC_CREAT);
訊號量的修改
函式:semop
函式名起的很同意理解semaphore operate
int semop(int sem_id,struct sembuf *sem_opa,size_t num_sem_ops);
sem_id
就是我們上面使用semget
函式的返回值
sem_opa
的型別是一個結構體sembuf
struct sembuf{ short sem_num; //除非使用一組訊號量,否則它為0 short sem_op; //訊號量在一次操作中需要改變的資料,通常是兩個數 //一個是-1,即P(等待)操作, //一個是+1,即V(傳送訊號)操作。 short sem_flg; //通常為SEM_UNDO,使作業系統跟蹤訊號, //並在程序沒有釋放該訊號量而終止時,作業系統釋放訊號量 };
補充一下結構體中sem_op
的描述:
- sem_op的值為正數
- 該值會加到現有的訊號內含值中,通常用於釋放所控資源的使用權
- sem_op的值為負數
- 而其絕對值又大於訊號的現值,操作將會阻塞,直到訊號值大於或等於sem_op的絕對值,通常用於獲取資源的使用權
- sem_op的值為0
- 如果沒有設定IPC_NOWAIT,則呼叫該操作的程序或者執行緒將暫時睡眠,直到訊號量的值為0
- 否則
- 程序或者執行緒不會睡眠,函式返回錯誤EAGAIN**
訊號量的初始化和刪除
函式:semctl
–>semaphore control
int semctl(int sem_id,int sem_num,int command,[union semun sem_union]);
引數sem_num
是訊號量在訊號量集合中的編號,第一個訊號的編號是0
引數command
有兩個取值:
SETVAL
- 初始化訊號量
IPC_RMID
- 刪除訊號量
引數sem_union
可選,是一個聯合,結構如下:
union semun{
int val; //一般用到的是val,表示要傳給訊號量的初始值
struct semid_ds *buf;
unsigned short *arry;
};
這裡順便複習一波union和struct的區別
-
union
中的成員的記憶體是共享的,這個理解起來也很容易,我們來看一段程式碼:union myun { struct { int x; int y; int z; }u; int k; }a; int main() { a.u.x =4; a.u.y =5; a.u.z =6; a.k = 0; printf("%d %d %d\n",a.u.x,a.u.y,a.u.z); return 0; }
我們在
union
中嵌入了一個struct
,都是int
型別,每個變數各佔四個位元組,根據union
中成員共享記憶體的特性,他們在記憶體中的排列列順序應該是這樣的當我們最後給
k
賦值為0
的時候,相應的,x
的值就被覆蓋了,因此最後的輸出應該是0 5 6
union
主要適用於這樣的場景-
由多個物件、多個事物,但是我們只需要其中一個,在實際中的應用可能是下面這樣的:(報文格式的設計)
struct CommuPacket { int iPacketType; //報文型別標誌 union //每次傳送的是三種報文中的一種,使用union { struct structA packetA; struct structB packetB; struct structC packetC; } };
-
下面貼一個從網上找到的訊號量
函式的使用示例:
include<stdio.h>
#include<stdlib.h>
#include<sys/sem.h>
union semun
{
int val;
struct semid_ds *buf;
unsigned short *array;
};
int sem_id;
int set_semvalue()
{
union semun sem_union;
sem_union.val = 1;
if(semctl(sem_id,0,SETVAL,sem_union)==-1)
return 0;
return 1;
}
int semaphore_p()
{
struct sembuf sem_b;
sem_b.sem_num = 0;
sem_b.sem_op = -1;
sem_b.sem_flg = SEM_UNDO;
if(semop(sem_id,&sem_b,1)==-1)
{
fprintf(stderr,"semaphore_p failed\n");
return 0;
}
return 1;
}
int semaphore_v()
{
struct sembuf sem_b;
sem_b.sem_num = 0;
sem_b.sem_op = 1;
sem_b.sem_flg = SEM_UNDO;
if(semop(sem_id,&sem_b,1)==-1)
{
fprintf(stderr,"semaphore_v failed\n");
return 0;
}
return 1;
}
void del_semvalue()
{
//刪除訊號量
union semun sem_union;
if(semctl(sem_id,0,IPC_RMID,sem_union)==-1)
fprintf(stderr,"Failed to delete semaphore\n");
}
int main(int argc,char *argv[])
{
char message = 'x';
//建立訊號量
sem_id = semget((key_t)1234,1,0666|IPC_CREAT);
if(argc>1)
{
//初始化訊號量
if(!set_semvalue())
{
fprintf(stderr,"init failed\n");
exit(EXIT_FAILURE);
}
//引數的第一個字元賦給message
message = argv[1][0];
}
int i=0;
for(i=0;i<5;i++)
{
//等待訊號量
if(!semaphore_p())
exit(EXIT_FAILURE);
printf("%c",message);
fflush(stdout);
sleep(1);
//傳送訊號量
if(!semaphore_v())
exit(EXIT_FAILURE);
sleep(1);
}
printf("\n%d-finished\n",getpid());
if(argc>1)
{
//退出前刪除訊號量
del_semvalue();
}
exit(EXIT_SUCCESS);
}
執行結果:
[email protected]:~# ./test.exe 0 & ./test.exe
[1] 20393
x0x0x0x0x0
20394-finished
[email protected]:~#
20393-finished
訊號量的存在使得兩個程序交替列印字元