本文詳解了Linux中行程間通信,包括了行程間通信的介紹,匿名管道和命名管道,
目錄
一、行程間通信的介紹
1. 行程間通信目的
2. 行程間通信分類
二、管道
1. 管道是什么
2. 匿名管道
(1)pipe
(2)實作
(3)用fork來共享管道原理
(4)從檔案描述符角度理解管道
(5)在內核角度理解管道
3.管道讀寫規則
4.管道特點
5.命名管道
(2)匿名管道與命名管道的區別
(3)命名管道的打開規則
(4)用命名管道實作server&client通信
一、行程間通信的介紹
行程在運行時是具有獨立性的,行程間通信一般要借助OS的資源,本質就是“資料拷貝”
1. 行程間通信目的
資料傳輸:一個行程需要將它的資料發送給另一個行程
資源共享:多個行程之間共享同樣的資源,
通知事件:一個行程需要向另一個或一組行程發送訊息,通知發生了某種事件(如行程終止時要通知父行程),
行程控制:有些行程希望完全控制另一個行程的執行(如Debug行程),此時控制行程希望能夠攔截另一個行程的所有陷入和例外,并能夠及時知道它的狀態改變,
2. 行程間通信分類
(1)管道
匿名管道
命名管道
(2)System V行程間通信
System V 訊息佇列
System V 共享記憶體
System V 信號量
(3)POSIX行程間通信
訊息佇列
共享記憶體
信號量
互斥量
條件變數
讀寫鎖
二、管道
1. 管道是什么
管道是Unix中最古老的行程間通信的形式,
一個行程連接到另一個行程的一個資料流稱為一個“管道”
2. 匿名管道
匿名管道用于行程之間通信,且僅限于本地父子行程之間通信,結構簡單,類似于一根水管,一端進水另一端出水(單工),
由于匿名管道是單工的,所以為實作父子行程雙向通信需要創建兩根管道,并由子行程繼承一根管道的讀句柄和另一根管道的寫句柄,
(1)pipe
原型:
#include <unistd.h>
int pipe(int fd[2]);
功能:創建一匿名管道
引數: fd:檔案描述符陣列,其中fd[0]表示讀端, fd[1]表示寫端
回傳值:成功回傳0,失敗回傳錯誤代碼
?
(2)實作
寫入管道,讀取管道,寫到螢屏
#include<stdio.h>
#include<unistd.h>
#include<sys/types.h>
#include<sys/wait.h>
#include<string.h>
#include<stdlib.h>
int main()
{
int fd[2] = {0};
if(pipe(fd) < 0)
{
printf("pipe error\n");
return 1;
}
pid_t id = fork(); //創建子行程
if(id == 0)
{
close(fd[0]); //關閉讀
const char *msg = "hi,i am chlid!";
int count = 30;
while(count)
{
write(fd[1], msg, strlen(msg)); //向fd[1]寫入
--count;
sleep(1);
}
close(fd[1]);
exit(0);
}
close(fd[1]); //關閉寫
char buff[64];
while(1)
{
ssize_t s = read(fd[0], buff, sizeof(buff));
if(s > 0) //讀取成功
{
buff[s] = '\0';
printf("child to father # %s\n", buff);
}
else if(s == 0) //讀取結束
{
printf("end\n");
break;
}
else //讀取失敗
{
printf("read error\n");
break;
}
}
waitpid(id, NULL, 0); //等待子行程
return 0;
}
?
(3)用fork來共享管道原理
fork之后會關閉各自不用的描述符,
?
(4)從檔案描述符角度理解管道
a.父行程創建管道
?
b.fork子行程
?
c.父行程關閉fd[0],子行程關閉fd[1]
?
(5)在內核角度理解管道
?
管道的使用和檔案一致,Linux一切皆檔案,
3.管道讀寫規則
(1)當沒有資料可讀時:
O_NONBLOCK disable:read呼叫阻塞,即行程暫停執行,一直等到有資料來到為止,
O_NONBLOCK enable:read呼叫回傳-1,errno值為EAGAIN,
(1)當管道滿的時候:
O_NONBLOCK disable: write呼叫阻塞,直到有行程讀走資料
O_NONBLOCK enable:呼叫回傳-1,errno值為EAGAIN
(1)如果所有管道寫端對應的檔案描述符被關閉,則read回傳0,
(1)如果所有管道讀端對應的檔案描述符被關閉,則write操作會產生信號SIGPIPE,進而可能導致write行程退出,
(1)當要寫入的資料量不大于PIPE_BUF時,linux將保證寫入的原子性,
當要寫入的資料量大于PIPE_BUF時,linux將不再保證寫入的原子性,
4.管道特點
(1)只能用于具有共同祖先的行程(具有親緣關系的行程)之間進行通信;通常,一個管道由一個行程創建,然后該行程呼叫fork,此后父、子行程之間就可應用該管道,
(2)管道提供流式服務
(3)一般而言,行程退出,管道釋放,所以管道的生命周期隨行程
(4)一般而言,內核會對管道操作進行同步與互斥
(5)管道是半雙工的,資料只能向一個方向流動;需要雙方通信時,需要建立起兩個管道

