LInux--进程间通信
1.進(jìn)程間通信概念介紹:
1.1(1).數(shù)據(jù)傳輸
一個(gè)進(jìn)程需要將它的數(shù)據(jù)發(fā)送給另一個(gè)進(jìn)程
(2).資源共享
多個(gè)進(jìn)程之間共享同樣的資源
(3)通知事件
一個(gè)進(jìn)程需要向另一個(gè)或一組進(jìn)程發(fā)送消息,通知它們發(fā)生了某種事件
(4).進(jìn)程控制
有些進(jìn)程希望完全控制另一個(gè)進(jìn)程的執(zhí)行(如Debug進(jìn)程),此時(shí)控制進(jìn)程希望能夠攔截另一個(gè)進(jìn)程的所有操作,并能夠及時(shí)知道它的狀態(tài)改變
1.2Linux進(jìn)程間通信(IPC)由以下幾部分發(fā)展而來(lái):
1.UNIX進(jìn)程間通信
2.基于System V進(jìn)程間通信
3.POSIX進(jìn)程間通信
2.進(jìn)程間通信方式
2.1共享內(nèi)存
進(jìn)程間需要共享的數(shù)據(jù)被放在一個(gè)叫做IPC共享內(nèi)存區(qū)域的地方,所有需要訪問(wèn)該共享區(qū)域的進(jìn)程都要把該共享區(qū)域映射到本進(jìn)程的地址空間中去。系統(tǒng)V共享內(nèi)存通過(guò)shmget獲得或創(chuàng)建一個(gè)IPC共享內(nèi)存區(qū)域,并返回相應(yīng)的標(biāo)識(shí)符。內(nèi)核在保證shmget獲得或創(chuàng)建一個(gè)共享內(nèi)存區(qū),初始化該共享內(nèi)存區(qū)相應(yīng)的shmid_kernel結(jié)構(gòu)注同時(shí),還將在特殊文件系統(tǒng)shm中,創(chuàng)建并打開(kāi)一個(gè)同名文件,并在內(nèi)存中建立起該文件的相應(yīng)dentry及inode結(jié)構(gòu),新打開(kāi)的文件不屬于任何一個(gè)進(jìn)程(任何進(jìn)程都可以訪問(wèn)該共享內(nèi)存區(qū))。所有這一切都是系統(tǒng)調(diào)用shmget完成的。
2.1.1共享內(nèi)存實(shí)現(xiàn)分為兩個(gè)步驟:
一、創(chuàng)建共享內(nèi)存,使用shmget函數(shù)
二、映射共享內(nèi)存,將這段創(chuàng)建的共享內(nèi)存映射到具體的進(jìn)程空間去,使用shmat函數(shù)
2.1.2 int shmget ( key_t key, int size, int shmflg )
2.1.3char * shmat ( int shmid, char *shmaddr, int flag)
參數(shù):
shmid:shmget函數(shù)返回的共享存儲(chǔ)標(biāo)識(shí)符
flag:決定以什么方式來(lái)確定映射的地址(通常為0)
返回值:
如果成功,則返回共享內(nèi)存映射到進(jìn)程中的地址;如果失敗,則返回- 1
2.1.4當(dāng)一個(gè)進(jìn)程不再需要共享內(nèi)存時(shí),需要把它從進(jìn)程地址空間中脫離。
int shmdt ( char *shmaddr )
2.1.5
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <error.h>
#define SIZE 1024
int main()
{
int shmid ;char *shmaddr ;struct shmid_ds buf ;int flag = 0 ;int pid ;shmid = shmget(IPC_PRIVATE, SIZE, IPC_CREAT|0600 ) ;if ( shmid < 0 ){perror("get shm ipc_id error") ;return -1 ;}pid = fork() ;if ( pid == 0 ){shmaddr = (char *)shmat( shmid, NULL, 0 ) ;if ( (int)shmaddr == -1 ){perror("shmat addr error") ;return -1 ;}strcpy( shmaddr, "Hi, I am child process!\n") ;shmdt( shmaddr ) ;return 0;} else if ( pid > 0) {sleep(3 ) ;flag = shmctl( shmid, IPC_STAT, &buf) ;if ( flag == -1 ){perror("shmctl shm error") ;return -1 ;}printf("shm_segsz =%d bytes\n", buf.shm_segsz ) ;printf("parent pid=%d, shm_cpid = %d \n", getpid(), buf.shm_cpid ) ;printf("chlid pid=%d, shm_lpid = %d \n",pid , buf.shm_lpid ) ;shmaddr = (char *) shmat(shmid, NULL, 0 ) ;if ( (int)shmaddr == -1 ){perror("shmat addr error") ;return -1 ;}printf("%s", shmaddr) ;shmdt( shmaddr ) ;shmctl(shmid, IPC_RMID, NULL) ;}else{perror("fork error") ;shmctl(shmid, IPC_RMID, NULL) ;}return 0 ;}
編譯 gcc shm.c –o shm。
執(zhí)行 ./shm,執(zhí)行結(jié)果如下:
shm_segsz =1024 bytes
shm_cpid = 9503
shm_lpid = 9504
Hi, I am child process!
多進(jìn)程讀寫即一個(gè)進(jìn)程寫共享內(nèi)存,一個(gè)或多個(gè)進(jìn)程讀共享內(nèi)存。下面的例子實(shí)現(xiàn)的是一個(gè)進(jìn)程寫共享內(nèi)存,一個(gè)進(jìn)程讀共享內(nèi)存。
(1)下面程序?qū)崿F(xiàn)了創(chuàng)建共享內(nèi)存,并寫入消息。
shmwrite.c源代碼如下:
#include <stdio.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/types.h>
#include <unistd.h>
#include <string.h>
typedef struct{
char name[8];int age;} people;
int main(int argc, char** argv)
{
int shm_id,i;key_t key;char temp[8];people *p_map;char pathname[30] ;strcpy(pathname,"/tmp") ;key = ftok(pathname,0x03);if(key==-1){perror("ftok error");return -1;}printf("key=%d\n",key) ;shm_id=shmget(key,4096,IPC_CREAT|IPC_EXCL|0600); if(shm_id==-1){perror("shmget error");return -1;}printf("shm_id=%d\n", shm_id) ;p_map=(people*)shmat(shm_id,NULL,0);memset(temp, 0x00, sizeof(temp)) ;strcpy(temp,"test") ;temp[4]='0';for(i = 0;i<3;i++){temp[4]+=1;strncpy((p_map+i)->name,temp,5);(p_map+i)->age=0+i;}shmdt(p_map) ;return 0 ;}
(2)下面程序?qū)崿F(xiàn)從共享內(nèi)存讀消息。
shmread.c源代碼如下:
#include <stdio.h>
#include <string.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/types.h>
#include <unistd.h>
typedef struct{
char name[8];int age;} people;
int main(int argc, char** argv)
{
int shm_id,i;key_t key;people *p_map;char pathname[30] ;strcpy(pathname,"/tmp") ;key = ftok(pathname,0x03);if(key == -1){perror("ftok error");return -1;}printf("key=%d\n", key) ;shm_id = shmget(key,0, 0); if(shm_id == -1){perror("shmget error");return -1;}printf("shm_id=%d\n", shm_id) ;p_map = (people*)shmat(shm_id,NULL,0);for(i = 0;i<3;i++){printf( "name:%s\n",(*(p_map+i)).name );printf( "age %d\n",(*(p_map+i)).age );}if(shmdt(p_map) == -1){perror("detach error");return -1;}return 0 ;}
(3)編譯與執(zhí)行
① 編譯gcc shmwrite.c -o shmwrite。
② 執(zhí)行./shmwrite,執(zhí)行結(jié)果如下:
key=50453281
shm_id=688137
③ 編譯gcc shmread.c -o shmread。
④ 執(zhí)行./shmread,執(zhí)行結(jié)果如下:
key=50453281
shm_id=688137
name:test1
age 0
name:test2
age 1
name:test3
age 2
⑤ 再執(zhí)行./shmwrite,執(zhí)行結(jié)果如下:
key=50453281
shmget error: File exists
可用 ipcrm -m 共享內(nèi)存標(biāo)識(shí)符大小即可。
2.2管道通信
2.2.1管道是單向的、先進(jìn)先出的,它把一個(gè)進(jìn)程的輸出和另一個(gè)進(jìn)程的輸入連接在一起。
一個(gè)進(jìn)程(寫進(jìn)程)在管道的尾部寫入數(shù)據(jù),另一個(gè)進(jìn)程(讀進(jìn)程)從管道的頭部讀出數(shù)據(jù)
2.2.2管道包括無(wú)名管道和有名管道兩種,前者用于父進(jìn)程和子進(jìn)程間的通信,后者可用于運(yùn)行于同一系統(tǒng)中的任意兩個(gè)進(jìn)程間的通信。
無(wú)名管道創(chuàng)建:
int pipe(int filedis[2]);
當(dāng)一個(gè)管道建立時(shí),它會(huì)創(chuàng)建兩個(gè)文件描述符:
filedis[0] 用于讀管道,
filedis[1] 用于寫管道
2.2.3管道用于不同進(jìn)程間通信。通常先創(chuàng)建一個(gè)管道,再通過(guò)fork函數(shù)創(chuàng)建一個(gè)子進(jìn)程,該子進(jìn)程會(huì)繼承父進(jìn)程所創(chuàng)建的管道
!!!注意:必須在系統(tǒng)調(diào)用fork( )前調(diào)用pipe( ),否則子進(jìn)程將不會(huì)繼承文件描述符
2.3 消息隊(duì)列:
消息隊(duì)列就是一個(gè)消息的鏈表.可以把消息看作一個(gè)記錄,具有特定的格式.進(jìn)程可以向中按照一定的規(guī)則添加新消息;另一些進(jìn)程則可以從消息隊(duì)列中讀走消息
2.3.1msgget:
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
int msgget(key_t key, int msgflg)
key:鍵值,由ftok獲得。
msgflg:標(biāo)志位。
返回值:與健值key相對(duì)應(yīng)的消息隊(duì)列描述字
msgflag:
IPC_CREAT
創(chuàng)建新的消息隊(duì)列
IPC_EXCL
與IPC_CREAT一同使用,表示如果要?jiǎng)?chuàng)建的消息隊(duì)列已經(jīng)存在,則返回錯(cuò)誤。
IPC_NOWAIT
讀寫消息隊(duì)列要求無(wú)法得到滿足時(shí),不阻塞
Msqid: 已打開(kāi)的消息隊(duì)列id
Msgp: 存放消息的結(jié)構(gòu)
Msgsz: 消息數(shù)據(jù)長(zhǎng)度
Msgflg:
發(fā)送標(biāo)志,有意義的msgflg標(biāo)志為IPC_NOWAIT,指明在消息隊(duì)列沒(méi)有足夠空間容納要發(fā)送的消息時(shí),msgsnd是否等待
2.3.2 接收消息 msgrcv
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
int msgrcv(int msqid, struct msgbuf *msgp, int msgsz, long msgtyp, int msgflg)
功能:
從msqid代表的消息隊(duì)列中讀取一個(gè)msgtyp類型的消息,并把消息存儲(chǔ)在msgp指向的msgbuf結(jié)構(gòu)中。在成功地讀取了一條消息以后,隊(duì)列中的這條消息將被刪除
struct msgbuf
{
long mtype;/消息類型/
char mtext[1]; /消息數(shù)據(jù)的首地址/
}
2.4 信號(hào)量
2.4.1信號(hào)燈的含義介紹:
信號(hào)量(又名:信號(hào)燈)與其他進(jìn)程間通信方式不大相同,主要用途是保護(hù)臨界資源.
進(jìn)程可以根據(jù)它判定是否能夠訪問(wèn)某些共享資源。除了用于訪問(wèn)控制外,還可用于進(jìn)程同步
2.4.2信號(hào)燈的分類:
二值信號(hào)燈:信號(hào)燈的值只能取0或1,類似于互斥鎖。 但兩者有不同:
信號(hào)燈強(qiáng)調(diào)共享資源,只要共享資源可用,其他進(jìn)程同樣可以修改信號(hào)燈的值;
互斥鎖更強(qiáng)調(diào)進(jìn)程,占用資源的進(jìn)程使用完資源后,必須由進(jìn)程本身來(lái)解鎖。
計(jì)數(shù)信號(hào)燈:信號(hào)燈的值可以取任意非負(fù)值
2.4.3
信號(hào)量的操作——semop函數(shù)
信號(hào)量的值與相應(yīng)資源的使用情況有關(guān),當(dāng)它的值大于 0 時(shí),表示當(dāng)前可用的資源數(shù)的數(shù)量;當(dāng)它的值小于 0 時(shí),其絕對(duì)值表示等待使用該資源的進(jìn)程個(gè)數(shù)。信號(hào)量的值僅能由 PV 操作來(lái)改變。
sem_op 參數(shù):
sem_op > 0 信號(hào)加上 sem_op 的值,表示進(jìn)程釋放控制的資源;
sem_flg 參數(shù):
該參數(shù)可設(shè)置為 IPC_NOWAIT 或 SEM_UNDO 兩種狀態(tài)。只有將 sem_flg 指定為 SEM_UNDO 標(biāo)志后,semadj (所指定信號(hào)量針對(duì)調(diào)用進(jìn)程的調(diào)整值)才會(huì)更新。 此外,如果此操作指定SEM_UNDO,系統(tǒng)更新過(guò)程中會(huì)撤消此信號(hào)燈的計(jì)數(shù)(semadj)。此操作可以隨時(shí)進(jìn)行—它永遠(yuǎn)不會(huì)強(qiáng)制等待的過(guò)程。調(diào)用進(jìn)程必須有改變信號(hào)量集的權(quán)限。
重點(diǎn)介紹的是semop函數(shù)。該函數(shù)主要功能是對(duì)信號(hào)燈進(jìn)行P/V操作。
P操作責(zé)把當(dāng)前進(jìn)程由運(yùn)行狀態(tài)轉(zhuǎn)換為阻塞狀態(tài),直到另外一個(gè)進(jìn)程喚醒它。操作為:申請(qǐng)一個(gè)空閑資源(把信號(hào)量減1),若成功,則退出;若失敗,則該進(jìn)程被阻塞;
V操作負(fù)責(zé)把一個(gè)被阻塞的進(jìn)程喚醒,它有一個(gè)參數(shù)表,存放著等待被喚醒的進(jìn)程信息。操作為:釋放一個(gè)被占用的資源(把信號(hào)量加1),如果發(fā)現(xiàn)有被阻塞的進(jìn)程,則選擇一個(gè)喚醒之。
semop函數(shù)原型如下:
int semop(int semid, struct sembuf *sops, unsigned nsops);
semop操作中:sembuf結(jié)構(gòu)的sem_flg成員可以為0、IPC_NOWAIT、SEM_UNDO 。為SEM_UNDO時(shí),它將使操作系統(tǒng)跟蹤當(dāng)前進(jìn)程對(duì)這個(gè)信號(hào)量的修改情況,如果這個(gè)進(jìn)程在沒(méi)有釋放該信號(hào)量的情況下終止,操作系統(tǒng)將自動(dòng)釋放該進(jìn)程持有的。
sembuf結(jié)構(gòu)的sem_flg成員為SEM_UNDO時(shí),它將使操作系統(tǒng)跟蹤當(dāng)前進(jìn)程對(duì)這個(gè)信號(hào)量的修改情況,如果這個(gè)進(jìn)程在沒(méi)有釋放該信號(hào)量的情況下終止,操作系統(tǒng)將自動(dòng)釋放該進(jìn)程持有的信號(hào)量
問(wèn)題描述:假設(shè)父子進(jìn)程對(duì)一個(gè)文件進(jìn)行寫操作,但是這個(gè)文件同一時(shí)間只能有一個(gè)進(jìn)程進(jìn)行寫操作。
示例程序如下:
#include <stdio.h>
//……此處省略了頭文件
void P(int sid)
{
struct sembuf sem_p;
sem_p.sem_num = 0;
sem_p.sem_op = -1;
sem_p.sem_flg = 0;
在該程序中,父子進(jìn)程都有可能執(zhí)行P操作成功,因此,兩個(gè)進(jìn)程中的提示語(yǔ)句,交替顯示。若通過(guò)kill命令把其中一個(gè)進(jìn)程殺死,且該進(jìn)程還沒(méi)有執(zhí)行V操作釋放資源。若使用SEM_UNDO標(biāo)志,則操作系統(tǒng)將自動(dòng)釋放該進(jìn)程持有的信號(hào)量,從而使得另外一個(gè)進(jìn)程可以繼續(xù)工作。若沒(méi)有這個(gè)標(biāo)志,另外進(jìn)程將P操作永遠(yuǎn)阻塞。
因此,一般建議使用SEM_UNDo標(biāo)志。
總結(jié)
以上是生活随笔為你收集整理的LInux--进程间通信的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 集训2--进程控制理论
- 下一篇: 大端与小端详细介绍