系统编程-进程-先后fork或open一个文件的区别


关联博文:

 

当文件操作遇上fork

 

 

Linux内核的文件结构体

struct file {
    .........    
    struct path        f_path;            //文件的路径
#define f_dentry    f_path.dentry
#define f_vfsmnt    f_path.mnt
    const struct file_operations    *f_op;//访问方式
    atomic_long_t        f_count;        //文件的引用计数,引用计数值为0时,文件才会关闭
    unsigned int         f_flags;        //标志位
    mode_t            f_mode;            //读写等权限
    loff_t            f_pos;             //读到了哪个位置
    struct fown_struct    f_owner;
    unsigned int        f_uid, f_gid;   //文件所属uid,gid
    struct file_ra_state    f_ra;

    u64            f_version;
#ifdef CONFIG_SECURITY
    void            *f_security;
#endif
    /* needed for tty driver, and maybe others */
    void            *private_data;
    
    .............
};

文件的引用计数,引用计数值为0时,文件才会被关闭。

 

两种情景分析:

1.fork()之前就open了文件,并且读取了部分内容

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

int main()
{
    int fd = open("tmp.txt",O_RDONLY);
    assert(fd!=-1);

    char buff[10]={0};

    pid_t pid = fork();

    if(pid == 0){
            read(fd,buff,1);  //子进程读1个字节内容
        printf("buff = %s\n",buff);

    }else if(pid  > 0){
        sleep(5);         //保证子进程可以先读文件
        read(fd,buff,2);  //父进程读2个字节
        printf("buff = %s\n",buff);
        wait(NULL);       //收尸,不获取死因
    }
    
    close(fd);
    return 0;    
}

 tmp.txt的内容为“niceday”

root@lmw-virtual-machine:/home/lmw/MINE/Linux_C_exercise/process# gcc process.c  -o ab
root@lmw-virtual-machine:/home/lmw/MINE/Linux_C_exercise/process# 
root@lmw-virtual-machine:/home/lmw/MINE/Linux_C_exercise/process# ./ab
buff = n
buff = ic

小结:

先open再fork,文件只被打开一次,父子进程共享一个文件描述信息,包括引用计数、读取位置等等。

虽然父子进程都读取了该文件,由于文件的引用计数始终为1,所以只需要close一次即可。

可以在子进程代码分支中close,也可以在父进程代码分支close,还可以在公共部分close。

子进程复制了父进程的文件表项指针, 指向的是同一个文件表项,如下图:

 

 

2.fork()调用之后open文件

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

int main()
{
    pid_t pid = fork();

    int fd = open("tmp.txt",O_RDONLY);
    assert(fd!=-1);

    char buff[10]={0};

    if(pid == 0){
            read(fd,buff,1);  //子进程读1个字节内容
        printf("buff = %s\n",buff);

    }else if(pid  > 0){
        sleep(5);         //保证子进程可以先读文件
        read(fd,buff,2);  //父进程读2个字节
        printf("buff = %s\n",buff);
        wait(NULL);       //收尸,不获取死因
    }
    
    close(fd);
    return 0;    
}

tmp.txt的内容为“niceday”, 同上。

root@lmw-virtual-machine:/home/lmw/MINE/Linux_C_exercise/process# gcc process.c  -o ab
root@lmw-virtual-machine:/home/lmw/MINE/Linux_C_exercise/process# 
root@lmw-virtual-machine:/home/lmw/MINE/Linux_C_exercise/process# ./ab
buff = n
buff = ni

小结:

此时的文件描述信息不再是共享的,一个文件被打开了两次,即引用计数值为2,每个进程都有自己的一份,所以两个进程读写操作互不影响。

 

总结:

先open,再fork:子进程无条件继承父进程的文件描述信息,子进程和父进程指向同一个文件描述信息。
先fork,再open:子进程有自己的配置,和父进程的配置是相互独立的关系。所谓配置,例如文件描述信息。

 

文末,再来一幅图(不一样的精彩)

 

 

 

 

 

.

posted @ 2020-10-15 22:35  一匹夫  阅读(546)  评论(0编辑  收藏  举报