通过open打开文件操作探究linux0.12的文件系统。
系统调用open是进程要存取一个文件中数据所必须采取的第一步(fs/open.c
)。系统调用open的语法格式是:
fd = open(pathname, flags, mode);
这里,pathname
是文件名;flags
指示打开的类型(读或写等);mode
给出文件的许可权(如果文件正在被建立)。open调用成功会返回一个称为文件描述符的整数fd
。
这个fd
到底代表什么呢?首先,我们需要知道每个任务(进程)结构中都会有一个用户文件描述符表(即filp[NR_OPEN]
数组),fd
即为该数组的索引。
filp[fd]
为一个文件结构的指针,指向系统内核中的文件表file_table[NR_FILE]
的某个文件结构;文件结构file
中保存有本文件被句柄引用的次数、偏移指针等以及用于找到文件的i节点指针;
文件结构中的i节点指针指向内存中的索引节点表中对应的i节点,然后就可以找到文件了。
需要明确的一点是,每个进程都有自己的用户文件描述符表,而系统内核中只有一个文件表和一个内存i节点表。每次open调用都会在进程的用户文件描述符表和内核的文件表中各分配一个表项。但在内核的索引节点表中,每个文件只有唯一的一个表项。
代码解析
/**
* 打开(或创建)文件
* @note 实际上open的操作是将进程中的文件描述符指向了系统中的文件表项,该文件表项又指向了打开的文件
* 索引节点(inode)。
* @param[in] filename 文件名
* @param[in] flag 打开文件标志
* @param[in] mode 文件属性
* @retval 成功返回文件句柄,失败返回出错码
*/
int sys_open(const char * filename, int flag, int mode)
{
struct m_inode * inode;
struct file * f;
int i, fd;
mode &= 0777 & ~current->umask;
/* 1. 在用户文件描述符表找到最小的文件描述符fd */
for(fd = 0; fd < NR_OPEN; fd ++) {
if (!current->filp[fd]) {
break;
}
}
if (fd >= NR_OPEN) {
return -EINVAL;
}
current->close_on_exec &= ~(1<<fd);
/* 2. 在文件表中找到空闲的文件结构项 */
f = 0 + file_table;
for (i = 0; i < NR_FILE; i++, f++) {
if (!f->f_count) {
break;
}
}
if (i >= NR_FILE) {
return -EINVAL;
}
(current->filp[fd] = f)->f_count++;
/* 3. 在内存的索引节点表中找到文件对应的i节点 */
if ((i = open_namei(filename, flag, mode, &inode)) < 0) {
current->filp[fd] = NULL;
f->f_count = 0;
return i;
}
/* ttys are somewhat special (ttyxx major==4, tty major==5) */
if (S_ISCHR(inode->i_mode)) {
if (check_char_dev(inode, inode->i_zone[0], flag)) {
iput(inode);
current->filp[fd] = NULL;
f->f_count = 0;
return -EAGAIN;
}
}
/* Likewise with block-devices: check for floppy_change */
if (S_ISBLK(inode->i_mode)) {
check_disk_change(inode->i_zone[0]);
}
f->f_mode = inode->i_mode;
f->f_flags = flag;
f->f_count = 1;
f->f_inode = inode;
f->f_pos = 0;
return (fd);
}