前言:?
前面我們已經(jīng)對進(jìn)程已經(jīng)有了一個簡單的了解與認(rèn)識,那么進(jìn)程間的通信是什么樣的呢,什么是父子進(jìn)程,什么是兄弟進(jìn)程,沒有血緣關(guān)系間的進(jìn)程是如何實(shí)現(xiàn)進(jìn)程通信的,下面讓我們一起學(xué)習(xí)一下什么是進(jìn)程間的通信吧。
目錄
一、進(jìn)程間通信常用方式
IPC方式:
二、管道
1.概念:
*2.pipe函數(shù):
*3.管道的讀寫行為:
4.父子間進(jìn)程 :
?5.兄弟間進(jìn)程通信:
6.多個讀寫端操作管道
7.管道緩沖區(qū)大?。?/p>
.8.管道的優(yōu)劣
?三、FIFO:???
1.命名管道fifo的創(chuàng)建和原理:
?2.非血緣關(guān)系進(jìn)程間通信avi:
一、進(jìn)程間通信常用方式
IPC方式:
????????Linux環(huán)境下,進(jìn)程地址空間相互獨(dú)立,每個進(jìn)程各自有不同的用戶地址空間。任何一個進(jìn)程的全局變量在另一個進(jìn)程中都看不到,所以進(jìn)程和進(jìn)程之間不能相互訪問,要交換數(shù)據(jù)必須通過內(nèi)核,在內(nèi)核中開辟一塊緩沖區(qū),進(jìn)程1把數(shù)據(jù)從用戶空間拷到內(nèi)核緩沖區(qū),進(jìn)程2再從內(nèi)核緩沖區(qū)把數(shù)據(jù)讀走,內(nèi)核提供的這種機(jī)制稱為進(jìn)程間通信(IPC,InterProcess Communication)。
?
????????在進(jìn)程間完成數(shù)據(jù)傳遞需要借助操作系統(tǒng)提供特殊的方法,如:文件、管道、信號、共享內(nèi)存、消息隊(duì)列、套接字、命名管道等。隨著計算機(jī)的蓬勃發(fā)展,一些方法由于自身設(shè)計缺陷被淘汰或者棄用?,F(xiàn)今常用的進(jìn)程間通信方式有:
1.管道(使用最簡單)
2.FIFO(無血緣關(guān)系,單次讀取)
3.共享映射區(qū)(無血緣關(guān)系,反復(fù)讀取)
4.信號(開銷最小)
5.本地套接字(最穩(wěn)定)
二、管道
1.概念:
??????
?????????管道是一種最基本的IPC機(jī)制,作用于有血緣關(guān)系的進(jìn)程之間,完成數(shù)據(jù)傳遞。調(diào)用pipe系統(tǒng)函數(shù)即可創(chuàng)建一個管道。有如下特質(zhì):
(1).其本質(zhì)是一個偽文件(實(shí)為內(nèi)核緩沖區(qū))
(2).由兩個文件描述符引用,一個表示讀端,一個表示寫端,只能一次讀取。
(3).規(guī)定數(shù)據(jù)從管道的寫端流入管道,從讀端流出,單向流動。
管道的原理:? 管道實(shí)為內(nèi)核使用環(huán)形隊(duì)列機(jī)制,借助內(nèi)核緩沖區(qū)(4k)實(shí)現(xiàn)。
管道的局限性:
????????1)數(shù)據(jù)不能進(jìn)程自己寫,自己讀。·
????????2)管道中數(shù)據(jù)不可反復(fù)讀取。一旦讀走,管道中不再存在。
????????3)采用半雙工通信方式,數(shù)據(jù)只能在單方向上流動。
? ? ? ? 4)只能在有公共祖先的進(jìn)程間使用管道
常用的通信方式: 單工通信、半雙工通信、全雙工通信
創(chuàng)建管道文件:
(不占用磁盤空間)
*2.pipe函數(shù):
pipe函數(shù):用于有血緣關(guān)系的進(jìn)程間通信
函數(shù)功能:創(chuàng)建,并打開管道。
?? ?int pipe(int fd[2]);
?? ?參數(shù):?? ?
????????fd[0]: 讀端。
?? ??? ?fd[1]: 寫端。
?? ?返回值:
???????? 成功: 0
?? ??? ? 失?。?-1 errno
管道通信:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <pthread.h>
void sys_err(const char *str)
{
perror(str);
exit(1);
}
int main(int argc, char *argv[])
{
int ret,re;
int fd[2];
pid_t pid;
char *str = "hello pipe\n";
char buf[1024];
ret = pipe(fd); //父進(jìn)程先創(chuàng)建一個管道,持有管道的讀端和寫端
if(ret == -1)
sys_err("pipe error");
pid = fork(); //子進(jìn)程同樣持有管道的讀和寫端
if(pid > 0){ //父進(jìn)程
close(fd[0]); //關(guān)閉讀段
write(fd[1],str,strlen(str));//寫入數(shù)據(jù)
sleep(1);
close(fd[1]); //關(guān)閉寫段
}else if(pid == 0){ //子進(jìn)程
close(fd[1]); //關(guān)閉寫段
re = read(fd[0],buf,sizeof(buf)); //讀取數(shù)據(jù)
write(STDOUT_FILENO,buf,re); //寫到屏幕上
close(fd[0]); //關(guān)閉讀段
}
return 0;
}
*3.管道的讀寫行為:
讀管道:
?? ??? ?1. 管道有數(shù)據(jù),read返回實(shí)際讀到的字節(jié)數(shù)。
?? ??? ?2. 管道無數(shù)據(jù):?? ?
????????????????????????1)無寫端,read返回0 (類似讀到文件尾)
?? ??? ??? ??? ?????????2)有寫端,read阻塞等待。
寫管道:
?? ??? ?1. 無讀端, 異常終止。 (SIGPIPE導(dǎo)致的)
?? ??? ?2. 有讀端:?? ?
????????????????????????1) 管道已滿, 阻塞等待(少見)
?? ??? ??? ??? ?????????2) 管道未滿, 返回寫出的字節(jié)個數(shù)。
?1)讀管道,管道無數(shù)據(jù)(無寫端)
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <pthread.h>
void sys_err(const char *str)
{
perror(str);
exit(1);
}
int main(int argc, char *argv[])
{
int ret,re;
int fd[2];
pid_t pid;
char *str = "hello pipe\n";
char buf[1024];
ret = pipe(fd); //父進(jìn)程先創(chuàng)建一個管道,持有管道的讀端和寫端
if(ret == -1)
sys_err("pipe error");
pid = fork(); //子進(jìn)程同樣持有管道的讀和寫端
if(pid > 0){ //父進(jìn)程
close(fd[0]); //關(guān)閉讀段
// write(fd[1],str,strlen(str));//寫入數(shù)據(jù)
close(fd[1]); //關(guān)閉寫段
}else if(pid == 0){ //子進(jìn)程
close(fd[1]); //關(guān)閉寫段
re = read(fd[0],buf,sizeof(buf)); //讀取數(shù)據(jù)
printf("child read ret =%d\n",ret);
write(STDOUT_FILENO,buf,re); //寫到屏幕上
close(fd[0]); //關(guān)閉讀段
}
return 0;
}
read返回0
*4.父子間進(jìn)程通信 :
????????使用管道實(shí)現(xiàn)父子進(jìn)程間通信,完成:ls | wc -l。假定父進(jìn)程實(shí)現(xiàn)ls,子進(jìn)程實(shí)現(xiàn)wc
ls | wc -l命令:
?實(shí)現(xiàn)流程:
(1)父進(jìn)程創(chuàng)建管道 pipe()
(2)父進(jìn)程創(chuàng)建子進(jìn)程 fork()
(3)設(shè)置父進(jìn)程執(zhí)行l(wèi)s命令,子進(jìn)程執(zhí)行wc命令 execlp()
(4)設(shè)置父子進(jìn)程通過管道的單項(xiàng)流動(設(shè)置指向標(biāo)準(zhǔn)輸出的指向管道)? dup2()
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <pthread.h>
void sys_err(const char *str)
{
perror(str);
exit(1);
}
int main(int argc,char *argv[])
{
/***************
dup2();
fork();
pipe();
execlp();
****************/
int fd[2];
int ret;
pid_t pid;
//父進(jìn)程創(chuàng)建管道
ret = pipe(fd); //父進(jìn)程先創(chuàng)建一個管道,持有管道的讀端和寫端
if(ret == -1){
sys_err("pipe error");
}
//父進(jìn)程創(chuàng)建子進(jìn)程
pid = fork(); //子進(jìn)程同樣持有管道的讀和寫端
if(pid == -1){
sys_err("fork error");
}else if(pid > 0){ //父進(jìn)程 讀,關(guān)閉寫端
close(fd[1]); //關(guān)閉寫,設(shè)置單項(xiàng)流動
dup2(fd[0],STDIN_FILENO); //設(shè)置讀管道信息,重定向stdin 到管道讀端
execlp("wc","wc","-l",NULL); //設(shè)置父進(jìn)程wc命令
sys_err("execlp wc error");
}else if(pid == 0){
close(fd[0]); //關(guān)閉讀,設(shè)置單項(xiàng)流動
dup2(fd[1],STDOUT_FILENO); //設(shè)置寫操作指向管道,重定向stdout 到管道寫端
execlp("ls","ls",NULL); //設(shè)置子進(jìn)程執(zhí)行l(wèi)s命令
sys_err("execlp ls error");
}
return 0;
}
?
?5.兄弟間進(jìn)程通信:
使用管道實(shí)現(xiàn)兄弟進(jìn)程間通信,完成:ls | wc -l。假定父進(jìn)程實(shí)現(xiàn)ls,子進(jìn)程實(shí)現(xiàn)wc
?實(shí)現(xiàn)流程:
(1)父進(jìn)程創(chuàng)建管道 pipe()
(2)父進(jìn)程創(chuàng)建倆個(兄弟)子進(jìn)程?fork()
(3)設(shè)置兄進(jìn)程執(zhí)行l(wèi)s命令,第進(jìn)程執(zhí)行wc命令 execlp()?
(4)設(shè)置兄弟進(jìn)程通過管道的單項(xiàng)流動(設(shè)置指向標(biāo)準(zhǔn)輸出的指向管道)? dup2()
(5)回收父進(jìn)程殘余文件? wait()
剛創(chuàng)建出的兄弟進(jìn)程:
?設(shè)置兄弟進(jìn)程通過管道的單項(xiàng)流動后
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <pthread.h>
#include <sys/wait.h>
void sys_err(const char *str)
{
perror(str);
exit(1);
}
int main(int argc,char *argv[])
{
/***************
dup2();
fork();
pipe();
execlp();
wait();
****************/
int fd[2];
int ret;
int i;
pid_t pid;
//父進(jìn)程創(chuàng)建管道
ret = pipe(fd);
if(ret == -1){
sys_err("pipe error");
}
for(i = 0;i < 2;i++){ //表達(dá)式2 出口,僅限父進(jìn)程使用
pid = fork();
if(pid == -1){
sys_err("fork error");
}
if(pid == 0) //子進(jìn)程出口
break;
}
if(i == 2){ //父進(jìn)程 不參與管道使用
//不需要父進(jìn)程所以需要關(guān)閉他管道的讀寫端
close(fd[0]);
close(fd[1]);
//回收子進(jìn)程
wait(NULL);
wait(NULL);
}else if(i == 0){ //兄進(jìn)程
close(fd[0]);
dup2(fd[1],STDOUT_FILENO); //重定向stdout
execlp("ls","ls",NULL); //兄進(jìn)程執(zhí)行l(wèi)s命令
sys_err("ececlp ls error");
}else if(i == 1){ //弟進(jìn)程
close(fd[1]);
dup2(fd[0],STDIN_FILENO); //重定向stdin
execlp("wc","wc","-l",NULL); //弟進(jìn)程執(zhí)行wc命令
sys_err("ececlp wc error");
}
return 0;
}
6.多個讀寫端操作管道
實(shí)現(xiàn)一個pipe有一個寫端,多個讀端
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <pthread.h>
#include <sys/wait.h>
void sys_err(const char *str)
{
perror(str);
exit(1);
}
int main(int argc,char *argv[])
{
/***************
dup2();
fork();
pipe();
execlp();
****************/
int fd[2],i,n;
int ret;
char buf[1024];
pid_t pid;
//父進(jìn)程創(chuàng)建管道
ret = pipe(fd);
if(ret == -1){
sys_err("pipe error");
exit(1);
}
for(i = 0;i < 2;i++){
pid = fork();
if(pid == -1){
sys_err("fork error");
exit(1);
}
if(pid == 0)
break;
}
if(i == 2){ //父進(jìn)程
close(fd[1]); //父進(jìn)程關(guān)閉寫端,留讀端讀取數(shù)據(jù)
sleep(1);
n = read(fd[0],buf,1024); //從管道中讀取數(shù)據(jù)
write(STDOUT_FILENO,buf,n);
for(i == 0;i < 2;i++) //兩個兒子wait兩次
wait(NULL);
}else if(i == 0){ //兄進(jìn)程
close(fd[0]);
write(fd[1],"1.hello\n",strlen("1.hello\n"));
}else if(i == 1){ //弟進(jìn)程
close(fd[0]);
write(fd[1],"2.world\n",strlen("2.world\n"));
}
return 0;
}
7.管道緩沖區(qū)大?。?/h3>
可以使用 ulimIt -a 命令來查看當(dāng)前系統(tǒng)中創(chuàng)建管道文件所對應(yīng)的內(nèi)核緩沖區(qū)大小。通常為:
????????pipe size? ? ? ?????????......(512 bytes,-p)? 8?
也可以使用fpathconf函數(shù),借助參數(shù)―選項(xiàng)來查看。使用該宏應(yīng)引入頭文件<unistd.h>
????????long fpathconf(int fd, int name);成功:返回管道的大小―失敗:-1,設(shè)置errno
.8.管道的優(yōu)劣
優(yōu)點(diǎn):簡單,相比信號,套接字實(shí)現(xiàn)進(jìn)程間通信,簡單很多。
缺點(diǎn):
????????????????1.只能單向通信,雙向通信需建立兩個管道。
????????????????2.只能用父子、兄弟進(jìn)程(有共同祖先)間通信。該問題后來使用fifo有名管道解決)
?三、FIFO:???
fifo管道:可以用于無血緣關(guān)系的進(jìn)程間通信。
?? ?命名管道: ?mkfifo?
?? ?無血緣關(guān)系進(jìn)程間通信:
?? ??? ?????????????????讀端,open fifo O_RDONLY
?? ??????????????????? ?寫端,open fifo O_WRONLY
?????
1.命名管道fifo的創(chuàng)建和原理:
使用命令:myfifo myfifo
?使用myfifo創(chuàng)建
#include<stdio.h>
#include<sys/stat.h>
#include<errno.h>
#include<pthread.h>
#include<stdlib.h>
void sys_err(const char *str){
perror(str);
exit(1);
}
int main(int argc,char *str)
{
int ret = mkfifo("mytestfifo",0664);
if(ret == -1)
sys_err("mkfifo error");
return 0;
}
?2.非血緣關(guān)系進(jìn)程間通信avi:
向管道寫數(shù)據(jù):fifo_w.c
#include <stdio.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <fcntl.h>
#include <stdlib.h>
#include <string.h>
void sys_err(char *str)
{
perror(str);
exit(-1);
}
int main(int argc, char *argv[])
{
int fd, i;
char buf[4096];
if (argc < 2) {
printf("Enter like this: ./a.out fifoname\n");
return -1;
}
fd = open(argv[1], O_WRONLY); //打開管道文件
if (fd < 0)
sys_err("open");
i = 0;
while (1) {
sprintf(buf, "hello itcast %d\n", i++);
write(fd, buf, strlen(buf)); // 向管道寫數(shù)據(jù)
sleep(1);
}
close(fd);
return 0;
}
?向管道讀數(shù)據(jù):fifo_r.c
#include <stdio.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <fcntl.h>
#include <stdlib.h>
#include <string.h>
void sys_err(char *str)
{
perror(str);
exit(1);
}
int main(int argc, char *argv[])
{
int fd, len;
char buf[4096];
if (argc < 2) {
printf("./a.out fifoname\n");
return -1;
}
fd = open(argv[1], O_RDONLY); // 打開管道文件
if (fd < 0)
sys_err("open");
while (1) {
len = read(fd, buf, sizeof(buf)); // 從管道的讀端獲取數(shù)據(jù)
write(STDOUT_FILENO, buf, len);
sleep(3); //多個讀端時應(yīng)增加睡眠秒數(shù),放大效果.
}
close(fd);
return 0;
}
一方寫,一方讀:
一個寫,多個讀:管道特性,不能反復(fù)讀取文章來源:http://www.zghlxwxcb.cn/news/detail-696894.html
文章來源地址http://www.zghlxwxcb.cn/news/detail-696894.html
到了這里,關(guān)于Linux下的系統(tǒng)編程——進(jìn)程間的通信(九)的文章就介紹完了。如果您還想了解更多內(nèi)容,請?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!