Linux网络编程--进程间通信(一)
進程間通信簡介(摘自《Linux網(wǎng)絡編程》p85)
AT&T 在 UNIX System V 中引入了幾種新的進程通訊方式,即消息隊列( MessageQueues),信號量( semaphores)和共享內(nèi)存( shared memory),統(tǒng)稱為 System V IPC。在Linux 系統(tǒng)編程中,它們有著廣泛的應用。
System V IPC 的一個顯著的特點,是它的具體實例在內(nèi)核中是以對象的形式出現(xiàn)的,我們稱之為 IPC 對象。每個 IPC 對象在系統(tǒng)內(nèi)核中都有一個唯一的標識符。通過標識符內(nèi)核可以正確的引用指定的 IPC 對象.。需要注意的是,標識符的唯一性只在每一類的 IPC 對象內(nèi)成立。比如說,一個消息隊列和一個信號量的標識符可能是相同的,但絕對不會出現(xiàn)兩個有相同標識符的消息隊列。
標識符只在內(nèi)核中使用, IPC 對象在程序中是通過關(guān)鍵字( key)來訪問的。和 IPC 對象標識符一樣,關(guān)鍵字也必須是唯一的。而且,要訪問同一個 IPC 對象, Server 和 Client必須使用同一個關(guān)鍵字。因此,如何構(gòu)造新的關(guān)鍵字使之不和已有的關(guān)鍵字沖突,并保證Server 和 Client 使用的關(guān)鍵字是相同的,是建立 IPC 對象時首先要解決的一個問題。(具體在后邊的msg通信中詳解)
通信方法還有:半雙工管道pipe,命名管道fifo,消息隊列,信號量,共享內(nèi),socket套接字等,下面一一介紹:
①半雙工管道:
int pipe(int filedes[2]);
管道是將兩個進程之間的標準輸入輸出相互對接的機制
linux命令中使用的管道 | ?: ls -l | grep *.c ?//顯示文件(輸入端)-(|)-(輸出端)>找到.c結(jié)尾文件
實現(xiàn):因為半雙工緣故,所以只能實現(xiàn)一段輸入,一段輸出,而不能雙向通信。所以:實現(xiàn)為,通過管道連接進程,一端開放讀文件描述,一端開放寫文件描述
//管道的性質(zhì)就是,一個進程的輸出作為另一個進程的輸入 //那么我們可以關(guān)閉一個進程讀端使之作為輸入端, //另一個進程關(guān)閉寫端,讀取數(shù)據(jù),接收數(shù)據(jù)作為管道輸出端//FIFO命名管道 //文件系統(tǒng)中,命名管道是特殊文件的方式存在的 //不同進程可以通過命名管道共享數(shù)據(jù)//命名管道一直是阻塞方式的,且必須是顯示的通過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); //創(chuàng)建管道if(-1==result){printf("管道創(chuàng)建失敗!\n");return -1;}pid = fork(); //進程創(chuàng)建分叉程序if(-1 == pid){printf("fork失敗");return -1;}if(0==pid) //子進程關(guān)閉讀端,寫入字符 {close(*read_fd);result = write(*write_fd,str,strlen(str));printf("寫入%d個數(shù)據(jù)\n",result);}else //父進程關(guān)閉寫端,讀取數(shù)據(jù) {close(*write_fd);nbytes = read(*read_fd,readBuffer,sizeof(readBuffer));printf("接收到%d個數(shù)據(jù),內(nèi)容為%s",nbytes,readBuffer);}return 0; }
②命名管道
int mkfifo(const char* pathname,mode_t mode);
類似于普通管道,只是
a.在文件系統(tǒng)中以設備特殊文件的形式存在
b.不同進程之間可以通過命名管道共享數(shù)據(jù)
操作區(qū)別于普通管道:FIFO中必須顯式通過open建立連接到管道的通道,且總是處于阻塞狀態(tài)的
③消息隊列
消息隊列是內(nèi)核地址空間的內(nèi)部鏈表,通過內(nèi)核在各個進程之間傳遞內(nèi)容。每個消息隊列通過唯一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; //函數(shù)msgget()使用的鍵值 __kernel_uid_t uid; //用戶UID__kernel_gid_t gid; //用戶GID__kernel_uid_t cuid; //創(chuàng)建者UID__kernel_gid_t cgid; //創(chuàng)建者GID__kernel_mode_t mode; //權(quán)限unsigned short seq; //序列號 };
內(nèi)核中的消息隊列
注:結(jié)構(gòu)list_head 形成一個鏈表,結(jié)構(gòu)msg_msg之中的m_list使得消息形成鏈表,查找,插入時,對m_list域進行偏移找到位置
相關(guān)函數(shù):
鍵值構(gòu)建 key_t ftok(const char* pathname,int proj_id);
獲取消息 int msgget(key_t key,int msgflg);
發(fā)送消息 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); //向內(nèi)核發(fā)送cmd命令判斷進行何種操作
一個簡單例子
④信號量
信號量是一種計數(shù)器,用來控制對多個進程共享的資源所進行的訪問。常用作鎖機制(生產(chǎn)者消費者模型是個典型使用)
信號量結(jié)構(gòu)
//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; /* 數(shù)組結(jié)構(gòu) */struct seminfo *__buf; /* 信號量內(nèi)部結(jié)構(gòu) */void *__pad; };
相關(guān)函數(shù)?
新建信號量 int semget(key_t key, int nsems, int semflg);
//key 來自于ftok()
信號量操作函數(shù) int semop(int semid,struct sembuf* sops, unsigned nsops);
//信號量的P,V操作通過向已經(jīng)建立好的信號量發(fā)送命令完成
控制信號量參數(shù)
int semctl(int semid, int semnum ,int cmd,.....);
//用于在信號量集合上執(zhí)行控制操作
#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; }
⑤共享內(nèi)存(最快捷的方法)沒有中間過程,管道等
在多個進程之間共享內(nèi)存區(qū)域的一種進程間通信方式,在多個進程之間對內(nèi)存段進行映射的方式實現(xiàn)內(nèi)存共享。
? ?相關(guān)函數(shù)
創(chuàng)建共享內(nèi)存函數(shù) int shmget(key_y key, size_t size, int shmflg);
獲得共享內(nèi)存地址void * shmat(int shmid,const void* shmaddr, int shmflg);
刪除共享內(nèi)存函數(shù) int shmdt(const void* shmadddr);
共享內(nèi)存控制函數(shù) int shmctl(int shmid ,int cmd, struct shmid_ds * buf);
⑥信號
用于在一個或多個進程之間傳遞異步信號。
相關(guān)函數(shù)
信號截取 sighandler signal(int signum ,sighandler handler);
發(fā)送信號 int kill(pid_t pid, int sig);
int raise(int sig);
轉(zhuǎn)載于:https://www.cnblogs.com/lang5230/p/5502771.html
總結(jié)
以上是生活随笔為你收集整理的Linux网络编程--进程间通信(一)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 房祖名是不是卧底?怎么供出了120个明星
- 下一篇: 求人力资源的大神指点