文件的创建、打开、关闭、读写操作已经学完了,可以横向和纵向的总结一下这一部分的知识了,纵向来说就是关于文件的操作,打开关闭和读写操作等。横向就是对比一下文件的使用。
回顾之前的学习,会发现管道通信与文件的读写有类似之处。
管道通信是以一种数据流的方式进行通信的,管道通信与文件有一定的相似性也有很多不同的地方。首先创建管道使用的是 pipe(fd[2]) 函数,fd[1]是写入端,fd[0]是读出端。向管道中写入数据用的函数是 write(fd[1], “……”, n) ,读出数据用的是函数 read(fd[0], buf, BUFSZ) 函数,跟我们读写文件的格式很像,这可以类比起来。管道在系统中相当于一个系统文件,缓存所要传输的数据,但是有很多地方与文件不同,比如当数据读出之后,管道中就没有数据可以读了。
匿名的半双工通道是没有实名的,是进程的一种资源方式,它随着进程的消失而被系统清除。总的来说,匿名的半双工通道可以理解成系统创建了一个文件,一端向管这个“文件”中写入数据,另一端读这个“文件”中的数据。对于“文件”的读写,当一个进程写文件的时候,其他进程阻塞,同样的,一个进程在读文件的时候其他进程也阻塞。只有属于同一祖先的进程可以读写这个 “文件”,其他进程不具有操作这个“文件”的权限,进程结束的时候,这个“文件”就被系统清除掉了。
再深入一点去想,为什么只有同一祖先的进程才有这个读写的权限?学习文件的时候,我们学习了文件描述符,文件描述符是进程与文件之前的一个桥梁,进程是通过文件描述符来操作文件的,当父进程fork()出子进程的时候,子进程会继承父进程的文件描述符表,这个文件描述符表里会记录所有父进程打开的文件,因此子进程会继承了父进程打开的文件,所以父子进程可以通过同一个文件描述符去访问同一个文件(但是不相干的进程文件描述符相同也不代表同一个文件),而管道就是建立在文件描述符上的,建立一个管道必须和两个文件描述符相关,一个可以赋予可读权限,一个可以赋予可写权限,但是这两个文件描述符其实都和同一个文件关联,所以你通过不同的文件描述符去访问这个文件时,可以获得不同的访问权限。
另外一种管道叫有名管道,也就是FIFO。FIFO有路径名与之相关联,以一种特殊设备文件形式存在于文件系统中。FIFO的创建类似于文件的创建。使用函数 mkfifo(const char * filename, mode_t mode) 来创建FIFO,与普通文件的创建方式很类似。filename是FIFO的名称,mode是FIFO的读写权限。打开FIFO用函数 open(“fifo1”,O_WRONLY) ,读写操作分别是 write(fd, buf, n) 和 read(fd, buf, n) ,这与一般的文件读写方式一样,因此一般的I/O函数都可以用于FIFO文件。FIFO能够在不同进程之间进行通信而普通的管道通信却只能在同一祖先的进程通信,这是因为普通管道的通信是通过继承文件描述符的方式进行的,祖先不同继承的不同,而FIFO是创建了一个实实在在的文件,所有的进程都可以得到他的文件描述符来对这个文件进行操作,因此不同的进程间可以通过FIFO进行通信。
文件学习的纵向比较就是关于文件操作的比较,创建一个文件需要这个文件的文件名和读写权限,如果没有读写权限就不能打开文件。而文件的打开方式主要分为3类,只读、只写和读写三种方式,这三种方式也关系到后边的读写问题,只读打开的文件不能写入,只写打开的文件不能读出数据,这是一个相辅相成的关系。
读操作需要关注的就是4个问题,是否以读打开,读哪个文件,数据读到哪,读多少数据。
写操作需要关注的也是4个问题,是否以写打开,写在哪个文件中,写什么数据,写多少数据。
可以看出来读写文件操作是一个对称的操作,在读写文件的时候,分别理清楚这4个问题思路就不会乱。
总结:一开始学习管道的时候仅仅是会用并不是很懂,现在学了文件之后,特别是了解了文件描述符这个概念之后,再去理解管道就比较透彻了。学完了文件的操作之后,对于管道通信的过程也更容易理清楚了,能够避免在编程的时候混淆。