5.命名管道
匿名管道應用的一個限制就是只能在具有共同祖先(具有親緣關系)的行程間通信,如果我們想在不相關的行程之間交換資料,可以使用FIFO檔案來做這項作業,它經常被稱為命名管道,命名管道是一種特殊型別的檔案,
(1)創建一個命名管道
命名管道可以從命令列上創建,命令列方法是使用下面這個命令:
mkfifo filename
命名管道也可以從程式里創建,相關函式有:
int mkfifo(const char *filename,mode_t mode);
創建命名管道:
int main(int argc, char *argv[])
{
mkfifo("p2", 0644);
return 0;
}
(2)匿名管道與命名管道的區別
a.匿名管道由pipe函式創建并打開,
b.命名管道由mkfifo函式創建,打開用open
c.FIFO(命名管道)與pipe(匿名管道)之間唯一的區別在它們創建與打開的方式不同,一但這些作業完成之后,它們具有相同的語意,
(3)命名管道的打開規則
如果當前打開操作是為讀而打開FIFO時
O_NONBLOCK disable:阻塞直到有相應行程為寫而打開該FIFO
O_NONBLOCK enable:立刻回傳成功
如果當前打開操作是為寫而打開FIFO時
O_NONBLOCK disable:阻塞直到有相應行程為讀而打開該FIFO
O_NONBLOCK enable:立刻回傳失敗,錯誤碼為ENXIO
(4)用命名管道實作server&client通信
makefile
.PHONY:all
all:client server
client: client.c
gcc -o $@ $^
server: server.c
gcc -o $@ $^
.PHONY:clear
clear:
rm client server
comm.h
#pragma once
#include<stdio.h>
#include<unistd.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#define FILE_NAME "myfifo"
server.c
#include"comm.h"
int main()
{
if(mkfifo(FILE_NAME, 0644) < 0){ //創建命名檔案
perror("myfifo\n");
return 1;
}
int fd = open(FILE_NAME, O_RDONLY); //打開檔案
if(fd < 0){
perror("file error\n");
return 2;
}
char msg[128];
while(1){
msg[0] = '\0';
ssize_t s = read(fd, msg, sizeof(msg) - 1); //讀管道檔案
if(s > 0){
msg[s] = 0;
printf("client to serve # %s", msg);
}else if(s == 0){
printf("read end\n");
break;
}else{
perror("read error\n");
break;
}
}
close(fd);
return 0;
}
client.c
#include"comm.h"
#include<string.h>
int main()
{
int fd = open(FILE_NAME, O_WRONLY);
if(fd < 0)
{
printf("open error\n");
return 1;
}
char msg[128];
while(1){
msg[0] = 0;
printf("Please Enter# ");
fflush(stdout); //重繪緩沖區
ssize_t s = read(0, msg, sizeof(msg)); //鍵盤讀取
if(s > 0){
msg[s] = 0;
write(fd, msg, strlen(msg)); //寫
}
}
close(fd);
return 0;
}
結果:

可以看到兩個行程實作了通信,
轉載請註明出處,本文鏈接:https://www.uj5u.com/ruanti/352276.html
標籤:其他
下一篇:【MySQL】表的增刪改查
