每个进程各自有不同的用户地址空间,任何一个进程的全局变量在另一个进程中都看不到,所以进程之间要交换数据必须通过内核,在内核中开辟一块缓冲区,进程1把数据从用户空间拷到内核缓冲区,进程2再从内核缓冲区把数据读走,内核提供的这种机制称为进程间通信(IPC,InterProcess Communication)。
一、pipe管道(无名管道)
#include <unistd.h>
int pipe(int filedes[2]);
- 管道作用于有血缘关系的进程之间,通过fork来传递
- 调用pipe函数时在内核中开辟一块缓冲区(称为管道)用于通信,它有一个读端一个写端,然后通过filedes参数传出给用户程序两个文件描述符,filedes[0]固定用于管道的读端,filedes[1]固定用于管道的写端(很好记,就像0是标准输入1是标准输出一样)。所以管道在用户程序看起来就像一个打开的文件,通过read(filedes[0]);或者write(filedes[1]);向这个文件读写数据其实是在读写内核缓冲区。
- pipe函数调用成功返回0,调用失败返回-1。
- 无名管道是单工的工作方式,即同一时刻要么只能读管道,要么只能写管道。父子进程虽然同时拥有管道的读端和写端,但只能使用其中一个。
程序1:实现父进程写,子进程读父进程写的内容,并将读到的内容输出到标准输出。
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <wait.h>
int main(void)
{
int fd[2];
char str[1024] = "hello world.\n";
char buf[1024];
pid_t pid;
//fd[0]读端, fd[1]写端
if(pipe(fd) < 0){
perror("pipe");
exit(1);
}
pid = fork();
//父写子读
if(pid > 0){ //父进程
close(fd[0]); //关闭父进程的读端
sleep(2);
write(fd[1], str, strlen(str));
wait(NULL);
}
else if(pid == 0){//子进程
int len;
close(fd[1]); //关闭子进程的写端
len = read(fd[0], buf, sizeof(buf));
//sprintf(str, "child %s", buf);
write(STDOUT_FILENO, buf, len); //将子进程读到的内容输出到标准输出
}
else{
perror("fork");
exit(1);
}
return 0;
}
使用pipe管道时要注意:
1、写管道关闭,读端读完管道里面内容时,再次读,返回0,相当于读到EOF
2、写端未关闭,写端暂时无数据;读端读完管道里数据时,再次读,阻塞
3、读端关闭,写端写管道,产生SIGPIPE信号,写进程默认情况下回终止进程
4、读端为读管道数据,当写端写满管道数据后,再次写,阻塞
二、FIFO有名管道
通过mkfifo指令创建管道文件(注意在WinShare目录下不能创建管道文件,因为windows没有管道文件)
fifo_write.c----向管道文件中写数据
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#incude <fcntl.h>
void sys_err(char *str, int exitno)
{
perror(str);
exit(exitno);
}
int main(int argc, char * argv[])
{
int fd;
char buf[1024] = "hello world.\n";
if(argc < 2){
printf("./a.out fifoname.\n");
exit(1);
}
//fd = open(argv[1], O_RDONLY);
fd = open(argv[1], O_WRONLY);
if(fd < 0)
sys_err("open", 1);
write(fd, buf, strlen(buf));
close(fd);
return 0;
}
fifo_read.c---读取管道文件中的数据
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#include <stdlib.h>
void sys_err(char *str, int exitno)
{
perror(str);
exit(exitno);
}
int main(int argc, char *argv[])
{
int fd, len;
char buf[1024];
if(argc < 2){
printf("./a.out fifoname.\n");
exit(1);
}
//fd = open(argv[1], O_RDONLY);
fd = open(argv[1], O_RDONLY);
if(fd < 0)
sys_err("open", 1);
len = read(fd, buf, sizeof(buf));
write(STDOUT_FILENO, buf, len);
close(fd);
return 0;
}
执行的时候开两个终端窗口,先执行fifo_write.c,向管道中写数据,可以看到此时是阻塞的;
打开另外一个终端窗口,执行fifo_read.c,可以看到读出管道的数据,并打印在终端。
同时可以看到读写前后管道文件的大小是不变的