多进程打开同一个文件问题分析

一、疑问

1、当多个进程同时打开同一个文件,同时对同一个文件写操作,文件的内容是会被覆盖只有一份还是不会覆盖会有两份内容?

二、问题验证

1、来点代码

#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <fcntl.h>
#include <string.h>
#include <sys/time.h>


int main(int argc, char *argv[])
{
	int rc = 0;
	int wc = 0;
	int count = 0;
	int fd = 0;
	int len = 0;
	char str[80] = {0};
	struct timeval val;

	printf("process is=%d\n", (int)getpid());

#ifdef OUT
	fd = open("./test.txt", O_RDWR | O_CREAT);
#endif
	rc = fork();
	switch (rc) {
		case -1:
			printf("fork error.\n");
			exit(1);
		case 0:
#ifdef IN
			fd = open("./test.txt", O_RDWR | O_CREAT);
#endif
			printf("child process is=%d, fd=%d\n", (int)getpid(), fd);
			while (count++ < 3) {
				memset(str, 0x00, (sizeof(str[0])/sizeof(str)));
				memset(&val, 0x00, sizeof(struct timeval));
				gettimeofday(&val, NULL);
				len = lseek(fd, 0, SEEK_CUR);
				sprintf(str, "s=%ld, us=%ld, xcount=%d, seek=%d\n", val.tv_sec, val.tv_usec, count, len);
				write(fd, str, strlen(str) + 1);
				printf("%s", str);

				sleep(1);
			}
			close(fd);
#ifdef IN
			close(fd);
#endif
			exit(EXIT_SUCCESS);
		default:
#ifdef IN
			fd = open("./test.txt", O_RDWR | O_CREAT);
#endif
			while (count++ < 5) {
				memset(str, 0x00, (sizeof(str[0])/sizeof(str)));
				memset(&val, 0x00, sizeof(struct timeval));
				gettimeofday(&val, NULL);
				len = lseek(fd, 0, SEEK_CUR);
				sprintf(str, "s=%ld, us=%ld, ycount=%d, seek=%d\n", val.tv_sec, val.tv_usec, count, len);
				write(fd, str, strlen(str) + 1);
				printf("%s", str);	
				sleep(1);
			}
			wc = wait(NULL);
			printf("parent process is=%d, rc=%d, wc=%d, cnt=%d, fd=%d\n", (int)getpid(), rc, wc, count, fd);
#ifdef IN
			close(fd);
#endif
			exit(EXIT_SUCCESS);
	}
#ifdef OUT
	close(fd);
#endif
	return 0;
}
gcc -DIN fork_fd.c -o fork_fdi
gcc -DOUT fork_fd.c -o fork_fdo

2、运行结果

第一个打印情况

第二个打印情况

三、问题分析

1、基础知识

1.1、大家都知道,open操作后最终会返回一个整型的文件描述符fd(一般都是从3开始)。进入task_struct中用来描述文件相关的结构体,继续往下找发现描述文件的结构体struct file,其中具体的struct file由文件描述符fd获取到。从而在struct file中就可以知道文件的存放路径f_path,文件的编号f_inode,文件当前的读写偏移量……

struct task_struct {
       /* Open file information: */
       ......
       struct files_struct                *files;
};


struct files_struct {
       ......
       struct file __rcu * fd_array[NR_OPEN_DEFAULT];
       ......
};


struct file {
       union {
               struct llist_node        fu_llist;
               struct rcu_head        fu_rcuhead;
       } f_u;
       struct path     f_path;
       struct inode    *f_inode;   //VFS描述文件信息的节点,还不是指向具体文件系统文件的inode
       const struct file_operations        *f_op;    //文件的操作函数,对应着驱动的实现

       /*

        * Protects f_ep, f_flags.

        * Must not be taken from IRQ context.

        */
       spinlock_t        f_lock;
       enum rw_hint      f_write_hint;
       atomic_long_t     f_count;
       unsigned int      f_flags;
       fmode_t           f_mode;
       struct mutex      f_pos_lock;
       loff_t            f_pos;                                //文件读写位置的偏移量
       struct fown_struct       f_owner;
       const struct cred        *f_cred;
       struct file_ra_state     f_ra;
}

2、具体原因

第一种情况,fork后的父子进程使用的是不同的fd文件,因此使用的是两份的文件表项,两个独立的位置点f_pos

第二种情况,fork后的父子进程共用fd文件,父进程和子进程打开相同的文件时,共享同一个文件表项,使用同一个位置点f_pos,当A进程写结束后,B进程写的位置点是A结束后的位置点。类似于dup系统调用,dup结束后fd会不一样,但是父子进程的fd是一样的,但是内核态共用的是同一份文件表项

 

  • 2
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值