同一进程共享操作相同的文件

在同一个进程中多次open打开同一文件时,文件描述符可能会相同吗?

答:不可能。在同一进程里面,一旦某个文件描述符被用了,在close释放之前,别人不可能使用,所以指向同一文件的描述符不可能相同。

代码演示

 #include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>
#include <unistd.h> #define FILE_NAME "./file.txt" void print_error(char * str)
{
perror(str);
exit(-);
} int main(void)
{
int fd1 = ;
int fd2 = ; fd1 = open(FILE_NAME, O_RDWR|O_TRUNC|O_APPEND);
if(- == fd1) print_error("1 open fail"); fd2 = open(FILE_NAME, O_RDWR|O_TRUNC|O_APPEND);
if(fd2 == -) print_error("2 open fail"); printf("fd1 = %d, fd2 = %d\n", fd1, fd2); while()
{
write(fd2, "world\n", );
sleep();
write(fd1, "hello\n", );
} return ;
}

file.txt文件内容会出现覆盖写情况,看看共享操作时的文件描述符表长啥样

由图知道,正是由于不同的文件描述符,各自对应一个独立的文件表,在文件表中有属于自己的“文件位移量”,开始时都是0。各自从0开始写,每写一个字节向后移动一个字节,他们写的位置是重叠的,因此肯定会相互的覆盖。

问题如何解决

指定O_APPEND即可解决。必须每个open都要指定,有一个不指定就会覆盖,就先过马路一样,都要准守交通规则才能安全,开车的和行人,只要有一个不准守都会出事。

为什么使用O_APPEND可以解决?

文件长度信息是大家共享的,当文件被写入数据后,文件长度就会被更新,都指定O_APPEND后,使用不同的文件描述符写数据时,都会使用文件长度更新自己的文件位移量,保证每次都是在文件的最末尾写数据,就不会出现相互覆盖的情况。

多个进程之间,共享操作相同文件

代码演示

shareOp_file1.c

 #include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>
#include <unistd.h> #define FILE_NAME "./file.txt" void print_error(char * str)
{
perror(str);
exit(-);
} int main(void)
{
int fd1 = ; fd1 = open(FILE_NAME, O_RDWR|O_TRUNC|O_APPEND);
if(- == fd1) print_error("1 open fail"); printf("fd1 = %d\n", fd1); while()
{
write(fd1, "hello\n", );
sleep();
} return ;
}

shareOp_file2.c

 #include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>
#include <unistd.h> #define FILE_NAME "./file.txt" void print_error(char * str)
{
perror(str);
exit(-);
} int main(void)
{
int fd1 = ; fd1 = open(FILE_NAME, O_RDWR|O_TRUNC|O_APPEND);
if(- == fd1) print_error("1 open fail"); printf("fd1 = %d\n", fd1); while()
{
write(fd1, "world\n", );
sleep();
} return ;
}

不同进程打开同一文件时,各自使用的文件描述符值可能相等,比如我们例子中的1和2进程,它们open后的描述符就相等。之所以相同,是因为不同的进程有自己独立的文件描述符池,都是0~1023的范围,各自分配自己的,有可能分派到相等值的文件描述符。

进程表 和 文件描述符表

覆盖的原因

和单个进程打开多个文件类似,这种情况也会覆盖写。其原因是因为因为各自有独立的文件位移量。

解决办法

同样的,指定O_APPEND标志,写操作时,使用文件长度去更新文件位移量,保证各自操作时,都在文件的尾部操作,就不会出现相互覆盖的情况。

父子进程共享操作文件

独立打开文件

这种情况即多个进程独立打开同一文件实现共享操作,只不过这里面进程之间是父子关系

代码演示

 #include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h> int main(void)
{
pid_t ret = ;
int fd = ; ret = fork();
if(ret > )
{
fd = open("./file.txt", O_RDWR|O_CREAT|O_APPEND, ); write(fd, "hello\n", );
}
else if(ret == )
{
fd = open("./file.txt", O_RDWR|O_CREAT|O_APPEND, ); write(fd, "world\n", );
} return ;
}

文件表结构

独立打开同一文件时,父子进程各自的文件描述符,指向的是不同的文件表。因为拥有不同的文件表,所以他们拥有各自独立的文件读写位置,会出现相互覆盖情况,如果不想相互覆盖,需要加O_APPEND标志。

fork之前打开文件

代码演示

 #include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h> int main(void)
{
pid_t ret = ;
int fd = ; fd = open("./file.txt", O_RDWR|O_CREAT, );
ret = fork();
if(ret > )
{
write(fd, "hello\n", );
printf("p, uid = %d, gid = %d\n", getuid(), getgid());
}
else if(ret == )
{
write(fd, "world\n", );
printf("c, uid = %d, gid = %d\n", getuid(), getgid());
} return ;
}

文件表结构

子进程会继承父进程已经打开的文件描述符,如果父进程的3描述符指向了某个文件,子进程所继承的文件描述符3也会指向这个文件。像这种继承的情况,父子进程这两个相同的“文件描述符”指向的是相同的“文件表”。由于共享的是相同的文件表,所以拥有共同的文件读写位置,不会出现覆盖的情况。

子进程的0 1 2这三个打开的文件描述符,其实也是从父进程那里继承过来的,并不是子进程自己去打开的,同样的父进程的0 1 2又是从它的父进程那里继承过来的,最根溯源的话,都是从最原始的进程哪里继承过来的,参考:进程控制——三大主要进程 ,最原始的进程是init进程。

init进程会去打开标准输入,标注输出、标准出错输出这三个文件,然后0 1 2分别指向打开的文件,之后所有进程的0 1 2,实际上都是从最开始的init进程那里继承而来的。

最新文章

  1. 为什么说在使用多条件判断时switch case语句比if语句效率高?
  2. NOSDK--SDK一键打包及统一接入的实现(前言)
  3. wex5 实战 登陆帐号更换与用户id一致性
  4. 一步一步来做WebQQ机器人-(一)(验证码)
  5. C# winform应用程序仅能打开一个进程运行
  6. POJ 1661 Help Jimmy LIS DP
  7. oracle 序列 详解
  8. 利用procdump+Mimikatz 绕过杀软获取Windows明文密码(转)
  9. [GIF] GIF Loop Coder - Interpolation
  10. python中的列表和字典
  11. QProcess进程间双向通信
  12. Unix/Linux环境C编程入门教程(26) 字符数字那些事儿
  13. Android 开发笔记 “Android 的消息队列模型”
  14. C# 调用CMD执行命令行
  15. C语言中,#include &lt;&gt;和#include &quot;&quot;的区别和注意点
  16. org.activiti.engine.activitiexception:version of activiti database(5.22) is more recent than the engine(5.12)
  17. Stable Fur Generation on Mesh
  18. elasticsearch开启外网访问
  19. Java中apache下面FtpClient主动模式和被动模式
  20. RabbitMQ None of the specified endpoints were reachable

热门文章

  1. 华为Liteos移植到stm32F03ZE
  2. 为什么linux系统中init被systemd替换了
  3. Python绘制可爱的卡通人物 | 【turtle使用】
  4. 32.网络编程TCP/UDP服务
  5. 浅谈&gt;/dev/null 2&gt;&amp;1
  6. TCP的socket连接
  7. 真理胜于一切 JAVA模拟表单提交
  8. C++多线程基础学习笔记(十)
  9. Django-djangorestframework-异常模块-源码及自定义异常
  10. spark调优篇-数据倾斜(汇总)