因为在linux系啊,进程地址空间相互独立,所以进程和进程之间不能相互访问。要交换数据必须通过内核。
有很多方法都可以完成这类功能,比如文件、管道、命名管道、信号、共享内存、消息队列、套接字等,随着技术的发展,有些方法已经被淘汰。现今最常用的方法是:
① 管道 :使用最简单,传递数据量较小 ,本机程序。
②信号:开销最小
③共享内存: 无血缘关系,相对复杂
④套接字:难度最高,最稳定,传递大量数据,不同机器。
整个linux文件可分为7种类型,- 文件 、 d 目录、 l 符号链接、s套接字、b 块设备、 c字符设备、 p管道
但实际上只有- 文件 、 d 目录、 l 符号链接 这3种是真正占用磁盘空间。
而后4种属于伪文件,不会占用磁盘空间。
管道就是伪文件,实质上内核缓冲区(内核使用环形队列方式实现),缓冲区大小默认是4k。
因此管道具有如下特性:
① 数据从一端写入,从另一端读出,不能自己读自己写。
② 数据一旦被读走,便不存在于管道中,因此不可反复读取。
③半双工通信方式、数据只能在一个方向上流动。
④只能在有血缘关系的进程之间使用。
int pipe(int pipefd[2]) 成功返回0,失败返回-1
pipefd[2] 存储着两个文件描述符,pipefd[0]表示写入端,pipefd[1]表示读取端。
#include
#include
#include
#include
#include
int main()
{
int pipefd[2];
pid_t pid;
int rep;
char buf[20]="hello pipe\n";
rep=pipe(pipefd);
if (rep==-1)
{
perror("pipe");
exit(1);
}
pid=fork();
if (pid>0)
{
// parent wirte
close(pipefd[0]);
write(pipefd[1],buf,20);
wait(NULL);
}
else if(pid ==0)
{
// child read
close(pipefd[1]);
read(pipefd[0],buf,20);
printf("child has read %s",buf);
}
else
{
perror("fork");
exit(1);
}
return 0;
}
这里有个思考,父写子读的时候,那就默认父进程必须要先执行,子进程后执行,但是linux中父子进程的先后顺序又没有保证。
那么程序会不会有bug?
子进程会不会先执行?
所以我在父进程中添加
// parent wirte
sleep(3);
printf("is parent \n");
如果把程序改写成子写父读,会怎么样呢?
会不会父进程先执行,但是此时并没有写入,所以程序出错读不出来?
#include
#include
#include
#include
#include
int main()
{
int pipefd[2];
pid_t pid;
int rep;
char buf[20]="hello pipe\n";
rep=pipe(pipefd);
if (rep==-1)
{
perror("pipe");
exit(1);
}
pid=fork();
if (pid==0)
{
// child wirte
close(pipefd[0]);
write(pipefd[1],buf,20);
wait(NULL);
}
else if(pid >0)
{
// parent read
printf("is parent\n");
close(pipefd[1]);
read(pipefd[0],buf,20);
printf("parent has read %s",buf);
}
else
{
perror("fork");
exit(1);
}
return 0;
}
程序正确执行,并没有问题,为什么呢?
原因在于wirte和read本身就是阻塞型I/O,一旦有了read或者wirte,系统会阻塞等待直到另一方完成操作。
所以在管道通信时,无需自己再去考虑进程的执行次序问题。
改进了管道的一些缺点,可以用于血缘关系进程,也可以用于非血缘关系进程。
两个独立运行的进程,最传统的可以用文件通讯,一个进程打开某个文件写入,然后另一个进程读取该文件。
这种方式要考虑到先后次序问题,即竞争问题,数据是被真正写入文件。
有名管道有点像文件通讯,但实际上并不真正把数据写入文件中,它和文件通讯的写法基本一样,有一个专用的函数来完成此功能。
#include
#include
int mkfifo(const char *pathname, mode_t mode); 成功返回0,错误返回-1
只需要其中一个独立进程创建有名管道就可以了,另一个进程需要去检测有名管道是否存在,如果存在,就打开,如果不存在,这一个进程可以选择等待,也可以选择创建有名管道。