處理序間通訊IPC:Inter-process communication(這邊介紹四種) 一、管道1、管道(有親緣關係)及有名管道(無親緣)、訊號、訊息佇列、共用記憶體、訊號量、通訊端。=====無名管道2、管道:是堵塞的;管道的讀端存在時向管道中寫入資料才有意義。具有固定的讀端和寫端。3、當一個管道建立pipe(fds)時,它會建立兩個檔案描述符 fds[0]和 fds[1]。結束後要關閉,close(fds[0]);close(fds[1]);4、一般是先建一個管道,再通過fork(),則會有兩個管道。其中 fds[0]固定用於讀管道,而 fd[1]固定用於寫管道具有父子親緣關係。為了實現父子進程之間的讀寫,只需把無關的讀端或寫端的檔案描述符關閉即可。一邊讀,一邊寫。 www.2cto.com 5、最後也要等待,等子進程結束後父進程再結束。防止殭屍進程,沒人收。6、注意:1.只有在管道的讀端存在時向管道中寫入資料才有意義。2.向管道中寫入資料時,linux 將不保證寫入的原子性,管道緩衝區一有空閑地區,寫進程就會試圖向管道寫入資料。如果讀進程不讀取管道緩衝區中的資料,那麼寫操作將會一直阻塞。3.父子進程在運行時,它們的先後次序並不能保證,因此,在這裡為了保證父進程已經關閉了讀描述符,可在子進程中調用 sleep 函數。=======標準流管道1、標準流管道:用來建立一個串連到另一個進程的管道。所以不屬於有名或無名管道。2、popen,"r"只是把結果作為輸出,但還沒有輸出。返回的是管道的檔案描述符。"w"貌似不可用-lai。==對應 pclose(fd);=======有名管道(相當於把建立的檔案當做管道串連,然後像對普通檔案一樣進行讀寫。檔案也就是管道可以通過路徑名來指出,而且在檔案系統中是可見的)1、有名管道:互不相關的兩個進程實現彼此通訊。(mkfifo)管道可以通過路徑名來指出,也可以mknod 管道名 p 來建立2、普通檔案讀寫不會出現堵塞問題,管道讀寫中卻有堵塞的可能。非堵塞標誌:O_NONBLOCK.3、如果檔案已經存在,則也可以成功,但是會出現“交叉讀寫”現象,所以盡量避免。4、但是以上測試還是有點問題,最好用完後就刪掉,不然第二次編譯不了===賴5、#define FIFO_SERVER "/linux_basic_study/pro_communication/myfifo"6、//只要建立一次,檔案就會存在,以後直接開啟也可以。//可以看到以P開頭的管道檔案在/linux_basic_study/myfifo//errno是系統定義的全域變數,函數發生異常時,一般會將errno變數(需include errno.h)賦一個整數值, www.2cto.com //不同的值表示不同的含義,可以通過查看該值推測出錯的原因.7.//阻塞讀時要每讀一次就open一次阻塞僅在第一次讀有效。否則第一次堵塞後,後面就不堵塞了。//非阻塞時open要放while外面,不然接收不到資料.8.unlink(FIFO);//刪除檔案。記住:要保證裡面沒有檔案。對應第四條。======================================================================二、訊號通訊1.訊號是在軟體層次上對中斷機制的一種類比,是一種非同步通訊方式。訊號可以直接進行使用者空間進程和核心進程之間的互動。相當於我們的中斷處理函數2.訊號生命週期3個重要階段==>通過四個畫面:訊號產生(核心)、訊號在進程中註冊(使用者進程)、訊號在進程中登出(使用者進程)、執行訊號處理函數。3.不可靠訊號(前)32個,若發現同樣的該訊號已經註冊,則忽略。可靠訊號則會再次註冊。4.注意:訊號的產生、註冊、登出等是指訊號的內部實現機制,而不是訊號的函數實現。5.三種=>忽略訊號(SIGKILL及SIGSTOP不能被忽略):對訊號不做處理;捕捉訊號:當訊號發生時,執行相應的處理函數;執行預設操作:Linux 對每種訊號都規定了預設操作。6.重要的六個訊號:SIGINT=>(CTRL+C)f發出; SIGQUIT=>(CTRL+\) SIGKILL SIGALARM SIGSTOP SIGCHLD=>(CTRL+Z)7.Alarm()一個進程只能有一個鬧鐘時間。時間到後,發出SIGALRM訊號,自動終止 程式執行 。8.Pause()用於將調用進程掛起直至捕捉到訊號為止。9.Raise()類似於kill函數允許進程而且只能向自身發送訊號。10.Kill()不僅可以中止進程(實際上發出 SIGKILL 訊號),也可以向進程發送其他 訊號。11.訊號處理的主要方法有兩種,一種是使用簡單的 signal 函數 eg:signal(SIGINT, my_func);,另一種是 使用訊號集合函式組。12.父進程為1是孤兒進程,是脫離控制台。Kill不掉。SIGSTOP用產生。。 只能重啟系統才能關掉孤兒進程。所以最好發送KILL訊號,不要發送stop三、共用記憶體(建立一個共用記憶體,可以映射到自己的記憶體上,通過同步機制。)利用共用記憶體存在問題:如果在迴圈裡面,它會迴圈讀取已經讀過的內容。可同時利用後面講到的訊息佇列解決這個問題。1、共用記憶體是一種最為高效的處理序間通訊方式。===>非阻塞。。。刪除一次就可以了,刪除兩次會出錯。2、是將需要訪問的記憶體映射到自己私人的地址空間。所以相當於訪問自己的記憶體。 3、步驟:1,建立共用記憶體:最好ipc_creat(沒有e)...不然如果這個key 的記憶體沒有 建立的話會找不到檔案會報錯。返回的是共用記憶體 區標識符shmid = shmget((key_t)100,BUFSZ,0666|IPC_CREAT); 2、映射到共用記憶體。返回的是共用記憶體映射到指定位置的指標shmadd = shmat(shmid ,0 ,0);3、讀取或者寫入if(!strncmp(shm_ptr,"end",3);if(!strcmp(shm_ptr, "end"))//不行,不要用,第一次可以,後面就 不行4、撤銷映射記憶體shmdt(shmadd)5、刪除共用記憶體:記住:讀或者寫一方刪除就可以了,不然會 報錯。6、在輸入時,用fgets(temp, 1024, stdin); 鍵盤輸入,標準stdin;用scanf("%s",temp);//中間不能有空格,因為是以空格或者斷行符號 為結束。所以不採用。四、訊息佇列1、訊息佇列具有一定的FIFO 的特性,但是它可以實現訊息的隨機 查詢,比FIFO 具有更大的優勢。2、也是一個放送,一個接收。兩種方法:一個用結構體,一個不要(其實項目中要的)。3、步驟:1、建立或開啟訊息佇列:返回的是訊息佇列IDmsg_id = msgget((key_t)1234, 0666 | IPC_CREAT);2、讀取訊息:msgrcv(msg_id,buf, BUFSIZ, 0, 0)2、發送添加訊息:msgsnd(message_id,buf,BUFSZ,0)3、控制訊息隊列:相當於撤銷,刪除。msgctl(message_id, IPC_RMID, 0) 4、利用結構體,發送資料,但發送過去的只是字串,而 message_type只是作為接收第幾個資訊的標誌。所以一 般用另一種。