进程间通信(IPC)
每个进程各自有不同的用户地址空间,任何一个进程的全局变量在另一个进程中都看不见。所有进程之间要交换数据必须通过内核,在内核中开辟一块缓冲区,进程1把数据从用户空间拷到内核缓冲区,进程2再从内核缓冲区读走数据,内核提供的这种机制就是进程间通信(IPC),管道是最基本的IPC机制。
管道容量
管道容量的大小由:pipe buf 和缓冲条目的数目来共同决定其pipe capacity容量。
pipe buf定义的是内核管道缓冲区的容量,这个值由内核设定。我们可以通过ulimit -a命令来查看:
[root@localhost ~]# ulimit -a
pipe size (512 bytes, -p) 8
1
2
[root@localhost~]# ulimit -a
pipesize(512bytes,-p)8
ulimit -a查看到的pipe size一次原子写入为:512 bytes * 8 = 4096 bytes 。
当然另外一个与之对应的就是缓冲条目的个数:它存在于/usr/src/kernels/内核版本/include/linux/pipe_fs_i.h中。当然缓冲条目的个数与linux的内核版本是有关联的,在我的2.6.32-573.el6.x86_64内核上,其缓冲条目个数为:16。
[root@localhost linux]# pwd
/usr/src/kernels/2.6.32-573.el6.x86_64/include/linux
[root@localhost linux]# cat pipe_fs_i.h
#define PIPE_BUFFERS (16)
1
2
3
4
[root@localhostlinux]# pwd
/usr/src/kernels/2.6.32-573.el6.x86_64/include/linux
[root@localhostlinux]# cat pipe_fs_i.h
#define PIPE_BUFFERS (16)
因此就可以得到管道的容量为:16 * 4096 bytes = 65536 bytes。
真正的管道容量即(pipe capacity)。如果你对上述不大理解,我们也可以使用代码来计算管道容量。
当管道满时:
O_NONBLOCK discable: write调用阻塞,直到有程序读走数据。
O_NONBLOCK enable:调用返回-1,errno值为EAGAIN。
管道是一块内存缓冲区,可写下面程序测试管道的容量pipe capacity:
#include
#include
#include
#include
int main()
{
int _pipe[2];
int count = 0;
if(pipe(_pipe) < 0)
{
printf("pipe error\n");
return -1;
}
while(1)
{
write(_pipe[1],"a",sizeof(char));
printf("count = %d\n",++count);
}
return 0;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#include
#include
#include
#include
intmain()
{
int_pipe[2];
intcount=0;
if(pipe(_pipe)<0)
{
printf("pipe error\n");
return-1;
}
while(1)
{
write(_pipe[1],"a",sizeof(char));
printf("count = %d\n",++count);
}
return0;
}
count = 1
count = 2
.
.
.
count = 65534
count = 65535
count = 65536
1
2
3
4
5
6
7
8
count=1
count=2
.
.
.
count=65534
count=65535
count=65536
结果也是不言而喻的,当我们一直对管道进行写操作,最后停止的时候即是管道的容量。65536也因此印证了我之前的第一种方法的正确性。
管道缓冲区
在linux中,管道的实现并没有专门的数据结构,而是借助了文件系统的file结构和VFS的索引节点inode。通过将两个file结构指向同一个临时的VFS索引节点,而这个索引节点又指向一个物理页面而实现。如下图所示:
两个file数据结构定义文件操作例程地址是不同的,其中一个是向管道写入数据的例程地址,而另一个是从管道读处数据的例程地址。这样,用户程序的系统调用仍然是通常的文件操作,而内核却利用这种抽象机制实现了管道这一特殊操作。一个普通的管道仅可供具有共同祖先的两个进程之间共享,并且这个祖先已经建立了供他们使用的管道。
注意,在管道中的数据始终以和写数据相同的次序来进行读,这表示lseek()系统调用对管道不起作用。