Linux进程间通信(管道、消息队列、共享内存、信号、信号量)
目錄
- Linux進程間通信概述
- 1.管道
- 無名管道(pipe)
- 有名管道(fifo)
- 2.消息隊列(msg)
- 消息隊列的通信原理
- 消息隊列相關api
- 消息隊列收發數據
- 鍵值生成
- 消息隊列移除
- 3.共享內存(shm)
- 4.信號(sig)
- 信號概述
- 信號編程(入門)
- 信號攜帶消息(高級)
- 5.信號量(sem)
- P、V操作(類似信號量lock、unlock)
- 6.通信方式總結
Linux進程間通信概述
進程間通信(IPC,InterProcess Communication)是指在不同進程之間傳播或交換信息。
IPC 的方式通常有管道(包括無名管道和命名管道)、消息隊列、共享內存、信號、信號量、Socket、Streams 等。其中 Socket 和 Streams 支持不同主機上的兩個進程 IPC。
1.管道
無名管道(pipe)
管道,通常指無名管道,是 UNIX 系統IPC最古老的形式。
特點:
1、它是半雙工的(即數據只能在一個方向上流動),具有固定的讀端(fd[0])和寫端(fd[1])。
2、它只能用于具有親緣關系的進程之間的通信(也是父子進程或者兄弟進程之間)。
3、它可以看成是一種特殊的文件,對于它的讀寫也可以使用普通的read、write 等函數。但是它不是普通的文件,并不屬于其他任何文件系統,并且只存在于內存中。
4、管道中的數據讀走就沒了。
原型:
1 #include <unistd.h> 2 int pipe(int fd[2]); // 返回值:若成功返回0,失敗返回-1當一個管道建立時,它會創建兩個文件描述符:fd[0]為讀而打開,fd[1]為寫而打開。要關閉管道只需將這兩個文件描述符關閉即可。如下圖:
例子:
創建管道后,在父進程中寫入,在子進程中讀
注意: 管道為半雙工通信,讀和寫操作在同一時間內只能進行一個,所以在讀的時候要關閉寫端,寫的時候關閉讀端。
運行結果:
有名管道(fifo)
FIFO,也稱為命名管道,它是一種文件類型。
1、特點
FIFO可以在無關的進程之間交換數據,與無名管道不同。
FIFO有路徑名與之相關聯,它以一種特殊設備文件形式存在于文件系統中。
2、原型
1 #include <sys/stat.h> 2 // 返回值:成功返回0,出錯返回-1 3 int mkfifo(const char *pathname, mode_t mode);其中的 mode 參數與open函數中的 mode 相同。一旦創建了一個 FIFO,就可以用一般的文件I/O函數操作它。
當 open 一個FIFO時,是否設置非阻塞標志(O_NONBLOCK)的區別:
1、若沒有指定O_NONBLOCK(默認),只讀 open 要阻塞到某個其他進程為寫而打開此 FIFO(參照下面的例子)。類似的,只寫 open 要阻塞到某個其他進程為讀而打開它。
2、若指定了O_NONBLOCK,則只讀 open 立即返回。而只寫 open 將出錯返回 -1 如果沒有進程已經為讀而打開該 FIFO,其errno置ENXIO。
創建管道例子
#include <stdio.h> #include <sys/types.h> #include <sys/stat.h> #include <errno.h>int main() {//int mkfifo(const char *pathname, mode_t mode);if(mkfifo("./file",0600) == -1 && errno != EEXIST){printf("mkfifo創建失敗\n");perror("why:");}else{if(errno == EEXIST){printf("文件已經存在\n");}printf("mkfifo創建成功\n");}return 0; }使用有名管道通信的例子
read代碼:
#include <stdio.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <string.h> int main() {int nread;mkfifo("./file",0600);char buf[1024] = {0};int fd = open("./file",O_RDONLY);//若沒有指定O_NONBLOCK(默認),只讀 open 要阻塞到某個其他進程為寫而打開此 FIFOprintf("open success\n");while(1){nread = read(fd,buf,1024);printf("read %d byte ,neirong:%s\n",nread,buf);}return 0; }write代碼:
#include <stdio.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <string.h> int main() {mkfifo("./file",0600);char *str = "this is a fifo demo!";int fd = open("./file",O_WRONLY);printf("open success\n");while(1){write(fd,str,strlen(str));sleep(1);}return 0; }運行read會阻塞,一直到運行write后read才會繼續往下執行。
個人感覺很像文件操作,不過不用進行lseek等操作,內核知道這是管道操作,數據讀出時管道數據清除。
2.消息隊列(msg)
消息隊列的通信原理
消息隊列,是消息的鏈接表(結構體),存放在內核中。一個消息隊列由一個標識符(即隊列ID)來標識。
特點
消息隊列是面向記錄的,其中的消息具有特定的格式以及特定的優先級。
消息隊列獨立于發送與接收進程。進程終止時,消息隊列及其內容并不會被刪除(Linux內核機制進行管理)。
消息隊列可以實現消息的隨機查詢(鏈表機制就是這樣的),消息不一定要以先進先出的次序讀取,也可以按消息的類型讀取。
消息隊列的通信原理理解圖:
消息隊列相關api
1 #include <sys/msg.h>2 // 創建或打開消息隊列:成功返回隊列ID,失敗返回-1 3 int msgget(key_t key, int flag);4 // 發送消息:成功返回0,失敗返回-1 5 int msgsnd(int msqid, const void *ptr, size_t size, int flag);6 // 接收消息:成功返回消息數據的長度,失敗返回-1 7 int msgrcv(int msqid, void *ptr, size_t size, long type,int flag);8 // 控制消息隊列:成功返回0,失敗返回-1 9 int msgctl(int msqid, int cmd, struct msqid_ds *buf);在以下兩種情況下,msgget將創建一個新的消息隊列:
1、如果沒有與鍵值key相對應的消息隊列,并且flag中包含了IPC_CREAT標志位。
2、key參數為IPC_PRIVATE。
函數msgrcv在讀取消息隊列時,type參數有下面幾種情況:
type == 0,返回隊列中的第一個消息;
type > 0,返回隊列中消息類型為 type 的第一個消息;
type < 0,返回隊列中消息類型值小于或等于 type 絕對值的消息,如果有多個,則取類型值最小的消息。
可以看出,type值非 0 時用于以非先進先出次序讀消息。也可以把 type 看做優先級的權值。(其他的參數解釋,請自行Google之)
消息隊列收發數據
readmsg
#include <stdio.h> #include <sys/types.h> #include <sys/ipc.h> #include <sys/msg.h> struct msgbuf {long mtype; /* message type, must be > 0 */char mtext[128]; /* message data */ };int main() {struct msgbuf readBuf;//int msgget(key_t key, int msgflg);int msgId=msgget(0x1234,IPC_CREAT|0777);//如果存在就打開,沒有則創建權限是可讀刻寫可執行if(msgId == -1){printf("創建失敗\n");}//int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);// ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp,int msgflg);msgrcv(msgId,&readBuf,sizeof(readBuf.mtext),888,0);//0是阻塞方式printf("讀取來自隊列的內容:%s\n",readBuf.mtext);return 0; }sendmsg
#include <stdio.h> #include <sys/types.h> #include <sys/ipc.h> #include <sys/msg.h> #include <string.h> struct msgbuf {long mtype; /* message type, must be > 0 */char mtext[128]; /* message data */ };int main() {struct msgbuf sendBuf={888,"this message from que"};;//int msgget(key_t key, int msgflg);int msgId=msgget(0x1234,IPC_CREAT|0777);//如果存在就打開,沒有則創建權限是可讀刻寫可執行if(msgId == -1){printf("創建失敗\n");}//int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);// ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp,int msgflg);msgsnd(msgId,&sendBuf,strlen(sendBuf.mtext),0);//0是非阻塞return 0; }運行結果:
先運行readmsg阻塞在那等待發送消息
實現雙方消息通信都能發送和接收
readmsg
#include <stdio.h> #include <sys/types.h> #include <sys/ipc.h> #include <sys/msg.h> #include <string.h> struct msgbuf {long mtype; /* message type, must be > 0 */char mtext[128]; /* message data */ };int main() {struct msgbuf readBuf;struct msgbuf sendBuf={998,"thank you que, i have received"};//int msgget(key_t key, int msgflg);int msgId=msgget(0x1234,IPC_CREAT|0777);//如果存在就打開,沒有則創建權限是可讀刻寫可執行if(msgId == -1){printf("創建失敗\n");}//int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);// ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp,int msgflg);msgrcv(msgId,&readBuf,sizeof(readBuf.mtext),888,0);//0是非阻塞方式printf("讀取來自隊列的內容:%s\n",readBuf.mtext);msgsnd(msgId,&sendBuf,strlen(sendBuf.mtext),0);//0是非阻塞printf("發送完畢\n");return 0; }sendmsg
#include <stdio.h> #include <sys/types.h> #include <sys/ipc.h> #include <sys/msg.h> #include <string.h> struct msgbuf {long mtype; /* message type, must be > 0 */char mtext[128]; /* message data */ };int main() {struct msgbuf sendBuf={888,"this message from que"};struct msgbuf readBuf;//int msgget(key_t key, int msgflg);int msgId=msgget(0x1234,IPC_CREAT|0777);//如果存在就打開,沒有則創建權限是可讀刻寫可執行if(msgId == -1){printf("創建失敗\n");}//int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);// ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp,int msgflg);msgsnd(msgId,&sendBuf,strlen(sendBuf.mtext),0);//0是非阻塞printf("發送完畢\n");msgrcv(msgId,&readBuf,sizeof(readBuf.mtext),998,0);//0是非阻塞方式printf("讀取來自隊列的內容:%s\n",readBuf.mtext);return 0; }鍵值生成
readmsg
#include <stdio.h> #include <sys/types.h> #include <sys/ipc.h> #include <sys/msg.h> #include <string.h> struct msgbuf {long mtype; /* message type, must be > 0 */char mtext[128]; /* message data */ };int main() {struct msgbuf readBuf;struct msgbuf sendBuf={998,"thank you que, i have received"};//key_t ftok(const char *pathname, int proj_id);key_t key;key=ftok(".",'z');//"."當前路徑, proj_id典型的用法是將一個ASCII碼作為proj_id,隨便取值都可以 內核這兩者組合出一個鍵值printf("key = %x\n",key);//用16進制輸出//int msgget(key_t key, int msgflg);int msgId=msgget(key,IPC_CREAT|0777);//如果存在就打開,沒有則創建權限是可讀刻寫可執行if(msgId == -1){printf("創建失敗\n");}//int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);// ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp,int msgflg);msgrcv(msgId,&readBuf,sizeof(readBuf.mtext),888,0);//0是非阻塞方式printf("讀取來自隊列的內容:%s\n",readBuf.mtext);msgsnd(msgId,&sendBuf,strlen(sendBuf.mtext),0);//0是非阻塞printf("發送完畢\n");return 0; }sendmsg
#include <stdio.h> #include <sys/types.h> #include <sys/ipc.h> #include <sys/msg.h> #include <string.h> struct msgbuf {long mtype; /* message type, must be > 0 */char mtext[128]; /* message data */ };int main() {struct msgbuf sendBuf={888,"this message from que"};struct msgbuf readBuf;key_t key;key=ftok(".",'z');//"."當前路徑,proj_id典型的用法是將一個ASCII碼作為proj_idprintf("key = %x\n",key);//用16進制輸出//int msgget(key_t key, int msgflg);int msgId=msgget(key,IPC_CREAT|0777);//如果存在就打開,沒有則創建權限是可讀刻寫可執行if(msgId == -1){printf("創建失敗\n");}//int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);// ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp,int msgflg);msgsnd(msgId,&sendBuf,strlen(sendBuf.mtext),0);//0是非阻塞printf("發送完畢\n");msgrcv(msgId,&readBuf,sizeof(readBuf.mtext),998,0);//0是非阻塞方式printf("讀取來自隊列的內容:%s\n",readBuf.mtext);return 0; }消息隊列移除
int msgctl(int msqid, int cmd, struct msqid_ds *buf);struct msqid_ds *buf這里我們一般寫NULL
CMD類型參照下圖(箭頭指的就是i用的最多的,把消息隊列生成的鏈表在內核里移除):
例如:
readmsg
sendmsg
#include <stdio.h> #include <sys/types.h> #include <sys/ipc.h> #include <sys/msg.h> #include <string.h> struct msgbuf {long mtype; /* message type, must be > 0 */char mtext[128]; /* message data */ };int main() {struct msgbuf sendBuf={888,"this message from que"};struct msgbuf readBuf;key_t key;key=ftok(".",'z');//"."當前路徑,proj_id典型的用法是將一個ASCII碼作為proj_idprintf("key = %x\n",key);//用16進制輸出//int msgget(key_t key, int msgflg);int msgId=msgget(key,IPC_CREAT|0777);//如果存在就打開,沒有則創建權限是可讀刻寫可執行if(msgId == -1){printf("創建失敗\n");}//int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);// ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp,int msgflg);msgsnd(msgId,&sendBuf,strlen(sendBuf.mtext),0);//0是非阻塞printf("發送完畢\n");msgrcv(msgId,&readBuf,sizeof(readBuf.mtext),998,0);//0是非阻塞方式printf("讀取來自隊列的內容:%s\n",readBuf.mtext);//int msgctl(int msqid, int cmd, struct msqid_ds *buf);msgctl(msgId,IPC_RMID,NULL);return 0; }3.共享內存(shm)
共享內存(Shared Memory),指兩個或多個進程共享一個給定的存儲區。
特點
-
共享內存是最快的一種 IPC,因為進程是直接對內存進行存取。
-
因為多個進程可以同時操作,所以需要進行同步。
信號量+共享內存通常結合在一起使用(最后一節講解),信號量用來同步對共享內存的訪問。
和消息隊列的區別:
消息隊列就像兩個人聊天,一方要將要說的話寫在一張紙上放入箱子里,另外一個人去箱子里取出來閱讀。這個箱子(消息隊列)不會自行銷毀,要調用msgctl才可以。
共享內存就好像兩個學生在上課的時候,由于不能說話,就只好拿一個本子來聊天,這個本子就相當于共享的內存,雙方可以同時看到紙上的內容。比消息隊列效率高。調用shmctl刪除共享內存。
相關api原型
1 #include <sys/shm.h>2 // 創建或獲取一個共享內存:成功返回共享內存ID,失敗返回-1 3 int shmget(key_t key, size_t size, int flag);4 // 連接共享內存到當前進程的地址空間(也叫掛載、映射):成功返回指向共享內存的指針,失敗返回-1 5 void *shmat(int shm_id, const void *addr, int flag);6 // 斷開與共享內存的連接:成功返回0,失敗返回-1 //注意,這并不是從系統中刪除該共享內存,只是當前進程不能再訪問該共享內存而已。 7 int shmdt(void *addr); 8 // 控制共享內存的相關信息:成功返回0,失敗返回-1 9 int shmctl(int shm_id, int cmd, struct shmid_ds *buf);參數說明:
key為ftok生成的鍵值
size為共享內存的長度,以字節為單位
當用shmget函數創建一段共享內存時,必須指定其 size;而如果引用一個已存在的共享內存,則將 size 指定為0 。
flag為所需要的操作和權限,可以用來創建一個共享存儲空間并返回一個標識符或者獲得一個共享標識符。
----flag的值為IPC_CREAT:如果不存在key值的共享存儲空間,且權限不為0,則創建共享存儲空間,并返回一個共享存儲標識符。如果存在,則直接返回共享存儲標識符。
----flag的值為 IPC_CREAT |IPC_EXCL:如果不存在key值的共享存儲空間,且權限不為0,則創建共享存儲空間,并返回一個共享存儲標識符。如果存在,則產生錯誤。
cmd 常用的是IPC_RMID從系統中刪除該共享內存
例子
創建共享內存并寫入數據
讀共享內存的內容
#include <stdio.h> #include <sys/ipc.h> #include <sys/shm.h> #include <stdlib.h>int main() {key_t key;char *shmaddr;// key_t ftok(const char *pathname, int proj_id);key=ftok(".",2);printf("key = %x\n",key);//int shmget(key_t key, size_t size, int shmflg);//創建或獲取一個共享內存:成功返回共享內存ID,失敗返回-1int shmId=shmget(key,1024*4,0);//只要打開就行不必創建if(shmId == -1){printf("創建共享內存失敗\n");exit(-1);//異常退出}//void *shmat(int shmid, const void *shmaddr, int shmflg);//連接共享內存到當前進程的地址空間:成功返回指向共享內存的指針,失敗返回-1shmaddr=shmat(shmId,0,0);//掛載映射,如果引用一個已存在的共享內存,則將 size 指定為0 printf("shmat ok\n");printf("內容是:%s\n",shmaddr);//int shmdt(const void *shmaddr);//斷開與共享內存的連接shmdt(shmaddr);return 0; }4.信號(sig)
本節參照博文:https://www.jianshu.com/p/f445bfeea40a
信號概述
1.信號:對Linux來說就是軟中斷,與單片機的硬件中斷(串口)類似。如在linux中輸入 ctrl+c 來停止一個程序
2.信號的名字與編號:可在linux中通過 kill -l 查詢(Linux系統一共有64個信號,編號1-64。不存在0信號,0信號有特殊的應用:在系統級的應用中被占用)
部分信號的說明:
2)SIGINT:ctrl+c 終止信號
3)SIGQUIT:ctrl+\ 終止信號
20)SIGTSTP:ctrl+z 暫停信號
26)SIGALRM:鬧鐘信號 收到此信號后定時結束,結束進程
17)SIGCHLD:子進程狀態改變,父進程收到信號
9)SIGKILL:殺死信號
3.信號處理的三種方式:忽略,捕捉和默認動作
1、忽略:就跟字面意思一樣忽略掉它(注意:SIGKILL,SIGSTOP不能被忽略)
2、捕捉:就是一些信號處理的函數,然后讓這個函數告訴內核,當信號產生時,內核調用該函數,實現某種信號的處理
3、默認動作:每個信號都有其對應的默認的處理動作,當觸發了某種信號,系統就會立刻去執行。
4.信號的使用
其實對于常用的 kill 命令就是一個發送信號的工具,kill -9 PID或者使用命令kill -SIGKILL PID (二者作用一樣)來殺死進程。比如,我在后臺運行了一個 top 工具,通過 ps 命令可以查看他的 PID,通過 kill -9 來發送了一個終止進程的信號來結束了 top 進程。如果查看信號編號和名稱,可以發現9對應的是 9) SIGKILL,正是殺死該進程的信號。而以下的執行過程實際也就是執行了9號信號的默認動作——殺死進程。
對于信號來說,最大的意義不是為了殺死信號,而是實現一些異步通訊(即信號處理方式第二種–捕捉)的手段,那么如何來自定義信號的處理函數呢?
信號編程(入門)
信號處理函數的注冊(信號綁定):signal (入門),sigaction(高級,可攜帶信號)
信號處理發送函數: kill (入門), sigqueue(高級,可攜帶信號)
信號綁定
#include <stdio.h> #include <signal.h>// typedef定義了一種類型sighandler_t//typedef void (*sighandler_t)(int);//函數指針(指向函數的指針),無返回值,傳入參數為一個int型//sighandler_t signal(int signum, sighandler_t handler); // 返回這種類型 帶有_t表示結構體 函數指針變量 void handler(int signum){switch(signum){case 2:printf("get SIGINT,signum=%d\n",signum);break;case 9:printf("get SIGKILL,signum=%d\n",signum);break;case 10:printf("get SIGUSR1,signum=%d\n",signum);break;}printf("never quit\n"); }int main(){//函數指針 指向函數signal(SIGINT,handler);//捕捉信號 SIGINT是ctrl c的指令signal(SIGKILL,handler);signal(SIGUSR1,handler);for(;;);//等于while(1);return 0; }編譯運行,在鍵盤上輸入 ctrl+c,結果為:
信號發送(殺死程序的信號 SIGKILL)
運行結果:
信號的忽略:
信號攜帶消息(高級)
信號攜帶消息思路:
發信號
1.用什么發 sigqueue()
2.怎么發消息
收信號
1.用什么綁定函數(以及收到信號如何處理動作)sigaction()
2.如何讀出消息
信號攜帶消息編程實現
收信號nicesignal.c
#include <stdio.h> #include <signal.h> #include <sys/types.h> #include <unistd.h> /*struct sigaction {void (*sa_handler)(int);void (*sa_sigaction)(int, siginfo_t *, void *);sigset_t sa_mask;int sa_flags;void (*sa_restorer)(void);};*/void handler(int signum, siginfo_t *info, void *context)//info里面的參數可以通過man手冊查詢 {printf("signum=%d\n",signum);if(context != NULL){printf("get data=%d\n",info->si_int);printf("get data=%d\n",info->si_value.sival_int);//和上面一樣換了種方式printf("發送者的pid=%d\n",info->si_pid);} }int main() {struct sigaction act;printf("pid=%d\n",getpid());act.sa_sigaction=handler;act.sa_flags=SA_SIGINFO;//能夠獲取到信息信息//int sigaction(int signum, const struct sigaction *act,struct sigaction *oldact);sigaction(SIGUSR1,&act,NULL);//第三個是參數是備份的這邊寫NULL SIGUSR1對應的編號為10!!!printf("22");while(1);return 0; }發信號niceSendSig.c
#include <stdio.h> #include <signal.h> #include <sys/types.h> #include <unistd.h> #include <stdlib.h> int main(int argc,char **argv) {int signum;int pid;signum=atoi(argv[1]);//atoi把字符串轉化為整形pid=atoi(argv[2]);//接收進程的pid號union sigval value;value.sival_int=100;//發送一個整型數100的消息/*union sigval {int sival_int;void *sival_ptr; 發送字符的話把地址傳進來};*///int sigqueue(pid_t pid, int sig, const union sigval value);sigqueue(pid,signum,value);printf("我的pid是:%d\n",getpid());printf("發送完畢\n");return 0; }5.信號量(sem)
1.信號量:
信號量(Semaphore是一個計數器,用于實現進程間的互斥與同步,不用于存儲進程間的通信數據
2.特點:
(1).用于進程間同步,若要在進程間傳遞數據需要結合共享內存
(2).信號量是基于PV操作,程序對信號量是原子操作
所謂原子操作,就是“不可中斷的一個或一系列操作”,也就是不會被線程調度機制打斷的操作(3).對信號量的操作不僅限于對信號的+1,-1,可以是任意數(視頻沒講)
P、V操作(類似信號量lock、unlock)
可以這樣理解:一間房間(臨界資源)的門前有一個盒子,盒子里有鑰匙(信號量),一個人拿了鑰匙(P操作),開了門并走進了房間,且門外還有人等著,得等進去的人出來放鑰匙(V操作),這個人才能拿鑰匙(P操作)進入房間
多道程序系統中存在許多進程,它們共享各種資源,然而有很多資源一次只能供一個進程使用。一次僅允許一個進程使用的資源稱為臨界資源。許多物理設備都屬于臨界資源,如輸入機、打印機、磁帶機等。
4.相關api
最簡單的信號量是只能取 0 和 1 的變量,這也是信號量最常見的一種形式,叫做二值信號量(Binary Semaphore)。而可以取多個正整數的信號量被稱為通用信號量。
Linux 下的信號量函數都是在通用的信號量數組上進行操作,而不是在一個單一的二值信號量上進行操作。
1 #include <sys/sem.h> 2 // 創建或獲取一個信號量組:若成功返回信號量集ID,失敗返回-1 3 int semget(key_t key, int num_sems, int sem_flags); 4 // 對信號量組進行操作,改變信號量的值:成功返回0,失敗返回-1 5 int semop(int semid, struct sembuf semoparray[], size_t numops); 6 // 控制信號量的相關信息 7 int semctl(int semid, int sem_num, int cmd, ...);例子
目前是沒有鎖的狀態,父進程想拿鎖,在這邊等待,然后子進程有鎖直接放回,父進程得到鎖后再運行。相當于不使用前面學習的wait方式保證子進程先運行,因為沒有鎖父進程卡在拿鎖那邊。
鎖初始化為0,子進程先運行,運行結束放鎖(1),父進程拿鎖(-1),然后放鎖(1),最后銷毀鎖。
6.通信方式總結
1.管道(無名管道):速度慢,容量有限,只有父子進程能通訊
2.FIFO(有名管道):任何進程間都能通訊,但速度慢
3.消息隊列:容量受到系統限制,且要注意第一次讀的時候,要考慮上一次沒有讀完數據的問題
4.信號量:不能傳遞數據,只能用來同步(P操作、V操作)
5.共享內存區:能夠很容易控制容量,速度快,但要保持同步(和信號量結合使用),比如一個進程在寫的時候,另一個進程要注意讀寫的問題,相當于線程中的線程安全。當然,共享內存區同樣可以用作線程間通訊,不過沒這個必要,線程間本來就已經共享了同一進程內的一塊內存
總結
以上是生活随笔為你收集整理的Linux进程间通信(管道、消息队列、共享内存、信号、信号量)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Yii中使用的简单方法
- 下一篇: Linux网络编程(Socket)