file_table是file类型. 如下:
struct file {
uint32_t fd_pos;
uint32_t fd_flag;
struct inode* fd_inode;
};
file没有引用计数字段. 实际上应该有引用计数字段. 为什么需要引用计数字段, 这要到实现fork时才有清晰的原因.
sys_fork调用了fork, fork调用了copy_process, 在copy_process中, 有update_inode_open_cnts, 这个函数干的事情是
static void update_inode_open_cnts(struct task_struct* thread) {
int32_t local_fd = 3, global_fd = 0;
while (local_fd < MAX_FILES_OPEN_PER_PROC) {
global_fd = thread->fd_table[local_fd];
ASSERT(global_fd < MAX_FILE_OPEN);
if (global_fd != -1) {
file_table[global_fd].fd_inode->i_open_cnts++;
}
local_fd++;
}
}
再看看sys_close的实现
int32_t sys_close(int32_t fd) {
int32_t ret = -1;
if (fd > 2) {
uint32_t _fd = fd_local2global(fd);
ret = file_close(&file_table[_fd]);
running_thread()->fd_table[fd] = -1;
}
return ret;
}
file_close是:
int32_t file_close(struct file* file) {
if (file == NULL) {
return -1;
}
file->fd_inode->write_deny = false;
inode_close(file->fd_inode);
file->fd_inode = NULL; // 这一行使file_table中的项失效
return 0;
}
现在来举出一个场景. 父进程fork出子进程, 子进程sys_close了一个文件, 看看上面的实现, file_table中的项必然失效了. 父进程即时不调用sys_close, 也会失效(因为子进程和父进程共用file_table). 其实书里也是有意识要解决这个问题的, 做的事就是前面列出过的update_inode_open_cnts, 但是那只是inode的引用计数++, 没有用. 我认为inode的引用计数不应该++, 应该在struct file中加上引用计数字段.