LInux--进程间通信
1.進程間通信概念介紹:
1.1(1).數據傳輸
一個進程需要將它的數據發送給另一個進程
(2).資源共享
多個進程之間共享同樣的資源
(3)通知事件
一個進程需要向另一個或一組進程發送消息,通知它們發生了某種事件
(4).進程控制
有些進程希望完全控制另一個進程的執行(如Debug進程),此時控制進程希望能夠攔截另一個進程的所有操作,并能夠及時知道它的狀態改變
1.2Linux進程間通信(IPC)由以下幾部分發展而來:
1.UNIX進程間通信
2.基于System V進程間通信
3.POSIX進程間通信
2.進程間通信方式
2.1共享內存
進程間需要共享的數據被放在一個叫做IPC共享內存區域的地方,所有需要訪問該共享區域的進程都要把該共享區域映射到本進程的地址空間中去。系統V共享內存通過shmget獲得或創建一個IPC共享內存區域,并返回相應的標識符。內核在保證shmget獲得或創建一個共享內存區,初始化該共享內存區相應的shmid_kernel結構注同時,還將在特殊文件系統shm中,創建并打開一個同名文件,并在內存中建立起該文件的相應dentry及inode結構,新打開的文件不屬于任何一個進程(任何進程都可以訪問該共享內存區)。所有這一切都是系統調用shmget完成的。
2.1.1共享內存實現分為兩個步驟:
一、創建共享內存,使用shmget函數
二、映射共享內存,將這段創建的共享內存映射到具體的進程空間去,使用shmat函數
2.1.2 int shmget ( key_t key, int size, int shmflg )
2.1.3char * shmat ( int shmid, char *shmaddr, int flag)
參數:
shmid:shmget函數返回的共享存儲標識符
flag:決定以什么方式來確定映射的地址(通常為0)
返回值:
如果成功,則返回共享內存映射到進程中的地址;如果失敗,則返回- 1
2.1.4當一個進程不再需要共享內存時,需要把它從進程地址空間中脫離。
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。
執行 ./shm,執行結果如下:
shm_segsz =1024 bytes
shm_cpid = 9503
shm_lpid = 9504
Hi, I am child process!
多進程讀寫即一個進程寫共享內存,一個或多個進程讀共享內存。下面的例子實現的是一個進程寫共享內存,一個進程讀共享內存。
(1)下面程序實現了創建共享內存,并寫入消息。
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)下面程序實現從共享內存讀消息。
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)編譯與執行
① 編譯gcc shmwrite.c -o shmwrite。
② 執行./shmwrite,執行結果如下:
key=50453281
shm_id=688137
③ 編譯gcc shmread.c -o shmread。
④ 執行./shmread,執行結果如下:
key=50453281
shm_id=688137
name:test1
age 0
name:test2
age 1
name:test3
age 2
⑤ 再執行./shmwrite,執行結果如下:
key=50453281
shmget error: File exists
可用 ipcrm -m 共享內存標識符大小即可。
2.2管道通信
2.2.1管道是單向的、先進先出的,它把一個進程的輸出和另一個進程的輸入連接在一起。
一個進程(寫進程)在管道的尾部寫入數據,另一個進程(讀進程)從管道的頭部讀出數據
2.2.2管道包括無名管道和有名管道兩種,前者用于父進程和子進程間的通信,后者可用于運行于同一系統中的任意兩個進程間的通信。
無名管道創建:
int pipe(int filedis[2]);
當一個管道建立時,它會創建兩個文件描述符:
filedis[0] 用于讀管道,
filedis[1] 用于寫管道
2.2.3管道用于不同進程間通信。通常先創建一個管道,再通過fork函數創建一個子進程,該子進程會繼承父進程所創建的管道
!!!注意:必須在系統調用fork( )前調用pipe( ),否則子進程將不會繼承文件描述符
2.3 消息隊列:
消息隊列就是一個消息的鏈表.可以把消息看作一個記錄,具有特定的格式.進程可以向中按照一定的規則添加新消息;另一些進程則可以從消息隊列中讀走消息
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:標志位。
返回值:與健值key相對應的消息隊列描述字
msgflag:
IPC_CREAT
創建新的消息隊列
IPC_EXCL
與IPC_CREAT一同使用,表示如果要創建的消息隊列已經存在,則返回錯誤。
IPC_NOWAIT
讀寫消息隊列要求無法得到滿足時,不阻塞
Msqid: 已打開的消息隊列id
Msgp: 存放消息的結構
Msgsz: 消息數據長度
Msgflg:
發送標志,有意義的msgflg標志為IPC_NOWAIT,指明在消息隊列沒有足夠空間容納要發送的消息時,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代表的消息隊列中讀取一個msgtyp類型的消息,并把消息存儲在msgp指向的msgbuf結構中。在成功地讀取了一條消息以后,隊列中的這條消息將被刪除
struct msgbuf
{
long mtype;/消息類型/
char mtext[1]; /消息數據的首地址/
}
2.4 信號量
2.4.1信號燈的含義介紹:
信號量(又名:信號燈)與其他進程間通信方式不大相同,主要用途是保護臨界資源.
進程可以根據它判定是否能夠訪問某些共享資源。除了用于訪問控制外,還可用于進程同步
2.4.2信號燈的分類:
二值信號燈:信號燈的值只能取0或1,類似于互斥鎖。 但兩者有不同:
信號燈強調共享資源,只要共享資源可用,其他進程同樣可以修改信號燈的值;
互斥鎖更強調進程,占用資源的進程使用完資源后,必須由進程本身來解鎖。
計數信號燈:信號燈的值可以取任意非負值
2.4.3
信號量的操作——semop函數
信號量的值與相應資源的使用情況有關,當它的值大于 0 時,表示當前可用的資源數的數量;當它的值小于 0 時,其絕對值表示等待使用該資源的進程個數。信號量的值僅能由 PV 操作來改變。
sem_op 參數:
sem_op > 0 信號加上 sem_op 的值,表示進程釋放控制的資源;
sem_flg 參數:
該參數可設置為 IPC_NOWAIT 或 SEM_UNDO 兩種狀態。只有將 sem_flg 指定為 SEM_UNDO 標志后,semadj (所指定信號量針對調用進程的調整值)才會更新。 此外,如果此操作指定SEM_UNDO,系統更新過程中會撤消此信號燈的計數(semadj)。此操作可以隨時進行—它永遠不會強制等待的過程。調用進程必須有改變信號量集的權限。
重點介紹的是semop函數。該函數主要功能是對信號燈進行P/V操作。
P操作責把當前進程由運行狀態轉換為阻塞狀態,直到另外一個進程喚醒它。操作為:申請一個空閑資源(把信號量減1),若成功,則退出;若失敗,則該進程被阻塞;
V操作負責把一個被阻塞的進程喚醒,它有一個參數表,存放著等待被喚醒的進程信息。操作為:釋放一個被占用的資源(把信號量加1),如果發現有被阻塞的進程,則選擇一個喚醒之。
semop函數原型如下:
int semop(int semid, struct sembuf *sops, unsigned nsops);
semop操作中:sembuf結構的sem_flg成員可以為0、IPC_NOWAIT、SEM_UNDO 。為SEM_UNDO時,它將使操作系統跟蹤當前進程對這個信號量的修改情況,如果這個進程在沒有釋放該信號量的情況下終止,操作系統將自動釋放該進程持有的。
sembuf結構的sem_flg成員為SEM_UNDO時,它將使操作系統跟蹤當前進程對這個信號量的修改情況,如果這個進程在沒有釋放該信號量的情況下終止,操作系統將自動釋放該進程持有的信號量
問題描述:假設父子進程對一個文件進行寫操作,但是這個文件同一時間只能有一個進程進行寫操作。
示例程序如下:
#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;
在該程序中,父子進程都有可能執行P操作成功,因此,兩個進程中的提示語句,交替顯示。若通過kill命令把其中一個進程殺死,且該進程還沒有執行V操作釋放資源。若使用SEM_UNDO標志,則操作系統將自動釋放該進程持有的信號量,從而使得另外一個進程可以繼續工作。若沒有這個標志,另外進程將P操作永遠阻塞。
因此,一般建議使用SEM_UNDo標志。
總結
以上是生活随笔為你收集整理的LInux--进程间通信的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 集训2--进程控制理论
- 下一篇: 大端与小端详细介绍