進程運行時時具有獨立性的,讓它本身通信是困難的,所以進程通信的前提條件是讓進程看到同一份資源(通常指的是某一塊內存)
- 數據傳輸:一個進程將它的數據發送給另一個進程。
- 資源共享:多個進程間共享相同的資源。
- 通知事件:一個進程需要向另一個進程發送消息,通知它發送某種事件,例如操作系統發送信號給子進程。
- 進程控制:有些進程希望能完全控制另一個進程,此時可直接從希望能夠攔截另一個進程的所有異常,並且知道它的狀態改變。
- 管道
- 匿名管道pipe
- 命名管道
- System V IPC
- System V 消息隊列
- System V 共享內存
- System V 信號量
- POSIX IPC
- 消息隊列
- 共享內存
- 信號量
- 互斥量
- 條件變量
- 讀寫鎖
ls | grep“hell”
命令格式:命令A|命令B,即命令1的正確輸出作爲命令B的操作對象(下圖應用別人的圖片)
例如: ps aux | grep “test” 在 ps aux中的結果中查找test。
管道可以分爲匿名管道與命名管道
- 匿名管道
用於具有親緣關係進程的通信
利用pipe函數來實現
#include<string.h>
#include <stdio.h>
#include <unistd.h>
int main()
{
int fd[2] = {0};
pipe(fd);
//printf("%d %d",fd[0],fd[1]);
pid_t id = fork();
if(id == 0)
{
close(fd[0]);
char message[] = "i'm a child process";
while(1)
{
write(fd[1],message,strlen(message));
sleep(1);
}
}
else
{
close(fd[1]);
while(1){
char buf[1024];
ssize_t s = read(fd[0],buf,sizeof(buf)-1);
if(s > 0)
{
buf[s] = 0;
printf("i'm a father,gotchild message:%s\n",buf);
}
}
}
return 0;
}
管道讀寫規則
- 當沒有數據可讀時
- O_NONBLOCK disable:read調用阻塞,進程暫停執行,一直到有數據來
- O_NONBLOCK enable:read調用返回-1
- 當管道滿時
- O_NONBLOCK disable:write調用阻塞,直到有進程取走數據。
- O_NONBLOCK enable:調用返回-1,errno值爲EAGAIN
- 如果所有管道寫端對應的文件描述符被關閉,則返回-1
- 如果所有管道讀端對應的文件描述符被關閉,則操作會產生信號SIGPIPE,可能會導致write進程退出
- 當要寫入的數據量不大於PIPE_BUF時,linux保證操作的原子性。
(PIPE_BUF與平臺版本有關係,一般爲4到8k)
管道特點
- 只能用於具有共同祖先的進程(具有親緣關係的進程)之間進行通信;通常,一個管道由一個進程創鍵,然後fork出子進程,此後父子進程就能用該管道。
- 一般而言,管道的生命週期和進程相同,內核會對管道操作進行互斥與同步
- 管道是半雙工的,數據只能從一個方向流動。
這裏解釋幾個概念
多進程共享的內存資源叫臨界資源,把訪問臨界資源的代碼叫臨界區在任何一個時刻只允許一個進程訪問臨界資源的行爲叫做互斥。
互斥可以保證安全性,一個進程每次訪問完臨界資源就要排在等待隊列的末尾。
在保證臨界資源的安全的情況下(通常是互斥),讓多進程訪問臨界資源具有一定的順序行,我們叫做同步(協同同步步調,避免飢餓問題),此處與IO模型的同步異步模型無任何關係。
原子性:通常對臨界資源的訪問只有訪問或不訪問,沒有中間態
半雙工數據在一個時刻只能從一個方向發往另一個方向而不能同時雙方發送,人的聊天也是半雙工的,畢竟沒有你說你的,我說我的的聊天方式(笑:D)
如果我們想要讓兩個進程完成雙向通信,可以用兩對管道實現具體方案爲,一方讀一方寫,另一方寫一方讀。
- 命名管道
匿名管道的一個特點是隻能在有親緣關係的進程間通信,如果想在不相干的進程間交換數據,可以用FIFO命令爲 mkfifo+文件名
同時我們可以mkfifo函數創建,再用read和write函數來讓兩進程間通信
#include <sys/types.h>
#include <string.h>
#include <stdio.h>
#include <unistd.h>
//#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
int main()
{
//用來寫入數據的
int fd = open("fifo",O_WRONLY);//客戶端 client
if(fd < 0){
printf("mkfifo error");
}
char buf[1024];
while(1)
{
ssize_t s = read(0,buf,sizeof(buf)-1);
if(s>0)
{
buf[s] = 0;
write(fd,buf,strlen(buf));
}
if(s == 0)
{
printf("celient end");
}
if(s < 0)
{
printf("read error");
}
}
}
#include <sys/types.h>
#include <string.h>
#include <stdio.h>
#include <unistd.h>
#include<sys/stat.h>
#include<fcntl.h>
int main()
{
//用來讀數據的
mkfifo("fifo",0644);
int fd = open("fifo",O_RDONLY);
if(fd < 0){
printf("mkfifo error");
exit(1);
}
char buf[1024];
while(1)
{
int s =read(fd,buf,strlen(buf));
if(s>0)
{
buf[s] = 0;
printf("%s",buf);
}
if(s == 0)
{
printf("celient end");
}
if(s < 0)
{
printf("read error");
}
}
}
Makefile文件爲
.PHONY:all
all:client serve
client:client.c
gcc -o $@ $^
serve:serve.c
gcc -o $@ $^
.PHONY:clean
clean:
rm -f client serve
匿名管道和命名管道的用法相似,且命名管道的用法更加簡單。
所以可以看到,數據並沒有拷貝,而是讓兩個進程看到同一份資源,系統所作的一共有兩個步驟
- 在物理內存中開闢一塊空間,使用函數shmget實現
- 將開闢的內存空間與進程的虛擬地址空間產生映射關係