进程间通信的方式:
- 管道 (半双工(只能收或者发,不能同时) ,全双工(同步进行,同发同收)
- 信号量
- 共享内存
- 消息队列
- 套接字
- 管道: 有名管道和无名管道
有名管道:可以在任意进程间通信
无名管道:仅在父子进程间通信 - 定义:
从一个进程链接数据流到另外一个进程
说人话:一个进程的输出通过某种介质作为另一个进程的输入
这个介质就是管道。这也是需要管道的原因。
命令的写法:cmd1 | cmd2
比如shell 让标准输入和标准输出两个命令通果管道显示在终端屏幕上:
cmd1 标准输入的命令(键盘)
cmd1 标准输出传给 cmd2 ,作为cmd2的标准输入
cmd2 标准输出到屏幕
如下图: - 管道通信:如果需要程序实现,需要两个函数(popen()和pclose())来实现两个进程之间的数据传递。
command :要运行的程序名和相应的参数
open_mode:r / w 权限
r : 被输出的程序可以被输出程序使用,输出程序利用popen返回的FILE*文件流指针,使用fread来读取被输出程序的输出。
w: 输出程序通过fwrite 向被输出程序发送数据。而被输出程序从标准输入读取数据。【它不知道这口饭刚从别人嘴里吐出来。放在盘子里。】
【接盘侠,可以理解为从婚介所得到一个其他人吐出来的二手货】
#include<stdio.h>
FILE *popen(const char* command,const char* open_mode);
int pclose(FILE *stream_to_close);
popen() :允许一个程序将另外一个程序做为新进程启动。
也就是一个程序的输出命令作为了另一个程序的输入
pclose() : 只在popen启动的进程结束后返回。返回值是所关闭的文件流所在的进程的退出码。如果父进程提前获取了该进程的退出码,那么该进程极其资源被回收。其返回值为-1或error。
也就是pclose必须在父进程获取该进程退出码之前执行。
例如:ps -ef 输出 | 输入grep “main”
出现一下问题:
1. 管道必须读写进程同时 open,否则会阻塞
2. 如果管道没有数据,那么read ,阻塞
3. 管道的写端关闭,读read返回值为0
4. 管道的读端关闭,写会产生异常(发送SIGPIPE)
(女朋友挂断电话,你还一个劲说话有啥用?)
#include<signal.h>
void fun(int sig)
{
printf("sig =%d",sig);
}
int main()
{
signal(SIGPIPE,fun);
- 创建有名管道
mkfifo FIFO (fifo 可以随便起)
touch a.c b.c
特殊之处:打开文件管道文件,会在内存中开辟一片空间,会将写入的数据写入内存中。不论有名管道还是无名管道
- a.c文件
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<fcntl.h>
int main()
{
int fdw = open("fifo", O_WRONLY);
if (fdw == -1)
{
exit(-1);
}
char buff[128] = { 0 };
fgets(buff, 128, stdin);
write(fdw, buff, strlen(buff) - 1);
close(fdw);
exit(0);
}
- b.c文件
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<fcntl.h>
int main()
{
int fdr = open("fifo", O_RDONLY);
if (fdr == -1)
{
exit(1);
}
printf("fdr =%d\n", fdr);
char buff[128] = { 0 };
int num = read(fdr, buff, 127);
printf("buff =%s\n", buff);
close(fdr);
exit(0);
}
- 改进:(连续发送数据,和接收数据)
管道中没有数据会阻塞住(只要不关闭管道)
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<fcntl.h>
int main()
{
int fdw = open("fifo", O_WRONLY);
if (fdw == -1)
{
exit(-1);
}
while(1){
char buff[128] = { 0 };
fgets(buff, 128, stdin);
if(strncmp(buff,"end",3)==0 )
{
break;
}
write(fdw, buff, strlen(buff) - 1);
}
close(fdw);
exit(0);
}
- b.c文件
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<fcntl.h>
int main()
{
int fdr = open("fifo", O_RDONLY);
if (fdr == -1)
{
exit(1);
}
printf("fdr =%d\n", fdr);
while(1){
char buff[128] = { 0 };
int num = read(fdr, buff, 127);
if(num == 0) //读到的数据为0
{
break;
}
printf("buff =%s\n", buff);
}
close(fdr);
exit(0);
}
无名管道创建
man pipe
通过pipe
打开管道文件,直接将管道的文件描述符返回
会用fork()
产生一个子进程,(因此父进程负责写,子进程负责读)
必须父子进程均关闭,该文件才关闭ca。(教室最后一个人走了,才算真的关闭)
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<fcntl.h>
int main()
{
int fd[2];
if (pipe(fd) == -1) //fd[0] 读,fd[1]写
{
exit(1);
}
printf("fd[0] = %d,fd[1] = %d\n", fd[0], fd[1]);
pid_t pid =fork();
if(pid == -1)
{
exit(0);
}
if(pid == 0)
{
close(fd[1]);
char buff[128] ={0};
read(fd[0],buff,127);
printf("child read :%s\n",buff);
close(fd[0]);
}
else
{
close(fd[0]);
write(fd[1],"abc",3);
close(fd[1]);
}
exit(0);
}
- 面试会问:
dup(3,1)
将3的文件描述符复制到1的位置
ls>a.test
文件重定向 (追加和覆盖)
因为标准输入输出已经被a.txt 覆盖,因此就没有标准输出和标准输入了。因此hello打印再a.txt中
#include<stdlib.h>
#include<unistd.h>
#include<fcntl.h>
int main()
{
int fd = open("a.txt", O_WRONLY | O_CREAT, 0600)
{
if (fd == -1)
{
printf("open a.txt filed\n");
exit(1);
}
dup2(fd, 1);
dup2(fd, 2);
}
printf("hello\n"); //因为标准输入输出已经被a.txt 覆盖,因此就没有标准输出和标准输入了。因此hello打印再a.txt中
exit(0);
}
管道的实现: