參考html
進程間通訊(IPC,InterProcess Communication)是指在不一樣進程之間傳播或交換信息。編程
IPC的方式一般有管道(包括無名管道和命名管道)、消息隊列、信號量、共享存儲、Socket、Streams等。其中 Socket和Streams支持不一樣主機上的兩個進程IPC。c#
以Linux中的C語言編程爲例。數組
管道,一般指無名管道,是 UNIX 系統IPC最古老的形式。安全
1.它是半雙工的(即數據只能在一個方向上流動),具備固定的讀端和寫端。服務器
2.它只能用於具備親緣關係的進程之間的通訊(也是父子進程或者兄弟進程之間)。網絡
3.它能夠當作是一種特殊的文件,對於它的讀寫也可使用普通的read、write 等函數。可是它不是普通的文件,並不屬於其餘任何文件系統,而且只存在於內存中。多線程
#include <unistd.h> int pipe(int fd[2]); // 返回值:若成功返回0,失敗返回-1
當一個管道創建時,它會建立兩個文件描述符:fd[0]爲讀而打開,fd[1]爲寫而打開。以下圖:函數
要關閉管道只需將這兩個文件描述符關閉便可。測試
單個進程中的管道幾乎沒有任何用處。因此,一般調用 pipe 的進程接着調用 fork,這樣就建立了父進程與子進程之間的 IPC 通道。以下圖所示:
若要數據流從父進程流向子進程,則關閉父進程的讀端(fd[0])與子進程的寫端(fd[1]);反之,則可使數據流從子進程流向父進程。
#include <stdio.h> #include <unistd.h> #include <errno.h> #include <string.h> #define MAX_MSG_SIZE 1024 int main(int argc, char** argv) { pid_t pid = -1; int pipefd[2] = {0}; /** * 建立的一個pipe會有兩個文件描述符,pipefd[0]是讀取數據的,pipefd[1]是寫入數據的。 */ if (0 != pipe(pipefd)) { printf("create pipe failed with errno = %d.\n", errno); return 0; } pid = fork(); if (pid < 0) { printf("call fork failed.\n"); } /** * 這是父進程,若是咱們要測試子進程發送數據給父進程,則父進程須要關閉寫入端(pipefd[1]), * 而子進程須要關閉讀取端(pipefd[0]) */ else if (pid > 0) // this is parent process { char msg[MAX_MSG_SIZE] = {0}; close(pipefd[1]); read(pipefd[0], msg, MAX_MSG_SIZE); printf("we recved : %s.\n", msg); } else if (pid == 0) { char* wmsg = "hello verybody!!!"; close(pipefd[0]); write(pipefd[1], wmsg, strlen(wmsg)); } printf("fuck, game over! pid=%d\n", getpid()); return 0; }
執行結果
root@iZbp1anc6yju2dks3nw5j0Z:~/test/ipc# ./pipe fuck, game over! pid=14921 we recved : hello verybody!!!. fuck, game over! pid=14920
FIFO,也稱爲命名管道,它是一種文件類型。
#include <sys/stat.h> // 返回值:成功返回0,出錯返回-1 int mkfifo(const char *pathname, mode_t mode);
其中的 mode 參數與open函數中的 mode 相同。一旦建立了一個 FIFO,就能夠用通常的文件I/O函數操做它。
當 open 一個FIFO時,是否設置非阻塞標誌(O_NONBLOCK)的區別:
FIFO的通訊方式相似於在進程中使用文件來傳輸數據,只不過FIFO類型文件同時具備管道的特性。在數據讀出時,FIFO管道中同時清除數據,而且「先進先出」。下面的例子演示了使用 FIFO 進行 IPC 的過程:
fifo_read.c
#include <sys/stat.h> #include <unistd.h> #include <stdio.h> #include <errno.h> #include <sys/types.h> #include <fcntl.h> #include <string.h> #define FIFO_FILE_PATH "/tmp/fifo_test" #define MAX_FIFO_MSG_SIZE 1024 int main(int argc, char** argv) { if (-1 == access(FIFO_FILE_PATH, F_OK)) { printf("fifo file is not exist, and try to create fifo file.\n"); } int fd = open(FIFO_FILE_PATH, O_RDONLY); char buffer[MAX_FIFO_MSG_SIZE] = {0}; int len = read(fd, buffer, MAX_FIFO_MSG_SIZE); if ( len <= 0 ) { printf("read fifo file return nothing.\n"); return -1; } printf("read msg result is : %s.\n", buffer); return 0; }
fifo_write.c
#include <sys/stat.h> #include <unistd.h> #include <stdio.h> #include <errno.h> #include <sys/types.h> #include <fcntl.h> #include <string.h> #define FIFO_FILE_PATH "/tmp/fifo_test" int main(int argc, char** argv) { if (-1 == access(FIFO_FILE_PATH, F_OK)) { printf("fifo file is not exist, and try to create fifo file.\n"); if (0 != mkfifo(FIFO_FILE_PATH, 0777)) { printf("create fifo file failed with error : %s\n", strerror(errno)); return -1; } } /* * 以只寫方式打開FIFO文件,不能以讀寫方式打開FIFO,固然咱們也能夠設置爲O_WRONLY | O_NONBLOCK,那麼此時是 * 以非阻塞的方式打開文件,對於非阻塞,咱們講網絡編程的時候會講到 */ int fd = open(FIFO_FILE_PATH, O_WRONLY); if (-1 == fd) { printf("open fifo file failed.\n"); return -1; } char* msg = "sexy lady, what's your LINE number.\n"; int len = strlen(msg); if( write(fd, msg, len) < len) { printf("only write "); } for(;;); return 0; }
運行結果:
root@iZbp1anc6yju2dks3nw5j0Z:~/test/ipc# ./fifo_write
root@iZbp1anc6yju2dks3nw5j0Z:~/test/ipc# ./fifo_read read msg result is : sexy lady, what's your LINE number.
上述例子能夠擴展成 客戶進程—服務器進程 通訊的實例,write_fifo的做用相似於客戶端,能夠打開多個客戶端向一個服務器發送請求信息,read_fifo相似於服務器,它適時監控着FIFO的讀端,當有數據時,讀出並進行處理,可是有一個關鍵的問題是,每個客戶端必須預先知道服務器提供的FIFO接口,下圖顯示了這種安排:
消息隊列,是消息的連接表,存放在內核中。一個消息隊列由一個標識符(即隊列ID)來標識。
1.消息隊列是面向記錄的,其中的消息具備特定的格式以及特定的優先級。
2.消息隊列獨立於發送與接收進程。進程終止時,消息隊列及其內容並不會被刪除。
3.消息隊列能夠實現消息的隨機查詢,消息不必定要以先進先出的次序讀取,也能夠按消息的類型讀取。
#include <sys/msg.h> // 建立或打開消息隊列:成功返回隊列ID,失敗返回-1 int msgget(key_t key, int flag); // 添加消息:成功返回0,失敗返回-1 int msgsnd(int msqid, const void *ptr, size_t size, int flag); // 讀取消息:成功返回消息數據的長度,失敗返回-1 int msgrcv(int msqid, void *ptr, size_t size, long type,int flag); // 控制消息隊列:成功返回0,失敗返回-1 int msgctl(int msqid, int cmd, struct msqid_ds *buf);
在如下兩種狀況下,msgget將建立一個新的消息隊列:
函數msgrcv在讀取消息隊列時,type參數有下面幾種狀況:
能夠看出,type值非 0 時用於以非先進先出次序讀消息。也能夠把 type 看作優先級的權值。
下面寫了一個簡單的使用消息隊列進行IPC的例子,服務端程序一直在等待特定類型的消息,當收到該類型的消息之後,發送另外一種特定類型的消息做爲反饋,客戶端讀取該反饋並打印出來。
msg_server.c
#include <stdio.h> #include <stdlib.h> #include <sys/msg.h> // 用於建立一個惟一的key #define MSG_FILE "/etc/passwd" // 消息結構 struct msg_form { long mtype; char mtext[256]; }; int main() { int msqid; key_t key; struct msg_form msg; // 獲取key值 if((key = ftok(MSG_FILE,'z')) < 0) { perror("ftok error"); exit(1); } // 打印key值 printf("Message Queue - Server key is: %d.\n", key); // 建立消息隊列 if ((msqid = msgget(key, IPC_CREAT|0777)) == -1) { perror("msgget error"); exit(1); } // 打印消息隊列ID及進程ID printf("My msqid is: %d.\n", msqid); printf("My pid is: %d.\n", getpid()); // 循環讀取消息 for(;;) { msgrcv(msqid, &msg, 256, 888, 0);// 返回類型爲888的第一個消息 printf("Server: receive msg.mtext is: %s.\n", msg.mtext); printf("Server: receive msg.mtype is: %d.\n", msg.mtype); msg.mtype = 999; // 客戶端接收的消息類型 sprintf(msg.mtext, "hello, I'm server %d", getpid()); msgsnd(msqid, &msg, sizeof(msg.mtext), 0); } return 0; }
msg_client.c
#include <stdio.h> #include <stdlib.h> #include <sys/msg.h> // 用於建立一個惟一的key #define MSG_FILE "/etc/passwd" // 消息結構 struct msg_form { long mtype; char mtext[256]; }; int main() { int msqid; key_t key; struct msg_form msg; // 獲取key值 if ((key = ftok(MSG_FILE, 'z')) < 0) { perror("ftok error"); exit(1); } // 打印key值 printf("Message Queue - Client key is: %d.\n", key); // 打開消息隊列 if ((msqid = msgget(key, IPC_CREAT|0777)) == -1) { perror("msgget error"); exit(1); } // 打印消息隊列ID及進程ID printf("My msqid is: %d.\n", msqid); printf("My pid is: %d.\n", getpid()); // 添加消息,類型爲888 msg.mtype = 888; sprintf(msg.mtext, "hello, I'm client %d", getpid()); msgsnd(msqid, &msg, sizeof(msg.mtext), 0); // 讀取類型爲777的消息 msgrcv(msqid, &msg, 256, 999, 0); printf("Client: receive msg.mtext is: %s.\n", msg.mtext); printf("Client: receive msg.mtype is: %d.\n", msg.mtype); return 0; }
信號量(semaphore)與已經介紹過的 IPC 結構不一樣,它是一個計數器。信號量用於實現進程間的互斥與同步,而不是用於存儲進程間通訊數據。
2.信號量基於操做系統的 PV 操做,程序對信號量的操做都是原子操做。
3.每次對信號量的 PV 操做不只限於對信號量值加 1 或減 1,並且能夠加減任意正整數。
4.支持信號量組。
最簡單的信號量是隻能取 0 和 1 的變量,這也是信號量最多見的一種形式,叫作二值信號量(Binary Semaphore)。而能夠取多個正整數的信號量被稱爲通用信號量。
Linux 下的信號量函數都是在通用的信號量數組上進行操做,而不是在一個單一的二值信號量上進行操做。
#include <sys/sem.h> // 建立或獲取一個信號量組:若成功返回信號量集ID,失敗返回-1 int semget(key_t key, int num_sems, int sem_flags); // 對信號量組進行操做,改變信號量的值:成功返回0,失敗返回-1 int semop(int semid, struct sembuf semoparray[], size_t numops); // 控制信號量的相關信息 int semctl(int semid, int sem_num, int cmd, ...);
當semget建立新的信號量集合時,必須指定集合中信號量的個數(即num_sems),一般爲1; 若是是引用一個現有的集合,則將num_sems指定爲 0 。
在semop函數中,sembuf結構的定義以下:
struct sembuf { short sem_num; // 信號量組中對應的序號,0~sem_nums-1 short sem_op; // 信號量值在一次操做中的改變量 short sem_flg; // IPC_NOWAIT, SEM_UNDO }
其中 sem_op 是一次操做中的信號量的改變量:
若sem_op < 0,請求 sem_op 的絕對值的資源。
當相應的資源數不能知足請求時,這個操做與sem_flg有關。
sem_flg 沒有指定IPC_NOWAIT,則將該信號量的semncnt值加1,而後進程掛起直到下述狀況發生:
若sem_op == 0,進程阻塞直到信號量的相應值爲0:
若是信號量的值不爲0,則依據sem_flg決定函數動做:
sem_flg沒有指定IPC_NOWAIT,則將該信號量的semncnt值加1,而後進程掛起直到下述狀況發生:
在semctl函數中的命令有多種,這裏就說兩個經常使用的:
sem_data.h
#ifndef MUTIPROCESS_SHM_DATA_H__ #define MUTIPROCESS_SHM_DATA_H__ #define MAX_MSG_SIZE 1024 #define SHARED_BUFFER_KEY 1234 #define SEM_KEY 5678 struct shared_buffer_t { int written; /* 標誌,0 : 可寫, 非0:可讀 */ char buffer[MAX_MSG_SIZE]; }; union semun { int val; struct semid_ds *buf; unsigned short *arry; }; #endif
//sem_read.cpp #include <sys/ipc.h> #include <sys/shm.h> #include <stdio.h> #include <errno.h> #include <unistd.h> #include <string.h> #include <sys/types.h> #include <sys/sem.h> #include "sem_data.h" int main(int argc, char **argv) { int shared_buffer_id = shmget(SHARED_BUFFER_KEY, sizeof(shared_buffer_t), 0666 | IPC_CREAT); if (shared_buffer_id == -1) { printf("create shared buffer failed with error = %s.\n", strerror(errno)); return -1; } /* 將建立的共享內存映射到當前進程的地址空間 */ void *shared_buffer_ptr = shmat(shared_buffer_id, (void *)0, 0); if ((void *)-1 == shared_buffer_ptr) { printf("attaches shared buffer failed with error=%s.\n", strerror(errno)); return -1; } shared_buffer_t *shared = (shared_buffer_t *)shared_buffer_ptr; /* 通知對端,我已經能夠讀數據了,你如今能夠寫入數據了 */ //建立信號量 int sem_id = semget(SEM_KEY, 1, 0666 | IPC_CREAT); if (sem_id == -1) { printf("create sem failed with error=%s.", strerror(errno)); return -1; } //初始化信號量 union semun sem_union; sem_union.val = 0; if (semctl(sem_id, 0, SETVAL, sem_union) == -1) { printf("init sem failed with error=%s.", strerror(errno)); // 刪除信號量 semctl(sem_id, 0, IPC_RMID, sem_union); return -1; } int index = 0; while (1) { // 等待信號量 struct sembuf sem_buffer; sem_buffer.sem_num = 0; sem_buffer.sem_op = -1; sem_buffer.sem_flg = SEM_UNDO; if (semop(sem_id, &sem_buffer, 1) == -1) { printf("wait sem failed with error=%s.\n", strerror(errno)); return -1; } // 若是非0,則能夠讀取內容 if (0 != shared->written) { char msg[MAX_MSG_SIZE] = {0}; strcpy(msg, shared->buffer); shared->written = 0; printf("recved content is : %s, index = %d.\n", msg, index); index++; shared->written = 0; } // 發送信號量,離開臨界區 sem_buffer.sem_op = 1; if (semop(sem_id, &sem_buffer, 1) == -1) { printf("leave sem failed with error=%s\n", strerror(errno)); return -1; } } //把共享內存從當前進程中分離 if (shmdt(shared_buffer_ptr) == -1) { fprintf(stderr, "dettaches shared buffer failed.\n"); return -1; } //刪除共享內存 if (shmctl(shared_buffer_id, IPC_RMID, 0) == -1) { fprintf(stderr, "remove shared buffer failed.\n"); return -1; } return 0; }
//sem_write.cpp #include <sys/ipc.h> #include <sys/shm.h> #include <stdio.h> #include <errno.h> #include <unistd.h> #include <string.h> #include <sys/sem.h> #include <sys/types.h> #include "sem_data.h" int main(int argc, char **argv) { int shared_buffer_id = shmget(SHARED_BUFFER_KEY, sizeof(shared_buffer_t), 0666 | IPC_CREAT); if (shared_buffer_id == -1) { printf("create shared buffer failed with error = %s.\n", strerror(errno)); return -1; } /* 將建立的共享內存映射到當前進程的地址空間 */ void *shared_buffer_ptr = shmat(shared_buffer_id, (void *)0, 0); if ((void *)-1 == shared_buffer_ptr) { printf("attaches shared buffer failed with error=%s.\n", strerror(errno)); return -1; } //建立信號量 int sem_id = semget(SEM_KEY, 1, 0666 | IPC_CREAT); if (sem_id == -1) { printf("create sem failed with error=%s.", strerror(errno)); return -1; } //初始化信號量 semun sem_union; sem_union.val = 1; if (semctl(sem_id, 0, SETVAL, sem_union) == -1) { printf("init sem failed with error=%s.", strerror(errno)); // 刪除信號量 semctl(sem_id, 0, IPC_RMID, sem_union); return -1; } pid_t pid = 0; // 開啓多個進程 for (int i = 0; i < 4; i++) { pid_t pid = fork(); if (pid == 0) { break; } } // 父進程 if (pid > 0) { for (;;) ; } while (1) { //等待信號量, 即執行P操做 struct sembuf sem_buffer; sem_buffer.sem_num = 0; sem_buffer.sem_op = -1; sem_buffer.sem_flg = SEM_UNDO; if (semop(sem_id, &sem_buffer, 1) == -1) { printf("wait sem failed with error=%s.\n", strerror(errno)); return -1; } shared_buffer_t *shared = (shared_buffer_t *)shared_buffer_ptr; char *msg = "lee ge stay alone, please contact me with my LINE : leege.\n"; strcpy(shared->buffer, msg); shared->written = 1; // 已經寫完,離開臨界區 sem_buffer.sem_op = 1; if (semop(sem_id, &sem_buffer, 1) == -1) { printf("leave sem failed with error=%s\n", strerror(errno)); return -1; } usleep(100000); /*100 毫秒*/ } //把共享內存從當前進程中分離 if (shmdt(shared_buffer_ptr) == -1) { fprintf(stderr, "dettaches shared buffer failed.\n"); return -1; } //釋放信號量 if (semctl(sem_id, 0, IPC_RMID, sem_union) == -1) { printf("release sem failed with error=%s.\n", strerror(errno)); return -1; } return 0; }
共享內存(Shared Memory),指兩個或多個進程共享一個給定的存儲區。
特色
#include <sys/shm.h> // 建立或獲取一個共享內存:成功返回共享內存ID,失敗返回-1 int shmget(key_t key, size_t size, int flag); // 鏈接共享內存到當前進程的地址空間:成功返回指向共享內存的指針,失敗返回-1 void *shmat(int shm_id, const void *addr, int flag); // 斷開與共享內存的鏈接:成功返回0,失敗返回-1 int shmdt(void *addr); // 控制共享內存的相關信息:成功返回0,失敗返回-1 int shmctl(int shm_id, int cmd, struct shmid_ds *buf);
當用shmget函數建立一段共享內存時,必須指定其 size;而若是引用一個已存在的共享內存,則將 size 指定爲0 。
當一段共享內存被建立之後,它並不能被任何進程訪問。必須使用shmat函數鏈接該共享內存到當前進程的地址空間,鏈接成功後把共享內存區對象映射到調用進程的地址空間,隨後可像本地空間同樣訪問。
shmdt函數是用來斷開shmat創建的鏈接的。注意,這並非從系統中刪除該共享內存,只是當前進程不能再訪問該共享內存而已。
shmctl函數能夠對共享內存執行多種操做,根據參數 cmd 執行相應的操做。經常使用的是IPC_RMID(從系統中刪除該共享內存)。
下面這個例子,使用了【共享內存+信號量+消息隊列】的組合來實現服務器進程與客戶進程間的通訊。
//server.c #include<stdio.h> #include<stdlib.h> #include<sys/shm.h> // shared memory #include<sys/sem.h> // semaphore #include<sys/msg.h> // message queue #include<string.h> // memcpy // 消息隊列結構 struct msg_form { long mtype; char mtext; }; // 聯合體,用於semctl初始化 union semun { int val; /*for SETVAL*/ struct semid_ds *buf; unsigned short *array; }; // 初始化信號量 int init_sem(int sem_id, int value) { union semun tmp; tmp.val = value; if(semctl(sem_id, 0, SETVAL, tmp) == -1) { perror("Init Semaphore Error"); return -1; } return 0; } // P操做: // 若信號量值爲1,獲取資源並將信號量值-1 // 若信號量值爲0,進程掛起等待 int sem_p(int sem_id) { struct sembuf sbuf; sbuf.sem_num = 0; /*序號*/ sbuf.sem_op = -1; /*P操做*/ sbuf.sem_flg = SEM_UNDO; if(semop(sem_id, &sbuf, 1) == -1) { perror("P operation Error"); return -1; } return 0; } // V操做: // 釋放資源並將信號量值+1 // 若是有進程正在掛起等待,則喚醒它們 int sem_v(int sem_id) { struct sembuf sbuf; sbuf.sem_num = 0; /*序號*/ sbuf.sem_op = 1; /*V操做*/ sbuf.sem_flg = SEM_UNDO; if(semop(sem_id, &sbuf, 1) == -1) { perror("V operation Error"); return -1; } return 0; } // 刪除信號量集 int del_sem(int sem_id) { union semun tmp; if(semctl(sem_id, 0, IPC_RMID, tmp) == -1) { perror("Delete Semaphore Error"); return -1; } return 0; } // 建立一個信號量集 int creat_sem(key_t key) { int sem_id; if((sem_id = semget(key, 1, IPC_CREAT|0666)) == -1) { perror("semget error"); exit(-1); } init_sem(sem_id, 1); /*初值設爲1資源未佔用*/ return sem_id; } int main() { key_t key; int shmid, semid, msqid; char *shm; char data[] = "this is server"; struct shmid_ds buf1; /*用於刪除共享內存*/ struct msqid_ds buf2; /*用於刪除消息隊列*/ struct msg_form msg; /*消息隊列用於通知對方更新了共享內存*/ // 獲取key值 if((key = ftok(".", 'z')) < 0) { perror("ftok error"); exit(1); } // 建立共享內存 if((shmid = shmget(key, 1024, IPC_CREAT|0666)) == -1) { perror("Create Shared Memory Error"); exit(1); } // 鏈接共享內存 shm = (char*)shmat(shmid, 0, 0); if((int)shm == -1) { perror("Attach Shared Memory Error"); exit(1); } // 建立消息隊列 if ((msqid = msgget(key, IPC_CREAT|0777)) == -1) { perror("msgget error"); exit(1); } // 建立信號量 semid = creat_sem(key); // 讀數據 while(1) { msgrcv(msqid, &msg, 1, 888, 0); /*讀取類型爲888的消息*/ if(msg.mtext == 'q') /*quit - 跳出循環*/ break; if(msg.mtext == 'r') /*read - 讀共享內存*/ { sem_p(semid); printf("%s\n",shm); sem_v(semid); } } // 斷開鏈接 shmdt(shm); /*刪除共享內存、消息隊列、信號量*/ shmctl(shmid, IPC_RMID, &buf1); msgctl(msqid, IPC_RMID, &buf2); del_sem(semid); return 0; }
//client.c #include<stdio.h> #include<stdlib.h> #include<sys/shm.h> // shared memory #include<sys/sem.h> // semaphore #include<sys/msg.h> // message queue #include<string.h> // memcpy // 消息隊列結構 struct msg_form { long mtype; char mtext; }; // 聯合體,用於semctl初始化 union semun { int val; /*for SETVAL*/ struct semid_ds *buf; unsigned short *array; }; // P操做: // 若信號量值爲1,獲取資源並將信號量值-1 // 若信號量值爲0,進程掛起等待 int sem_p(int sem_id) { struct sembuf sbuf; sbuf.sem_num = 0; /*序號*/ sbuf.sem_op = -1; /*P操做*/ sbuf.sem_flg = SEM_UNDO; if(semop(sem_id, &sbuf, 1) == -1) { perror("P operation Error"); return -1; } return 0; } // V操做: // 釋放資源並將信號量值+1 // 若是有進程正在掛起等待,則喚醒它們 int sem_v(int sem_id) { struct sembuf sbuf; sbuf.sem_num = 0; /*序號*/ sbuf.sem_op = 1; /*V操做*/ sbuf.sem_flg = SEM_UNDO; if(semop(sem_id, &sbuf, 1) == -1) { perror("V operation Error"); return -1; } return 0; } int main() { key_t key; int shmid, semid, msqid; char *shm; struct msg_form msg; int flag = 1; /*while循環條件*/ // 獲取key值 if((key = ftok(".", 'z')) < 0) { perror("ftok error"); exit(1); } // 獲取共享內存 if((shmid = shmget(key, 1024, 0)) == -1) { perror("shmget error"); exit(1); } // 鏈接共享內存 shm = (char*)shmat(shmid, 0, 0); if((int)shm == -1) { perror("Attach Shared Memory Error"); exit(1); } // 建立消息隊列 if ((msqid = msgget(key, 0)) == -1) { perror("msgget error"); exit(1); } // 獲取信號量 if((semid = semget(key, 0, 0)) == -1) { perror("semget error"); exit(1); } // 寫數據 printf("***************************************\n"); printf("* IPC *\n"); printf("* Input r to send data to server. *\n"); printf("* Input q to quit. *\n"); printf("***************************************\n"); while(flag) { char c; printf("Please input command: "); scanf("%c", &c); switch(c) { case 'r': printf("Data to send: "); sem_p(semid); /*訪問資源*/ scanf("%s", shm); sem_v(semid); /*釋放資源*/ /*清空標準輸入緩衝區*/ while((c=getchar())!='\n' && c!=EOF); msg.mtype = 888; msg.mtext = 'r'; /*發送消息通知服務器讀數據*/ msgsnd(msqid, &msg, sizeof(msg.mtext), 0); break; case 'q': msg.mtype = 888; msg.mtext = 'q'; msgsnd(msqid, &msg, sizeof(msg.mtext), 0); flag = 0; break; default: printf("Wrong input!\n"); /*清空標準輸入緩衝區*/ while((c=getchar())!='\n' && c!=EOF); } } // 斷開鏈接 shmdt(shm); return 0; }
注意:當scanf()輸入字符或字符串時,緩衝區中遺留下了n,因此每次輸入操做後都須要清空標準輸入的緩衝區。可是因爲 gcc 編譯器不支持fflush(stdin)(它只是標準C的擴展),因此咱們使用了替代方案:
while((c=getchar())!='\n' && c!=EOF);
//shm_data.cpp #ifndef MUTIPROCESS_SHM_DATA_H__ #define MUTIPROCESS_SHM_DATA_H__ #define MAX_MSG_SIZE 1024 #define SHARED_BUFFER_KEY 1234 struct shared_buffer_t { int written; /* 標誌,0 : 可寫, 非0:可讀 */ char buffer[MAX_MSG_SIZE]; }; #endif
//shm_read.cpp #include <sys/ipc.h> #include <sys/shm.h> #include <stdio.h> #include <errno.h> #include <unistd.h> #include <string.h> #include "shm_data.h" int main(int argc, char** argv) { int shared_buffer_id = shmget(SHARED_BUFFER_KEY, sizeof(shared_buffer_t), 0666 | IPC_CREAT ); if (shared_buffer_id == -1) { printf("create shared buffer failed with error = %s.\n", strerror(errno)); return -1; } /* 將建立的共享內存映射到當前進程的地址空間 */ void* shared_buffer_ptr = shmat(shared_buffer_id, (void*)0, 0); if ((void*)-1 == shared_buffer_ptr) { printf("attaches shared buffer failed with error=%s.\n", strerror(errno)); return -1; } shared_buffer_t* shared = (shared_buffer_t*)shared_buffer_ptr; /* 設置共享內存爲可寫狀態 */ shared->written = 0; // 若是工程內存可寫,則等待 while (0 == shared->written) { sleep(1); } char msg[MAX_MSG_SIZE] = {0}; strcpy(msg, shared->buffer); shared->written = 0; printf("recved content is : %s.\n", msg); //把共享內存從當前進程中分離 if(shmdt(shared_buffer_ptr) == -1) { fprintf(stderr, "dettaches shared buffer failed.\n"); return -1; } //刪除共享內存 if(shmctl(shared_buffer_id, IPC_RMID, 0) == -1) { fprintf(stderr, "remove shared buffer failed.\n"); return -1; } return 0 ; }
//shm_write.cpp #include <sys/ipc.h> #include <sys/shm.h> #include <stdio.h> #include <errno.h> #include <unistd.h> #include <string.h> #include "shm_data.h" int main(int argc, char** argv) { int shared_buffer_id = shmget(SHARED_BUFFER_KEY, sizeof(shared_buffer_t), 0666 | IPC_CREAT ); if (shared_buffer_id == -1) { printf("create shared buffer failed with error = %s.\n", strerror(errno)); return -1; } /* 將建立的共享內存映射到當前進程的地址空間 */ void* shared_buffer_ptr = shmat(shared_buffer_id, (void*)0, 0); if ((void*)-1 == shared_buffer_ptr) { printf("attaches shared buffer failed with error=%s.\n", strerror(errno)); return -1; } shared_buffer_t* shared = (shared_buffer_t*)shared_buffer_ptr; // 若是不能夠寫,則阻塞住 while (0 != shared->written) { sleep(1); } const char* msg = "hello,world"; strcpy(shared->buffer, msg); shared->written = 1; //把共享內存從當前進程中分離 if(shmdt(shared_buffer_ptr) == -1) { fprintf(stderr, "dettaches shared buffer failed.\n"); return -1; } return 0 ; }
共享內存的方式在多線程或者多進程通訊時須要進行同步,這時使用臨界區,信號量進行實現同步。