進程間通訊簡介(摘自《Linux網絡編程》p85)linux
AT&T 在 UNIX System V 中引入了幾種新的進程通信方式,即消息隊列( MessageQueues),信號量( semaphores)和共享內存( shared memory),統稱爲 System V IPC。在Linux 系統編程中,它們有着普遍的應用。
System V IPC 的一個顯著的特色,是它的具體實例在內核中是以對象的形式出現的,咱們稱之爲 IPC 對象。每一個 IPC 對象在系統內核中都有一個惟一的標識符。經過標識符內核能夠正確的引用指定的 IPC 對象.。須要注意的是,標識符的惟一性只在每一類的 IPC 對象內成立。好比說,一個消息隊列和一個信號量的標識符多是相同的,但絕對不會出現兩個有相同標識符的消息隊列。編程
標識符只在內核中使用, IPC 對象在程序中是經過關鍵字( key)來訪問的。和 IPC 對象標識符同樣,關鍵字也必須是惟一的。並且,要訪問同一個 IPC 對象, Server 和 Client必須使用同一個關鍵字。所以,如何構造新的關鍵字使之不和已有的關鍵字衝突,並保證Server 和 Client 使用的關鍵字是相同的,是創建 IPC 對象時首先要解決的一個問題。(具體在後邊的msg通訊中詳解)數組
通訊方法還有:半雙工管道pipe,命名管道fifo,消息隊列,信號量,共享內,socket套接字等,下面一一介紹:網絡
①半雙工管道:異步
int pipe(int filedes[2]);socket
管道是將兩個進程之間的標準輸入輸出相互對接的機制函數
linux命令中使用的管道 | : ls -l | grep *.c //顯示文件(輸入端)-(|)-(輸出端)>找到.c結尾文件ui
實現:由於半雙工緣故,因此只能實現一段輸入,一段輸出,而不能雙向通訊。因此:實現爲,經過管道鏈接進程,一端開放讀文件描述,一端開放寫文件描述spa
//管道的性質就是,一個進程的輸出做爲另外一個進程的輸入 //那麼咱們能夠關閉一個進程讀端使之做爲輸入端, //另外一個進程關閉寫端,讀取數據,接收數據做爲管道輸出端 //FIFO命名管道 //文件系統中,命名管道是特殊文件的方式存在的 //不一樣進程能夠經過命名管道共享數據 //命名管道一直是阻塞方式的,且必須是顯示的經過open創建鏈接到管道的通道 #include<stdio.h> #include<stdlib.h> #include<string.h> #include<unistd.h> #include<sys/types.h> int main() { int result = 1; int fd[2]; pid_t pid; int *write_fd = &fd[1]; //寫文件描述 int *read_fd = &fd[0]; //讀文件描述 int nbytes; char str[] = "管道,你好\n"; char readBuffer[80]; memset(readBuffer,0,sizeof(readBuffer)); result = pipe(fd); //建立管道 if(-1==result) { printf("管道建立失敗!\n"); return -1; } pid = fork(); //進程建立分叉程序 if(-1 == pid) { printf("fork失敗"); return -1; } if(0==pid) //子進程關閉讀端,寫入字符 { close(*read_fd); result = write(*write_fd,str,strlen(str)); printf("寫入%d個數據\n",result); } else //父進程關閉寫端,讀取數據 { close(*write_fd); nbytes = read(*read_fd,readBuffer,sizeof(readBuffer)); printf("接收到%d個數據,內容爲%s",nbytes,readBuffer); } return 0; }
②命名管道code
int mkfifo(const char* pathname,mode_t mode);
相似於普通管道,只是
a.在文件系統中以設備特殊文件的形式存在
b.不一樣進程之間能夠經過命名管道共享數據
操做區別於普通管道:FIFO中必須顯式經過open創建鏈接到管道的通道,且老是處於阻塞狀態的
③消息隊列
消息隊列是內核地址空間的內部鏈表,經過內核在各個進程之間傳遞內容。每一個消息隊列經過惟一IPC標識符標識,不一樣隊列相對獨立。
//file: msg.h
/* message buffer for msgsnd and msgrcv calls */ struct msgbuf { __kernel_long_t mtype; /* type of message */ char mtext[1]; /* message text */ }; /* Obsolete, used only for backwards compatibility and libc5 compiles */ struct msqid_ds { struct ipc_perm msg_perm; struct msg *msg_first; /* first message on queue,unused */ struct msg *msg_last; /* last message in queue,unused */ __kernel_time_t msg_stime; /* last msgsnd time */ __kernel_time_t msg_rtime; /* last msgrcv time */ __kernel_time_t msg_ctime; /* last change time */ unsigned long msg_lcbytes; /* Reuse junk fields for 32 bit */ unsigned long msg_lqbytes; /* ditto */ unsigned short msg_cbytes; /* current number of bytes on queue */ unsigned short msg_qnum; /* number of messages in queue */ unsigned short msg_qbytes; /* max number of bytes on queue */ __kernel_ipc_pid_t msg_lspid; /* pid of last msgsnd */ __kernel_ipc_pid_t msg_lrpid; /* last receive pid */ };
//filename
/* Obsolete, used only for backwards compatibility and libc5 compiles */ struct ipc_perm { __kernel_key_t key; //函數msgget()使用的鍵值 __kernel_uid_t uid; //用戶UID __kernel_gid_t gid; //用戶GID __kernel_uid_t cuid; //建立者UID __kernel_gid_t cgid; //建立者GID __kernel_mode_t mode; //權限 unsigned short seq; //序列號 };
內核中的消息隊列
注:結構list_head 造成一個鏈表,結構msg_msg之中的m_list使得消息造成鏈表,查找,插入時,對m_list域進行偏移找到位置
相關函數:
鍵值構建 key_t ftok(const char* pathname,int proj_id);
獲取消息 int msgget(key_t key,int msgflg);
發送消息 int msgsnd(int msqid, const void * msgp,size_t msgsz,int msgflg);
接收消息 ssize_t msgrcv(int msqid, void * msgp, size_t msgsz, long msgtype, int msgflg);
消息控制 int msgctl(int msqid, int cmd, struct msqid_ds *buf); //向內核發送cmd命令判斷進行何種操做
一個簡單例子
④信號量
信號量是一種計數器,用來控制對多個進程共享的資源所進行的訪問。經常使用做鎖機制(生產者消費者模型是個典型使用)
信號量結構
//filename sys/sem.h
/* arg for semctl system calls. */ union semun { int val; /* value for SETVAL */ struct semid_ds *buf; /* buffer for IPC_STAT & IPC_SET */ unsigned short *array; /* 數組結構 */ struct seminfo *__buf; /* 信號量內部結構 */ void *__pad; };
相關函數
新建信號量 int semget(key_t key, int nsems, int semflg);
//key 來自於ftok()
信號量操做函數 int semop(int semid,struct sembuf* sops, unsigned nsops);
//信號量的P,V操做經過向已經創建好的信號量發送命令完成
控制信號量參數
int semctl(int semid, int semnum ,int cmd,.....);
//用於在信號量集合上執行控制操做
#include<stdio.h> #include<unistd.h> #include<sys/ipc.h> #include<sys/sem.h> #include<sys/types.h> typedef int sem_t; union semun { int val; struct semid_ds * buf; unsigned short *array; }arg; sem_t CreateSem(key_t key, int value) { union semun sem; sem_t semid; sem.val = value; semid = semget(key,0,IPC_CREAT); if(-1 == semid) { printf("create semaphore error\n"); return -1; } semctl(semid,0,SETVAL,sem); return semid; } int Sem_P(sem_t semid) { struct sembuf sops = {0,+1,IPC_NOWAIT}; return (semop(semid,&sops,1)); } int Sem_V(sem_t semid) { struct sembuf sops = {0,-1,IPC_NOWAIT}; return (semop(semid,&sops,1)); } void SetvalueSem(sem_t semid , int value) { union semun sem; sem.val = value; semctl(semid,0,SETVAL,sem); } int GetvalueSem(sem_t semid) { union semun sem; return semctl(semid,0,GETVAL,sem); } void DestroySem(sem_t semid) { union semun sem; sem.val = 0; semctl(semid,0,IPC_RMID,sem); } int main() { key_t key; int semid; char i; int value = 0; key = ftok("/ipc/sem",'a'); semid = CreateSem(key,100); for( i = 0;i <= 3;++i) { Sem_P(semid); Sem_V(semid); } value = GetvalueSem(semid); DestroySem(semid); return 0; }
⑤共享內存(最快捷的方法)沒有中間過程,管道等
在多個進程之間共享內存區域的一種進程間通訊方式,在多個進程之間對內存段進行映射的方式實現內存共享。
相關函數
建立共享內存函數 int shmget(key_y key, size_t size, int shmflg);
得到共享內存地址void * shmat(int shmid,const void* shmaddr, int shmflg);
刪除共享內存函數 int shmdt(const void* shmadddr);
共享內存控制函數 int shmctl(int shmid ,int cmd, struct shmid_ds * buf);
⑥信號
用於在一個或多個進程之間傳遞異步信號。
相關函數
信號截取 sighandler signal(int signum ,sighandler handler);
發送信號 int kill(pid_t pid, int sig);
int raise(int sig);