进程文件描述符:filedescrption,实际上我们调用open打开文件后得到的一个句柄,是个整数。属于用户区用来记录文件的一些信息,如文件指针,指向系统文件描述符表的指针,保存在进程的PCB中。得到的整数可以认为是系统描述符表的一个索引,实际上内核利用的是hash表完成系统文件描述符表,得到数字利用哈希算法得到相应struct file {}的内容。
标准输入STDIN fd = 0
标准输出 STDOUT fd = 1
标准错误输出 STDERR fd = 2
包含在 unistd.h 文件中
我们用户进程打开的文件最少从3开始,前三个文件描述符已经被占用,因为fd也是一种系统资源
在打开一个文件时,文件描述符总是从选择可以使用的文件描述符中最小的一个
当我们打开一个文件的时候:
int fd = open();
返回的句柄fd对应着一个结构体,而fd是用来索引系统的文件描述符表
系统描述符表保存的是file_struct{ ... }
系统文件描述符表:为系统所所有进程所共享,每次open他都会包含一个条目。每个系统文件表的条目都包含文件的偏移量访问模式以及文件引用计数等,即为文件描述符struct file{}中的内容。
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;
.............
};
进程中打开一个文件就得到一个文件描述符fd,
这个文件描述符fd是文件描述符表fdtable中的一项,
而每个进程的PCB都有一个指针指向内核中的文件描述符表,
进程通过该指针 拿着文件描述符 对应文件描述符的某一项,就可以查到文件对应的结构体
也就得到了文件的具体信息
之前,我们提到过,父子进程之间是共享文件描述符的。
父进程的文件描述符被拷贝给了子进程。
因此父进程打开的所有文件描述符都在子进程中保存了(每个进程都有独立的描述符)。
我在面试的时候遇到过两种情景分析:
1.fork()之前就open了文件,并且读取了部分内容;
问题分析:
在fork前就打开了文件,那么父子进程中的指针不是一样的,但是指向和内容却是一样的
所以,父子进程都是共享这个文件的描述符,也就是父子进程的谁的操作都会影响另外一个
假如子进程先读,读到一个位置,父进程再读,是不是会在子进程的基础上继续读,
还是从头开始读。
创建一个a.txt文件,写入内容:
hello world
跑个程序验证一下:
#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("a.txt",O_RDONLY);
assert(fd!=-1);
char buff[128]={0};
pid_t pid = fork();
if(pid == 0)
{
read(fd,buff,1);//我给buff中读进去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;
}
执行结果:
buff = h
buff = el
根据现象得结论:
1.先open再fork,父子进程共享一个文件文件描述信息,包括引用计数、读取位置等等
2.既然共享,那么上述现象不言而喻
虽然,父子进程都读取了该文件,但是文件的引用计数始终 == 1,所以需要close一次即可
可以在子进程代码分支中close,也可以在父进程代码分支close,还可以在公共部分close
真正记录文件信息的表单处于内核,是所有进程共享的,子进程复制了父进程的指针内容,
指向的是同一个文件描述符信息,发生上面的现象
2.fork()调用之后open文件:
我们将上面的代码做一个简单的修改。
将open放到fork下面,文件a.txt内容不变,依旧是hello world
程序验证:
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
#include<string.h>
#include<unistd.h>
#include<sys/wait.h>
#include<fcntl.h>
int main()
{
char buff[128]={0};
pid_t pid = fork();
int fd = open("a.txt",O_RDONLY);
assert(fd!=-1);
if(pid == 0)
{
read(fd,buff,1);//我给buff中读进去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;
}
执行结果:
buff = h
buff = he
根据现象得结论:
从执行结果我们可以发现,父子进程都是从文件开始处读取,并没有共享 读取位置等信息
此时文件得描述信息不再是共享得了。
而是每个进程都有自己得一份,所以两个进程读写操作都是互不影响得的
不同的是一个文件别打开两次,引用计数 == 2
文件表存在于内核,fork之后,父子进程指针指向的不是同一个描述信息,因为PCB已经独立文件表也独立
总结:
fork前进行open,子进程无条件继承父进程的文件描述信息,子进程和父进程指向一样文件描述信息
fork后进行open,子进程可以有自己的选择啊,不用继承父进程的所有,比如文件描述信息