我會用幾篇博客總結一下在Linux中進程之間通信的幾種方法,我會把這個開頭的摘要部分在這個系列的每篇博客中都打出來
進程之間通信的方式
進程間通信(二)—消息隊列傳送門:http://www.cnblogs.com/lenomirei/p/5642575.html
進程間通信(一)—管道傳送門:http://www.cnblogs.com/lenomirei/p/5636339.html
第三篇來了!前兩篇訪問量很多,真的是很感謝了
這次記下信號量的相關操作函數和方法,和以前一樣會在博文的最後把測試代碼貼出來!
學過操作系統這本書的話應該對信號量這個名詞不會感到陌生,同時信號和信號量是不同的!
信號量多用於進程間的同步與互斥,簡單的說一下同步和互斥的意思
同步:處理競爭就是同步,安排進程執行的先後順序就是同步,每個進程都有一定的個先後執行順序
互斥:互斥訪問不可共享的臨界資源,同時會引發兩個新的控制問題(互斥可以說是特殊的同步)
競爭:當並發進程競爭使用同一個資源的時候,我們就稱為競爭進程
簡單說一下信號量的工作機制(因為真的很簡單),可以直接理解成計數器(當然其實加鎖的時候肯定不能這麼簡單,不只只是信號量了),信號量會有初值(>0),每當有進程申請使用信號量,通過一個P操作來對信號量進行-1操作,當計數器減到0的時候就說明沒有資源了,其他進程要想訪問就必須等待(具體怎麼等還有說法,比如忙等待或者睡眠),當該進程執行完這段工作(我們稱之為臨界區)之後,就會執行V操作來對信號量進行+1操作。
臨界區:不是個簡單的區域!是加鎖區間的代碼!
臨界資源:只能被一個進程同時使用(不可以多個進程共享),要用到互斥
我們可以說信號量也是進程間通信的一種方式,比如互斥鎖的簡單實現就是信號量,一個進程使用互斥鎖,並通知(通信)其他想要該互斥鎖的進程,阻止他們的訪問和使用(老子正在用!@_@!)
其實信號量的操作函數和之前幾個都是類似的,都是套路
一定會覺得熟悉的!和消息隊列十分類似
下面會看到信號量的一個缺點
這說明信號量的初始化和創建是分開操作的,為什麼是分開的,因為創建的時候沒有給信號量的大小的參數,信號量是可以設置大的(不是只可以設置為1),當然設置為1就是互斥訪問了,比如典型的生產者和消費者問題,但是如果像是讀者寫者中的讀者信號量可不一樣(因為可以有多個讀者同時讀),這裡就不解釋這個模型問題了。
缺點就是,一旦初始化和創建分開之後,就會有線程安全的問題,進程A剛創建一信號量還沒有初始化,進程B便已經開始對該信號量進程P操作(哥哥!我還沒賦值初始化!)根本無法進行-1操作,會阻塞的,我都不知道咋回事(親身體驗該錯誤)。。。
廢話不多說了,函數還是套路
當SETVAL被設置之後,可變參數列表的第四個參數就可以用上了,這裡傳入一個神奇的聯合體(我在手冊裡找到了,但是死活用不了它,還是自己寫了一個)
1 union semun { 2 int val; /* Value for SETVAL */ 3 struct semid_ds *buf; /* Buffer for IPC_STAT, IPC_SET */ 4 unsigned short *array; /* Array for GETALL, SETALL */ 5 struct seminfo *__buf; /* Buffer for IPC_INFO 6 (Linux-specific) */ 7 };
就是它!就是它!就看第一個吧,其他我也沒用了(英語我也看不太懂),val就是設置的初值啦,傳入的時候直接傳入就行了,參數就是一個變量(不用傳指針)
這個函數一會刪除我們還會遇到它,ctl就是control控制的簡寫,很多操作都有它
一開始就寫了操作的兩種方式,一種叫P操作,一種叫V操作,都是通過一個函數實現的,這個函數還關聯一個結構體
1 struct sembuf 2 { 3 unsigned short sem_num; /* semaphore number */ 4 short sem_op; /* semaphore operation */ 5 short sem_flg; /* operation flags */ 6 };
sem_num就是index of信號量,你想操作的信號量的下標,sem_op是個短整型,這裡給1或者-1分別表示V和P操作,而最後一個semflg手冊中提到了兩個
IPC_NOWAIT:一看就是IPC通用的,非等待,不讓忙等待只好去休眠(或者別的,反正不能循環等待)
SEM_UNDO:這個一看就是信號量獨有的,看英文也能看出來UNDO是取消之前做過的,什麼意思呢,不得不提到,當進程在臨界區工作(持有鎖)的時候,是不允許掛(掛掉更不好啦)起的!!!這很重要(又要加篇幅啦!@_@),因為一旦掛起或者休眠,有可能就醒不了啦!(掛起有可能等待事件)這樣不好,會讓等待的其他進程饑餓,尤其是掛掉的時候,肯定就更糟糕了,根本就無法執行V操作,等待的進程就會無限的等待下去,遞歸申請信號量也是不好的,尤其是互斥信號量的時候會造成死鎖。這時候UNDO就出來把之前做過的東西都取消掉,P操作就當沒執行過,信號量又從0變回了1,皆大歡喜。
semctl又來了,和消息隊列的刪除類似
事已至此,基本操作就說完了,廢話少說,show me the code
我的程序分為comm.h(公共頭文件) comm.c(封裝基本函數) server.c(采用fork完成父子進程間使用信號量) 一共3個文件
基本功能:模擬線程安全(簡單版本),父進程會打印"A""A"子進程會打印"B""B",因為執行順序的原因可能會交叉打印,但我不要這樣,我要讓AA和BB分別連著!完成這個功能
先看之前的版本
交叉打印不是我想要的
功能完成連續打印
comm.h
1 #include <stdio.h> 2 #include <sys/ipc.h> 3 #include <string.h> 4 #include <unistd.h> 5 #include <sys//sem.h> 6 #include <stdlib.h> 7 #include <errno.h> 8 9 10 #define _PATH_NAME_ "/tmp" 11 #define _PROJ_ID_ 0x6666 12 13 static int comm_create_sem(int flags,int num); 14 int create_sem(int num); 15 int get_sem(); 16 void P_sem(int sem_id,int index); 17 void V_sem(int sem_id,int index);
comm.c
1 #include "comm.h" 2 3 union semun { 4 int val; /* Value for SETVAL */ 5 struct semid_ds *buf; /* Buffer for IPC_STAT, IPC_SET */ 6 unsigned short *array; /* Array for GETALL, SETALL */ 7 struct seminfo *__buf; /* Buffer for IPC_INFO 8 (Linux-specific) */ 9 }; 10 11 12 static int comm_create_sem(int flags,int num) 13 { 14 key_t _key=ftok(_PATH_NAME_,_PROJ_ID_); 15 if(_key<0) 16 { 17 printf("%d:%s",errno,strerror(errno)); 18 } 19 int sem_id; 20 if((sem_id=semget(_key,num,flags))<0) 21 { 22 printf("semget errno,%d:%s",errno,strerror(errno)); 23 } 24 return sem_id; 25 } 26 27 int create_sem(int num) 28 { 29 int flags=IPC_CREAT | IPC_EXCL; 30 int sem_id=comm_create_sem(flags,num); 31 union semun v; 32 v.val=1; 33 if(semctl(sem_id,0,SETVAL,v)<0)//init 34 { 35 printf("init error,%d:%s",errno,strerror(errno)); 36 } 37 return sem_id; 38 } 39 40 int get_sem() 41 { 42 int flags=0; 43 return comm_create_sem(flags,0); 44 } 45 46 47 void P_sem(int sem_id,int index) 48 { 49 struct sembuf s; 50 s.sem_num=index; 51 s.sem_op=-1; 52 s.sem_flg=0; 53 if(semop(sem_id,&s,1)<0) 54 { 55 printf("op errro,%d:%s",errno,strerror(errno)); 56 } 57 } 58 59 void V_sem(int sem_id,int index) 60 { 61 struct sembuf s; 62 s.sem_num=index; 63 s.sem_op=1; 64 s.sem_flg=0; 65 if(semop(sem_id,&s,1)<0) 66 { 67 printf("op error,%d:%s",errno,strerror(errno)); 68 } 69 70 } 71 72 void destory_sem(int sem_id) 73 { 74 semctl(sem_id,0,IPC_RMID); 75 }
server.c
1 #include "comm.h" 2 3 4 5 int main() 6 { 7 int pid; 8 pid=fork(); 9 if(pid>0) 10 { 11 //father 12 int sem_id=create_sem(1); 13 while(1) 14 { 15 P_sem(sem_id,0); 16 printf("A"); 17 fflush(stdout); 18 sleep(1); 19 printf("A"); 20 fflush(stdout); 21 V_sem(sem_id,0); 22 } 23 } 24 else 25 { 26 //child 27 while(1) 28 { 29 int sem_id=get_sem(); 30 P_sem(sem_id,0); 31 printf("B"); 32 fflush(stdout); 33 sleep(1); 34 printf("B"); 35 fflush(stdout); 36 V_sem(sem_id,0); 37 } 38 } 39 40 return 0; 41 }
http://xxxxxx/Linuxjc/1140282.html TechArticle