ssize_t read(int fd,void* buffer,size_t count)
read调用并会在在buffer的末尾添加一个'\0',所以若将buffer当作字符串处理的话需要自行添加'\0',常用操作如下:
if((read_num = read(fd,buffer,count) > 0)
{
buffer[read_num] = '\0';
...
}
如果程序的文件偏移量(对于每个打开的文件,系统内核会记录其偏移量)已然超过了文件末尾,再进行IO操作会发生什么。。。
--对于read调用会返回0,表示文件结尾
--对于write,可写入数据,但会形成文件空洞(从文件结尾到新写入数据间的这段空间称为空洞)。从编程角度讲,文件空洞中是存在字节的,读取空洞将返回以0(空字节)填充的缓冲区;但文件空洞并不占用任何磁盘空间,直到后续某个时点为在文件空洞中写入数据,文件系统才会为其分配磁盘块
所有系统调用都是以原子操作方式执行的。竞争状态是指:操作共享资源的两个进程(或线程),其结果取决于一个无法预期的顺序,即这些进程(或线程)获得CPU使用权的先后相对顺序。即竞争状态往往出现于执行结果依赖于内核对进程的调度顺序。
文件描述符表是进程级的,即针对每个进程,内核为其维护打开文件的描述符表,该表的每一个条目记录了单个文件描述符的相关信息
打开文件表是系统级别的,即内核为所有打开的文件维护一个系统级的表格,即只有一张。每个条目记录了当前文件偏移量,状态标志(open时的flag参数)、访问模式等
i-node表也是系统级别的,每个文件系统都会为驻留其上的所有文件建立一个i-node表,每个条目记录文件类型、文件锁、指向数据块的指针等
注意三者之间的关系:
文件描述符表项----->打开文件表项------->i-node表项
注意各个操作造成的指向与共享:
--两个进程打开同一个文件,那么有独立的文件描述符表项和独立的打开文件表项,共享同一个i-node表项
--一个进程打开同一个文件多次,那么也有独立的描述符表项和独立的打开文件表项,但共享同一个i-node表项(同一个进程打开同一个文件多次,可能造成描述符泄露也即内存泄露)
--dup以及fork操作后,都造成独立的文件描述符表项,但共享同一个打开文件表项和i-node表项
详细内容见《Linux/Unix系统编程手册》第5.4章
fflush和fsync的区别:
fflush是C库中提供的库函数,而fsync是系统提供的系统调用
fflush通过调用write将用户空间的缓冲区写到内核空间的缓冲区中,而fsync将内核缓冲区中的数据冲洗到磁盘上
另外fsync是阻塞调用
数据的流动顺序如下:
printf等IO操作------>用户空间缓冲区(C库缓冲区)----->fflush(它会调用write)----->内核缓冲区------>fsync------>磁盘
这两个函数也代表了stdio和系统调用IO的概念:
stdio IO--------------(用户缓冲区)-------------->系统调用IO write,read---------------(内核缓冲区)-------------->磁盘
fflush(stream*)强制将stdio输出流中的数据通过write刷新到内核缓冲区中,此函数会刷新指定stream的输出缓冲区。
整个流程如下图所示:
当使用stdio库函数,并结合系统调用IO来实现对磁盘文件的IO操作时,必须将缓冲区文件牢记于心。IO系统调用会直接将数据传递到内核缓冲区中,而stdio库函数会等到用户空间的流缓冲区填满,再调用write将其传递到内核缓冲区中,例如:
printf("this is test for printf ");
write(STDOUT_FILENO,"test for write ”,15)
那么输出将是"test for write this is test for printf",因为stdout是行缓冲的,先调用printf时将“this is test for print"放入行缓冲区中,因为没有换行符而且行缓冲区并未满,故不刷新行缓冲区即不将其内容通过write写入内核缓冲区,而调用write后直接将”test for write "写入内核缓冲区中,故而程序终止时会将行缓冲区中的内容"this is test for printf"通过write写入内核缓冲区中,导致“test for write”先写入内核缓冲区,所以先打印出来。解决的办法是在printf中加入换行符使其刷新,或者在printf后面加入fflush(stdout)强制用户缓冲区内容写入内核缓冲区中!
每个进程都有两个目录相关属性根目录和当前工作目录,分别用于为解释绝对路径和相对路径提供参照点。
一个进程的当前工作目录定义了该进程解析相抵路径名的起点,新进程的当前工作目录继承自其父进程。
目录在本质上说是一个表格,包含文件名和i-node编号
对硬链接的两个限制(可通过符号链接来规避):
--因为目录条目(硬链接)对文件的指代采用了i-node编号,而i-node编号的唯一性仅在一个文件系统中才能得到保障,所以硬链接必须与其指代的文件驻留在同一文件系统中
--不能为目录创建硬链接,从而避免出现令诸多系统程序陷于混乱的链接环路(使用绑定挂载bind mount可以获得与为目录创建硬链接相似的效果)
因为符号链接指代一个文件名,而非i-node编号,所以可以用其来链接不同文件系统中的一个文件,而且可以为目录创建符号链接
各个系统调用有的会对符号链接进行解引用,有的则不会。然而有一点是约定俗成的:总是会对路径名中目录部分(即最后一个斜线字符钱的所有组成部分)的符号链接进行解除引用操作。因此,在轮奸/somdir/somesubdir/file中,弱somedir和somesubdir属于符号链接,则一定会解除对这两个目录的引用,而针对file是否进行解除引用取决于系统调用的特性。
i-node中并不包含文件的名称。相反,对文件的命名利用的是目录条目,而目录则是列出文件名和i-node编号之间对应关系的一个表格。也将这些目录条目称作(硬)链接,换个角度说,硬链接也就是为特点文件添加了一个目录条目,其中这个目录条目包含相同的i-node,但绝对路径名不同。一个文件可以有多个链接,这些链接之间的地位是平等的。可使用link和unlink来创建和移除链接,对文件的重命名则使用rename.