(2.5)文件和目录操作——Linux文件共享

时间:2021-07-28 00:44:35


文章目录

  • ​​1.文件共享​​
  • ​​2.不同进程共享相同文件​​
  • ​​3.父子进程共享相同的文件​​
  • ​​4.同一进程共享相同的文件​​
  • ​​(1)方法1:多次使用相同的函数打开一个相同的文件​​
  • ​​(2)方法2:使用dup()和dup2()​​
  • ​​4.线程共享文件​​
  • ​​5.进程间文件描述符的传递​​

1.文件共享

  • UNIX/Linux支持不同进程间共享文件。内核使用的三种表(文件描述符表、文件表、索引结点表)之间的关系决定了在文件共享方面一个进程对另一个进程可能产生的影响
    (1) 每个进程在进程表中有一个文件描述符表,每个描述符表项指向一个文件表
    (2) 内核为每一个被打开的文件维护一张文件表,文件表项包含
    • 文件的状态标志(读、写、同步、非阻塞)
    • 文件当前位置
    • 指向该文件索引节点表的指针
    (3) 每个文件(或设备)都有一个索引节点,它包含了文件类型属性及文件数据
  • 如果两个进程分别打开同一个的文件(物理文件),则它们有不同的文件表,因此每个进程有自己的文件当前位置,因此其读写操作互不影响。
  • 也存在不同进程共享同一个文件表(父子进程),或同一进程共享同一个文件表(dup操作)。
    此时,两个进程对该文件的读写操作将基于同一个文件当前位置。

2.不同进程共享相同文件

(2.5)文件和目录操作——Linux文件共享

3.父子进程共享相同的文件

(2.5)文件和目录操作——Linux文件共享

4.同一进程共享相同的文件

(1)方法1:多次使用相同的函数打开一个相同的文件

(2.5)文件和目录操作——Linux文件共享

(2)方法2:使用dup()和dup2()

  • 经常用来重定向进程的标准输入,标准输出和标准错误流
头文件: unistd.h

函数原型
int dup( int oldfd );
传入旧的fd,会产生新的fd,新的fd是传入旧的fd的拷贝,即:这俩描述符共享同一个数据结构。

int dup2( int oldfd, int targetfd );
可以规定有效描述符和目标描述符的ID

函数说明:
复制一个文件的描述符, dup2函数跟dup函数相似,但dup2函数允许调
用者规定一个有效描述符和目标描述符的

返回值:
dup2函数成功返回时,目标描述符(dup2函数的第二个参数)将变成源描
述符(dup2函数的第一个参数)的复制品,这俩描述符指向了同一个文件,并
且是函数的第一个参数指向的文件
  • eg
int fd1, fd2;
fd2 = dup( fd1 );


int oldfd;
oldfd = open("app_log", (O_RDWR | O_CREATE), 0644 );
dup2( oldfd, 1 );
/*
新打开的fd会替换掉由1代表的fd;
1是标准输出stdout的ID,任何写到stdout的东西都将写到“app_log”的文件中
*/
close( oldfd );
/*
不会关闭新打开的文件描述符,这是因为文件描述符1也指向了他
*/
  • 同一进程通过dup操作共享文件表

4.线程共享文件

  • 线程的定义: 有时称轻量级进程,是进程中的一个执行线路或线索,是一个相对独立的、可独立调度和指派的执行单元
    进程包括:程序,资源,执行三部分。
    程序指的是代码;
    资源指的是内存资源,IO资源,信号处理等部分;
    程序的执行指的是执行上下文,包括对CPU的占用。
  • 线程的创建: 应用程序可以通过一个统一的clone()系统调用接口,用不同的参数指定创建轻量进程还是普通进程。
  • clone()调用do_fork()创建线程, do_fork()参数为:
(CLONE_VM | CLONE_FS | CLONE_FILES | CLONE_SIGHAND)
这四个表示共享内存,共享文件系统访问计数,共享文件描述符表,以及共享信号处理方式等。
前三个共享tast_struct数据结构中的mm,fs,以及files

CLONE_VM:
do_fork()需要调用copy_mm()来设置task_struct中的mm和active_mm项,这两个mm_struct数据与进程所关联的内存空间
相对应。如果do_fork()时指定了CLONE_VM开关, copy_mm()将把新的task_struct中的mm和active_mm设置成与current
的相同,同时提高该mm_struct的使用者数目(mm_struct::mm_users)。
重点:也就是说,轻量级进程与父进程共享内存地址空间。

CLONE_FS:
task_struct中利用fs(struct fs_struct *)记录了进程所在文件系统的根目录和当前目录信息, do_fork()时调用copy_fs()
复制了这个结构;而对于轻量级进程则仅增加fs->count计数,与父进程共享相同的fs_struct。
重点:也就是说,轻量级进程没有独立的文件系统相关的信息,进程中任何一个线程改变当前目录、根目录等信息都将直接影响到其他线程。

CLONE_FILES:
一个进程可能打开了一些文件,在进程结构task_struct中利用files(struct files_struct *)来保存进程打开的文
件结构(struct file)信息, do_fork()中调用了copy_files()来处理这个进程属性;轻量级进程与父进程是共享该结构的,
copy_files()时仅增加files->count计数。
重点:这一共享使得任何线程都能访问进程所维护的打开文件,对它们的操作会直接反映到进程中的其他线程。

CLONE_SIGHAND:
每一个Linux进程都可以自行定义对信号的处理方式,在task_struct中的sig(struct signal_struct)中使用一个struct
k_sigaction结构的数组来保存这个配置信息, do_fork()中的copy_sighand()负责复制该信息;轻量级进程不进行复制,而
仅仅增加signal_struct::count计数,与父进程共享该结构。
重点:也就是说,子进程与父进程的信号处理方式完全相同,而且可以相互更改。
  • 总结: 线程间所有文件结构都为共享资源,不但“文件表项”(file对象)是共享的,就连“文件描述符表”(files_struct结构)也是共享的。
    (1)线程的创建仅仅增加的是files和fs的引用计数,“文件打开计数”(file对象的引用计数)并没有增加,所以任何一个线程对打开的文件执行close操作,文件都将关闭(文件打开计数为1的情况)。
    (2)但是如果线程不进行打开文件的关闭,则文件直到进程结束时才会关闭,这就是使用多线程实现tcp服务器时,服务线程必须要显示调用close的原因,否则永远不会发送FIN终止链接(因为主线程一直处于监听不会结束)。

5.进程间文件描述符的传递

  • 传递描述符的函数的参数是fd, fd是打开文件指针在数组中的下标
  • 将一个文件描述符传递给另一个进程后,文件的“访问计数”会增加
  • 进程间传递文件描述符可以看做跨进程的dup调用,也就是同一个file对象在不同进程间的映射
    这和两个进程打开同一个文件的结果相同,只是接受文件描述符的进程少了open()步骤而已
  • 对于网络接口返回的描述符 ,只能采取传递文件描述符的方法。
  • UNIX系统中两个方法: BSD sendmsg, recvmsg方法; SYSV ioctl方法
  • 进程间传递文件描述符时,发送进程和接收进程共享同一文件表项
  • 进程间文件描述符的传递,只是通过内核将接收文件的一个新file指针,指向和发送进程的同一个file对象,并使这个file对象的引用计数增加
  • eg: