过几天就要给linux兴趣小组的同学讲下我最近学的一些东西,我觉的还是进程间通信这块是学习linux C的一个亮点吧,进程间通信的手段很多。其中管道以及有名管道划分一类,还有信号,消息队列(又称报文队列),共享内存,信号量,套接口等。
1.管道的特点总结起来也就是下面几点:
- 半双工,即数据只能向一个方向流动,需要双方通信时,需要建立两个管道
- 仅用于父子进程和兄弟进程之间
- 单独的一个特殊的文件系统,只存在于内存当中
- 特殊的数据读出和写入规则:pipe一个管道后 [0]为读出端,[1]为写入端
2.管道的创建:pipe()函数,原型为
#include
int pipe(int pipefd[2]);
3.管道的使用(读写相关):
管道创建后就可以使用,进程间通信,所以,是pipe创建管道后,然后fork下,父子进程共同继承这个管道的信息,于是就有了父子进程间信息的交流,当然,你也可以自己给自己发消息(使用管道,不过这没什么意义,貌似)。
前面说过,pipe创建一个位于内存中的管道后,文件描述符fd[0]和fd[1]分别就是读端和写端(名字随意,也可以是pipe_fd[0],pipe_fd[1],关键是这个索引,0和1),这是规定,这样用就行了。可以这样记忆,pipe产生的管道0端读,1端写。
管道中读数据的时候:如果没写端,默认到了数据末尾,函数返回读出数据字节数为0;有写端,那么要读取的字节数大于PIPE_BUF(这个定义于include/linux/limits.h中,我的内核定义为4096),则返回管道中现有的所有数据字节数。如果不大于PIPE_BUF返回管道中现有的数据字节数,或者返回请求的数据字节数。
下面是一个读规则的测试。
/*
* Copyright (c) 2010-~ zhouyongfei
*
* The source code is released for free distribution under the terms of the GNU General Public License
*
*
* Author: alen Chou
* Created Time: 2010年08月13日 星期五 11时01分35秒
* File Name: readtest.c
* Description:
*
*/
#include
#include
#include
#include
int main(int argc, char *argv[])
{
int pipe_fd[2];
pid_t pid;
char r_buf[100];
char w_buf[50];
int r_num;
memset(r_buf,0,sizeof(r_buf));
memset(w_buf,0,sizeof(r_buf));
if(pipe(pipe_fd) < 0){ printf("pipe create error\n"); return -1; } if(0 == (pid=fork())){ printf("\n"); close(pipe,pipe_fd[1]);/*子进程关闭写端*/ sleep(3); /*确保父进程关闭写端*/ r_num = read(pipe_fd[0],r_buf,100); printf("read num is %d ,the data read from the pipe is \"%s\"\n",r_num,r_buf); close(pipe_fd[0]);/*子进程关闭读端*/ exit(0); }else if(pid > 0){
close(pipe_fd[0]);/*父进程关闭读端*/
strcpy(w_buf,"hello ,this is alen Chou….");
if(write(pipe_fd[1],w_buf,sizeof(w_buf)) != -1)
printf("parent write over\n");
close(pipe_fd[1]);/*父进程关闭写端*/
printf("parent close fd[1] over\n");
sleep(5);
exit(0);
}
return 0;
} |
从上面的例子可以简单的看出,读端每次先关闭写端,读取数据完成后关读端,写端每次先关闭读端,写完后关闭写端,你也可以不完全这样,但是会出现这样或者那样的问题,因为管道是半双工,所以每次必须保证一端写,另一端读,这样才能保证数据传输的正确性。
管道中写数据时:linux不保证写数据的原子性,管道缓冲区有空闲区,写进程就会尝试写入,如果管道已满,而没有读进程读取管道缓冲区的数据,那么写进程将会一直阻塞直到有读进程读走数据。
下面是一个测试写规则的例子:
/*
* Copyright (c) 2010-~ zhouyongfei
*
* The source code is released for free distribution under the terms of the GNU General Public License
*
*
* Author: alen Chou
* Created Time: 2010年08月13日 星期五 11时59分57秒
* File Name: pipe3.c
* Description:
*
*/
#include
#include
#include
#include
#include
#include
main(int argc,char**argv)
{
int stat;
int pipe_fd[2];
pid_t pid;
char r_buf[4096];
char w_buf[4096*2];
int writenum;
int rnum;
memset(r_buf,0,sizeof(r_buf));
if(pipe(pipe_fd)<0){ printf("pipe create error\n"); return -1; } if((pid=fork())==0){ close(pipe_fd[1]); /*while(1){ sleep(1); rnum=read(pipe_fd[0],r_buf,1000); printf("child: readnum is %d\n",rnum); if(rnum == 0){ break; } //sleep(1); }*/ close(pipe_fd[0]); exit(0); } else if(pid>0){
//close(pipe_fd[0]);//write
memset(r_buf,0,sizeof(r_buf));
if((writenum=write(pipe_fd[1],w_buf,5000))==-1)
printf("write to pipe error\n");
else
printf("the bytes write to pipe is %d \n", writenum);
if((writenum=write(pipe_fd[1],w_buf,5000))==-1){
printf("write to pipe error\n");
}else
printf("the bytes write to pipe again is %d\n",writenum);
close(pipe_fd[1]);
wait(&stat);
}
return;
} |
这个例子中有许多值得琢磨的地方,比如可以看到写操作原子性的规则,以及写操作的时候的阻塞问题,测试程序的时候可以在不同的地方加上注释就会出现不同的结果。
4.最后说下管道的一些局限性:
- 只支持单向数据流;
- 只能用于具有亲缘关系的进程之间;
- 没有名字;
- 管道的缓冲区是有限的
- 管道所传送的是无格式字节流,这就要求管道的读出方和写入方必须事先约定好数据的格式,比如多少字节算作一个消息等。
当然上面的一些问题我们现在就可以解决,比如半双工只能是数据单向流动,那我们可以创建两个管道,然后fork父子进程,这样就可以使用两个管道进行相互的数据传输了。有些问题就得留给后面去解决,比如解决非亲缘关系的进程间的信息传输就可以使用有名管道等
好了,管道就说道这了。。
阅读(1011) | 评论(0) | 转发(0) |