Linux下进程间通信方式之管道、信号、共享内存、消息队列、信号量、套接字
生活随笔
收集整理的這篇文章主要介紹了
Linux下进程间通信方式之管道、信号、共享内存、消息队列、信号量、套接字
小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.
/*
1,進(jìn)程間通信 (IPC ) Inter-Process Communication比較好理解概念的就是進(jìn)程間通信就是在不同進(jìn)程之間傳播或交換信息。2,linux下IPC機(jī)制的分類:管道、信號、共享內(nèi)存、消息隊(duì)列、信號量、套接字3,這篇主要說說管道:本質(zhì)是文件,其他理論什么的網(wǎng)上已經(jīng)有一大堆了,我就只寫一點(diǎn)用法吧。3.1 特點(diǎn)1)管道是最古老的IPC,但目前很少使用2)以文件做交互的媒介,管道分為有名管道和無名管道3)歷史上的管道通常是指半雙工管道3.2 管道:有兩種形式,命令行和非命令行(1)命令行:mkfifo testfifoecho "testfifo" >fifocat fifo(2)非命令行:這里又分有名管道和無名管道編程模型:進(jìn)程A創(chuàng)建管道(mkfifo) -> 進(jìn)程A寫打開管道(open) -> 進(jìn)程B讀打開管道(open) -> 進(jìn)程A開始往管道里寫數(shù)據(jù)(write) ->進(jìn)程B從管道中讀數(shù)據(jù)(read) -> 進(jìn)程A關(guān)閉管道(close) -> 進(jìn)程B關(guān)閉管道(close) -> 刪除管道(unlink)
*/
//有名管道(實(shí)例):
//進(jìn)程A:#include<sys/stat.h>
#include<fcntl.h>
#include<stdio.h>#define PIPENAME "pipetest"int main()
{// 創(chuàng)建管道if(mkfifo(PIPENAME, 0666) < 0){perror("mkfifo");return -1;}// 寫打開管道 int fd = open(PIPENAME, O_WRONLY);if(-1 == fd){perror("open");return -1;}unlink(PIPENAME);int i = 0;for(i = 0; i < 10; i++){write(fd, &i, sizeof(i));printf("%d\n", i);sleep(1); // 這個(gè)是以秒為單位掛起}// 關(guān)閉管道close(fd);return 0;}//進(jìn)程B:#include<sys/stat.h>
#include<fcntl.h>
#include<stdio.h>#define PIPENAME "pipetest"int main()
{// 讀打開管道int fd = open(PIPENAME, O_RDONLY);if(-1 == fd){perror("open");return -1;}int num = 0;int i = 0;for(i = 0; i < 10; i++){read(fd, &num, sizeof(int));printf("%d\n", num);fflush(stdout); // 強(qiáng)制刷新輸出緩沖區(qū)}printf("\n");close(fd);return 0;}/*
一,消息隊(duì)列1,概念:“消息隊(duì)列”是在消息的傳輸過程中保存消息的容器2,消息隊(duì)列就是一個(gè)消息的鏈表。可以把消息看作一個(gè)記錄,具有特定的格式以及特定的優(yōu)先級。對消息隊(duì)列有寫權(quán)限的進(jìn)程可以向消息隊(duì)列中按照一定的規(guī)則添加新消息;對消息隊(duì)列有讀權(quán)限的進(jìn)程則可以從消息隊(duì)列中讀走消息。消息隊(duì)列是隨內(nèi)核持續(xù)的。3,編程注意事項(xiàng):使用時(shí)先把數(shù)據(jù)封裝成消息,把消息存入隊(duì)列編程步驟: 具體函數(shù)的用法可以用man手冊查看(強(qiáng)力推薦)(1)ftok()生產(chǎn)key(2)使用msgget( ) 創(chuàng)建/獲取消息隊(duì)列,返回值是隊(duì)列標(biāo)識符(3)使用msgsnd( ) 發(fā)送消息使用msgrcv( ) 接收消息(4)使用msgctl( ) 刪除消息隊(duì)列
4,實(shí)例:sendmsg.c 用來發(fā)送消息的
*/
// sendmsg.c#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<sys/types.h>
#include<sys/ipc.h>
#include<sys/msg.h>
#include<string.h>struct my_msg
{int mtype; // 消息類型char buf[256];
}msg1, msg2;int main()
{key_t key = ftok("./", 88);int msgid = msgget(key, 0666|IPC_CREAT);if(-1 == msgid){perror("msgget failed!!!");exit(1);}msg1.mtype = 2;strcpy(msg1.buf, "hello, msg2");msgsnd(msgid, &msg1, sizeof(msg1), 0); // 阻塞// msgsnd(msgid, &msg1, sizeof(msg1), IPC_NOWAIT); // 非阻塞msg2.mtype = 1;strcpy(msg2.buf, "hello, msg1");msgsnd(msgid, &msg2, sizeof(msg2), 0); // 阻塞printf("消息發(fā)送完成,按回車銷毀消息隊(duì)列\(zhòng)n");getchar();if(-1 == shmctl(msgid, IPC_RMID, NULL)){perror("shmctl failed");exit(2);}return 0;
}//recvmsg.c 用來接收消息的// recvmsg.c#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<sys/types.h>
#include<sys/ipc.h>
#include<sys/msg.h>
#include<string.h>struct my_msg
{int mtype; // 消息類型char buf[256];
}msg;int main()
{key_t key = ftok("./", 88);// 獲取消息隊(duì)列int msgid = msgget(key, 0);if(-1 == msgid){perror("msgget failed!!!");exit(1);}int res = msgrcv(msgid, &msg, sizeof(msg),2, // 取消息類型為2的消息0);printf("類型:%d, 內(nèi)容:%s\n", msg.mtype, msg.buf);printf("消息接收完成,按回車銷毀消息隊(duì)列\(zhòng)n");getchar();if(-1 == shmctl(msgid, IPC_RMID, NULL)){perror("shmctl failed");exit(2);}return 0;
}/*
一,共享內(nèi)存
內(nèi)核管理一片物理內(nèi)存,允許不同的進(jìn)程同時(shí)映射,多個(gè)進(jìn)程可以映射同一塊內(nèi)存,被多個(gè)進(jìn)程同時(shí)映射的物理內(nèi)存,即共享內(nèi)存。
映射物理內(nèi)存叫掛接,用完以后解除映射叫脫接。1,共享內(nèi)存的特點(diǎn):優(yōu)點(diǎn):是最快的IPC。缺點(diǎn):要編程者自己實(shí)現(xiàn)對共享內(nèi)存互斥訪問。如何實(shí)現(xiàn)?2,編程模型:具體函數(shù)的用法可以用man手冊查看(強(qiáng)力推薦)進(jìn)程A: writeshm.c1) 獲得key, ftok()2) 使用key來創(chuàng)建一個(gè)共享內(nèi)存 shmget()3) 映射共享內(nèi)存(得到虛擬地址), shmat()4) 使用共享內(nèi)存, 往共享內(nèi)存中寫入數(shù)據(jù)5) 解除映射 shmdt()6) 如果共享內(nèi)存不再使用,可以使用shmctl()銷毀共享內(nèi)存進(jìn)程B: readshm.c 1) 獲得key, ftok() 2) 使用key來獲得一個(gè)共享內(nèi)存 shmget() 3) 映射共享內(nèi)存(得到虛擬地址), shmat() 4) 使用共享內(nèi)存, 讀取共享內(nèi)存中的數(shù)據(jù) 5) 解除映射 shmdt()
3,實(shí)例*///進(jìn)程A:
// writeshm.c#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
#include<sys/types.h>
#include<sys/ipc.h>
#include<sys/shm.h>int main()
{// 生成一個(gè)keykey_t key = ftok("./", 66);// 創(chuàng)建共享內(nèi)存,返回一個(gè)idint shmid = shmget(key, 8, IPC_CREAT|0666|IPC_EXCL);if(-1 == shmid){perror("shmget failed");exit(1);}// 映射共享內(nèi)存,得到虛擬地址void *p = shmat(shmid, 0, 0);if((void*)-1 == p){perror("shmat failed");exit(2);}// 寫共享內(nèi)存int *pp = p;*pp = 0x12345678;*(pp + 1) = 0xffffffff;// 解除映射if(-1 == shmdt(p)){perror("shmdt failed");exit(3);}printf("解除映射成功,點(diǎn)擊回車銷毀共享內(nèi)存\n");getchar();// 銷毀共享內(nèi)存if(-1 == shmctl(shmid, IPC_RMID, NULL)){perror("shmctl failed");exit(4);}return 0;
}//進(jìn)程B:// readshm.c#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
#include<sys/types.h>
#include<sys/ipc.h>
#include<sys/shm.h>int main()
{// 生成一個(gè)keykey_t key = ftok("./", 66);// 獲取共享內(nèi)存,返回一個(gè)idint shmid = shmget(key, 0, 0);if(-1 == shmid){perror("shmget failed");exit(1);}// 映射共享內(nèi)存,得到虛擬地址void *p = shmat(shmid, 0, 0);if((void*)-1 == p){perror("shmat failed");exit(2);}// 讀共享內(nèi)存int x = *(int *)p;int y = *((int *)p + 1);printf("從共享內(nèi)存中都取了:0x%x 和 0x%x \n", x, y);// 解除映射if(-1 == shmdt(p)){perror("shmdt failed");exit(3);}return 0;
}/*
一、信號量1,信號量本質(zhì)是一個(gè)計(jì)數(shù)器,控制訪問共享資源的最大并行進(jìn)程總數(shù)。(和信號有很大的區(qū)別)2,信號量的使用主要是用來保護(hù)共享資源,使得資源在一個(gè)時(shí)刻只有一個(gè)進(jìn)程(線程)所擁有。
信號量的值為正的時(shí)候,說明它空閑。所測試的線程可以鎖定而使用它。若為0,說明它被占用,測試的線程要進(jìn)入睡眠隊(duì)列中,等待被喚醒。3,信號量分類:Linux提供兩種信號量:
(1) 內(nèi)核信號量,由內(nèi)核控制路徑使用
(2) 用戶態(tài)進(jìn)程使用的信號量,這種信號量又分為POSIX信號量和SYSTEMV信號量。
POSIX信號量又分為有名信號量和無名信號量。
有名信號量,其值保存在文件中, 所以它可以用于線程也可以用于進(jìn)程間的同步。無名信號量,其值保存在內(nèi)存中。干貨來源: http://blog.csdn.net/qinxiongxu/article/details/78305374,最簡單的信號量是只能取0和1的變量,這也是信號量最常見的一種形式,叫做二進(jìn)制信號量。而可以取多個(gè)正整數(shù)的信號量被稱為通用信號量。這里主要討論二進(jìn)制信號量。5,使用方法使用時(shí)給其一個(gè)初始值,假如該資源允許同時(shí)兩個(gè)進(jìn)程使用,初始值就設(shè)置為2,有一個(gè)進(jìn)程使用該資源計(jì)數(shù)-1(原子操作),有一個(gè)進(jìn)程放棄使用該資源計(jì)數(shù)+1(原子操作)。如果計(jì)數(shù)為0,不允許新的進(jìn)程來訪問資源,新的進(jìn)程阻塞等待,直到計(jì)數(shù)重新大于0解除阻塞。
如果有多個(gè)資源需要控制訪問,就需要多個(gè)信號量,把多個(gè)信號量存入數(shù)組中,這個(gè)數(shù)組就叫信號量集。二,編程實(shí)現(xiàn)參考: http://blog.csdn.net/ljianhui/article/details/10243617 其實(shí)就是用這篇博客的。這里用的是二進(jìn)制信號量,初始值是1,最多允許1個(gè)進(jìn)程獲取信號量。 這個(gè)例子采用兩個(gè)相同的程序往終端輸出字符,根據(jù)命令行參數(shù)加以區(qū)分。*/#include<unistd.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<stdlib.h>
#include<stdio.h>
#include<string.h>
#include<sys/sem.h>union semun
{int val;struct semid_ds *buf;unsigned short *arry;
};static int sem_id = 0;
static int set_semvalue();
static void del_semvalue();
static int semaphore_p();
static int semaphore_v();int main(int argc, char **argv)
{char message = 'x';int i = 0;// 創(chuàng)建信號量sem_id = semget((key_t)1234, 1, 0666|IPC_CREAT);if(argc > 1){// 程序第一次調(diào)用,初始化信號量if(!set_semvalue()){fprintf(stderr, "Failed Init semaphore\n");exit(EXIT_FAILURE);}// 設(shè)置輸出到屏幕中的信息message = argv[1][0];sleep(2);}for(i = 0; i < 10; i++){if(!semaphore_p()) // 進(jìn)入臨界區(qū){exit(EXIT_FAILURE);}printf("%c", message);fflush(stdout); // 清理緩沖區(qū)sleep(rand() % 3); // 休眠隨機(jī)時(shí)間printf("%c", message);fflush(stdout); if(!semaphore_v()) // 離開臨界區(qū){exit(EXIT_FAILURE);}sleep(rand() % 2); // 休眠隨機(jī)時(shí)間}sleep(10);printf("\n %d - finished\n", getpid());if(argc > 1){sleep(3);del_semvalue();}exit(EXIT_SUCCESS);
}// 初始化信號量
static int set_semvalue()
{union semun sem_union;sem_union.val = 1;if(-1 == semctl(sem_id, 0, SETVAL, sem_union)){return 0;}return 1;
}// 刪除信號量
static void del_semvalue()
{union semun sem_union;if(-1 == semctl(sem_id, 0, IPC_RMID, sem_union)){fprintf(stderr, "Failed delete semphore\n");}
}// 對信號量-1操作,即等待P(sv)
static int semaphore_p()
{struct sembuf sem_b;sem_b.sem_num = 0;sem_b.sem_op = -1; // P()sem_b.sem_flg = SEM_UNDO;if(-1 == semop(sem_id, &sem_b, 1)){fprintf(stderr, "Failed semaphore_p()\n");return 0;}return 1;
}// 釋放操作, +1, 發(fā)送信號V(sv)
static int semaphore_v()
{struct sembuf sem_b;sem_b.sem_num = 0;sem_b.sem_op = 1; // P()sem_b.sem_flg = SEM_UNDO;if(-1 == semop(sem_id, &sem_b, 1)){fprintf(stderr, "Failed semaphore_v()\n");return 0;}return 1;
}
/*
因?yàn)槊總€(gè)程序都在其進(jìn)入臨界區(qū)后和離開臨界區(qū)前打印一個(gè)字符,所以每個(gè)字符都應(yīng)該成對出現(xiàn)。
一個(gè)進(jìn)程在打印時(shí),會先執(zhí)行P操作,若沒有打印完,也就是沒有執(zhí)行V操作。另一個(gè)進(jìn)程要執(zhí)行打印,也要進(jìn)行P操作,這時(shí)候由于信號量的值為0,獲取信號量失敗,進(jìn)程只能掛起自己。等另一個(gè)程序釋放(V操作)才能打印。
任何時(shí)刻只有一個(gè)進(jìn)程得到了信號量,只有一個(gè)進(jìn)程在執(zhí)行打印
總結(jié):
信號量是一個(gè)特殊的變量,程序?qū)ζ湓L問都是原子操作,且只允許對它進(jìn)行等待(即P(信號變量))和發(fā)送(即V(信號變量))信息操作。
我們通常通過信號來解決多個(gè)進(jìn)程對同一資源的訪問競爭的問題,使在任一時(shí)刻只能有一個(gè)執(zhí)行線程訪問代碼的臨界區(qū)域,
也可以說它是協(xié)調(diào)進(jìn)程間的對同一資源的訪問權(quán),也就是用于同步進(jìn)程的。
*/
總結(jié)
以上是生活随笔為你收集整理的Linux下进程间通信方式之管道、信号、共享内存、消息队列、信号量、套接字的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: win32下进程间通信方式之管道、邮件槽
- 下一篇: 高速缓存系统之redis c++使用实例