这篇笔记记录下,多进程(包括父子进程)操作同一个文件,或者同一个文件被同一个进程多次打开时的情况,总是傻傻分不清,必须记录一下了。
1 文件IO_共享文件
分下面四种情况记录。
情况1:同一进程多次open同一个文件
在同一个进程中多次open打开同一文件时,文件描述符不可能相同,因为同一个文件描述符池中某个文件描述符被占用,在close之前,该fd无法再次被使用。
在一个进程中,两次打开同一个文件,对应的代码如下:
通过实验发现,hello会被world覆盖,具体原因如下:
每个文件描述符都对应一个文件表,因为打开同一个文件,所以每个文件表V节点指针指向相同的V节点。每个文件描述符fd指向的文件表中有自己的“文件位移量”,新创建的文件,所以文件位移量是0,从0开始写,每write一次(clase()之后),文件偏移量增加写入的字节数,如果文件偏移量超出了当前文件长度,则用文件位移量更新当前文件长度。
在上面代码中,他们写的位置重重复了,所以hello会被world覆盖。
怎样解决上面问题呢?每次都加上O_APPEND,即可解决,其原理如下:当设置A_APPEND后,每次执行写操作时,文件表项中的当前文件偏移量首先会被设置为i节点表项中的文件长度,这就使得每次写入数据都会被追加到文件的末尾处。
情况2:多个进程共享操作同一个文件
不同进程打开同一个文件时候,各自使用的文件描述符表可能相等,本例子中相等。之所以相同,因为不同的进程有独立的文件描述符池,都是0-1023的范围,打开一个文件时候,首先会从最小的开始分配。实验代码如下:
单个进程打开文件两次与两个进程同时打开文原理相同,都指向了相同的V节点,所以也会出现覆盖的情况。同样的解决方法:指定A_Append标志,写操作时候,使用文件长度去更新文件位移量。保证了各自操作时,都在文件的末尾操作,就不会出现覆盖的情况。
情况3: 父子进程共享同一文件
父子进程共享文件和情况2中的多进程共享文件的原理相同,出现覆盖的原因相同。
int main()
{
pid_t ret = 0;
ret = fork();
if (ret > 0) // parent pid
{
int fd = 0;
fd = open("./file.txt ",O_RDWR|O_CREAT,0664);
write(fd,"hello\n",6);
}
else if(ret == 0) // child pid
{
int fd = 0;
fd = open("./file.txt ",O_RDWR|O_CREAT,0664);
write(fd,"world\n",6);
}
//while(1);
return 0;
}
子进程和父进程指向相同的V节点表,它们拥有各自的独立的文件读写位置,刚开始都是0开始写,又会出现情况2中的覆盖,如果不想相互覆盖,需要加O_APPEND标志。
情况4: fork之前打开文件,会出现什么情况?
代码如下:
int main()
{
pid_t ret = 0;
int fd = 0;
fd = open("./file.txt ",O_RDWR|O_CREAT|O_APPEND,0664);
ret = fork();
if (ret > 0) // parent pid
{
write(fd,"hello\n",6);
}
else if(ret == 0) // child pid
{
write(fd,"world\n",6);
}
//while(1);
return 0;
}
上面代码中情况如下:
子进程会继承父进程已经打开的文件描述符,所以子进程和父进程的fd都指向了相同的文件表。由于共享相同的文件表,所以父子进程拥有共同的读写位置,不会出现写覆盖的情况。