系统调用open是由函数sys_open(fs/open.c)实现的。该系统调用用来获得欲访问文件的文件描述符。如果文件不存在则可以用它创建一个新文件。打开的文件不存在时,使用三个参数形式:filename,falges,mode。
⑴参数filename是指向所要打开文件的路径名指针。
⑵参数flags指定打开文件的方式,它必须包含下列三个值之一:O_RDONLY(只读打开);O_WRONLY(只写打开);O_RDWR(读/写打开)
⑶参数mode指定对文件所有者、文件用户组及系统中所有其他用户的访问权限位。
具体实现如下:
asmlinkage long sys_open(const char * filename, int flags, int mode)
{
char * tmp;
int fd, error;
#if BITS_PER_LONG != 32
flags |= O_LARGEFILE;#endif
tmp = getname(filename);
/* 调用getname()函数作路径名的合法性检查。getname()在检查名字错误的同时要
把filename从用户区拷贝到核心资料区。它会检查给出的地址是否在当前进程的虚
存段内,在核心空间中申请一页,并不断把filename字符的内容拷贝到该页中去。
这样是为了使系统效率提高,减少不比要的操作。*/
fd = PTR_ERR(tmp);
if (!IS_ERR(tmp)) {
fd = get_unused_fd();
/* 调用get_unused_fd(),获取一个文件描述符。*/
if (fd >= 0) {
struct file *f = filp_open(tmp, flags, mode);
/* 调用filp_open(),获取文件对应的filp结构。*/
error = PTR_ERR(f);
if (IS_ERR(f))
goto out_error;
/* 如果所获file结构有错误,跳到out_error处释放文件描述符,返回出错。*/
fd_install(fd, f);
/* 调用fd_install()将打开文件的file结构装入当前进程打开文件表中。*/
}
out:
putname(tmp);
}
return fd;
out_error:
put_unused_fd(fd);
fd = error;
goto out;
}
int get_unused_fd(void)
{
struct files_struct * files = current->files;
/* 获取当前进程打开文件表。*/
int fd, error;
error = -EMFILE;
write_lock(&files->file_lock);
repeat:
fd = find_next_zero_bit(files->open_fds,
/* files->open_fds是打开文件描述符的位图,files->open_fds为下一个可以打开
文件的描述符,files->max_fdset为可以打开文件的最大数目。通过这些参数,我们
调用find_next_zero_bit()来获取一个空闲的文件描述符。*/
files->max_fdset,
files->next_fd);
/*
* N.B. For clone tasks sharing a files structure, this test
* will limit the total number of files that can be opened.
*/
if (fd >= current->rlim[RLIMIT_NOFILE].rlim_cur)
/* 如果fd大于进程可用最大文件数目直接退出。*/
goto out;
/* Do we need to expand the fdset array? */
if (fd >= files->max_fdset) {
/* 如果fd大于files->max_fdset,那调用expand_fdset()扩展files->open_fds
与files->close_on_exec,用于标记更多的文件。如果扩展成功,那要跳回上面,重
新调用find_next_zero_bit()来获取一个文件描述符。如果出错则退出。*/
error = expand_fdset(files, fd);
if (!error) {
error = -EMFILE;
goto repeat;
}
goto out;
}
/*
* Check whether we need to expand the fd array.
*/
if (fd >= files->max_fds) {
/* 如果fd大于files->max_fds,那调用expand_fd_array()扩展file结构数组fd
(这个fd是指files_struct中的struct file * * fd,与该程序中的变量fd 不同)。
如果扩展成功,那要跳回到上面重新调用find_next_zero_bit()来获取一个文件描述
符。如果出错则退出。*/
error = expand_fd_array(files, fd);
if (!error) {
error = -EMFILE;
goto repeat;
}
goto out;
}
FD_SET(fd, files->open_fds);
/* 将files->open_fds中相应的fd位置位。*/
FD_CLR(fd, files->close_on_exec);
/* 清除files->close_on_exec中的相应位。*/
files->next_fd = fd + 1;
#if 1
/* Sanity check */
if (files->fd[fd] != NULL) {
/*如果files->fd[fd]不为空,那打印出提示信息并将其置空。*/
printk(KERN_WARNING "get_unused_fd: slot %d not NULL!\n", fd);
files->fd[fd] = NULL;
}
#endif
error = fd;
out:
write_unlock(&files->file_lock);
return error;
}
struct file *filp_open(const char * filename, int flags, int mode)
{
int namei_flags, error;
struct nameidata nd;
namei_flags = flags;
if ((namei_flags+1) & O_ACCMODE)
namei_flags++;
if (namei_flags & O_TRUNC)
namei_flags |= 2;
error = open_namei(filename, namei_flags, mode, &nd);
if (!error)
return dentry_open(nd.dentry, nd.mnt, flags);
/* filp_open()的工作大体分成两部分1.调用open_namei(),通过路径名获取其相
应的dentry与vfsmount结构。 2.调用dentry_open(),通过open_namei得到dentry
与vfsmount来得到file结构。(下面马上介绍。)*/
return ERR_PTR(error);
}
调用dentry_open(),通过open_namei得到dentry与vfsmount来得到file结构。
struct file *dentry_open(struct dentry *dentry, struct vfsmount *mnt, int flags)
{
struct file * f;
struct inode *inode;
static LIST_HEAD(kill_list);
int error;
error = -ENFILE;
f = get_empty_filp();
/* 调用get_empty_filp(),创建一个file结构,并将这个结构链入anon_list链表。*/
if (!f)
goto cleanup_dentry;
/* 如果创建file结构失败,那释放dentry与vfsmount,返回出错。*/
f->f_flags = flags;
f->f_mode = (flags+1) & O_ACCMODE;
/*对file结构中的f_flags、f_mode赋值。*/
inode = dentry->d_inode;
if (f->f_mode & FMODE_WRITE) {
/* 检查对打开文件是否以写模式打开。若是则调用get_write_access()获取对文件的写权限。get_write_access()注意通过对inode 的i_writecount项的操作来实现其功能。*/
error = get_write_access(inode);
if (error)
goto cleanup_file;
}
f->f_dentry = dentry;
/ *对file结构中的相关项赋值。*/
f->f_vfsmnt = mnt;
f->f_pos = 0;
f->f_reada = 0;
f->f_op = fops_get(inode->i_fop);
file_move(f, &inode->i_sb->s_files);
/* 将file结构挂到超级块打开文件链表上。*/
/* preallocate kiobuf for O_DIRECT */
f->f_iobuf = NULL;
f->f_iobuf_lock = 0;
if (f->f_flags & O_DIRECT) {
error = alloc_kiovec(1, &f->f_iobuf);
if (error)
goto cleanup_all;
}
if (f->f_op && f->f_op->open) {
/* 将file结构挂到超级块打开文件链表上。*/
error = f->f_op->open(inode,f);
if (error)
goto cleanup_all;
}
f->f_flags &= ~(O_CREAT | O_EXCL | O_NOCTTY | O_TRUNC);
/* 改变file结构的标志项。*/
return f;
cleanup_all:
if (f->f_iobuf)
free_kiovec(1, &f->f_iobuf);
fops_put(f->f_op);
if (f->f_mode & FMODE_WRITE)
put_write_access(inode);
file_move(f, &kill_list); /* out of the way.. */
f->f_dentry = NULL;
f->f_vfsmnt = NULL;
cleanup_file:
put_filp(f);
cleanup_dentry:
dput(dentry);
mntput(mnt);
return ERR_PTR(error);
}
open操作用到的主要函数的调用关系结构如下图所示
sys_open
get_unused_fd
file_open
fd_install
open_namei
dentry_open
path_walk
real_lookup
图6
5.2 Close操作
关闭文件时要调用sys_close()来实现。关闭打开的文件描述符, int close (unsigned int fd),函数调用成功返回0值,否则返回-1值。由errno存放错误编号EBAD表示参数是一个有效的打开的文件描述符。
系统调用open()打开一个文件时系统就分配给文件一个文件描述符,同时为打开文件描述符的引用计数加1。调用close()关闭文件时打开文件描述符的引用计数减1。引用计数为0时才彻底关闭打开的文件。
具体实现如下:
asmlinkage long sys_close(unsigned int fd)
{
struct file * filp;
struct files_struct *files = current->files;
/* 将局部变量files指向当前进程的打开文件表。*/
write_lock(&files->file_lock);
/* 对该表设置读写自旋锁。*/
if (fd >= files->max_fds)
/* 如果函数传入参数fd(打开文件描述符)大于该表的最大打开文件数目则返回出错。*/
goto out_unlock;
filp = files->fd[fd];
/* 根据文件描述符,从打开文件表中得到相应文件的file结构。*/
if (!filp)
/* 如果对应的file结构指针为空,那返回出错。*/
goto out_unlock;
files->fd[fd] = NULL;
/* 将打开文件表中文件描述符所对应的指针置为空。*/
FD_CLR(fd, files->close_on_exec);
/* 将close_on_exec中的fd位置为0。*/
__put_unused_fd(files, fd);
/* 将open_fds项的fd位置为0。并且比较一下fd与next_fd,那将fd的值赋给next_fd,作为未使用的文件描述符提供给后面打开的文件。*/
write_unlock(&files->file_lock);
/* 解开读写自旋锁。*/
return filp_close(filp, files);
/* 调用file_close()做余下的工作。*/
out_unlock:
write_unlock(&files->file_lock);
return -EBADF;
}
int filp_close(struct file *filp, fl_owner_t id)
{
int retval;
if (!file_count(filp)) {
/* 读取文件引用数。若为0直接返回。*/
printk(KERN_ERR "VFS: Close: file count is 0\n");
return 0;
}
retval = 0;
if (filp->f_op && filp->f_op->flush) {
lock_kernel();
retval = filp->f_op->flush(filp);
unlock_kernel();
}
/* 如果文件系统有自己的操作函数flush,那直接调用该函数。在这个操作时要设置内核锁。*/
fcntl_dirnotify(0, filp, 0);
locks_remove_posix(filp, id);
fput(filp);
/* 调用fput()释放文件对应的file结构。*/
return retval;
}
void fput(struct file * file)
{
struct dentry * dentry = file->f_dentry;
struct vfsmount * mnt = file->f_vfsmnt;
struct inode * inode = dentry->d_inode;
if (atomic_dec_and_test(&file->f_count)) {
/* 对文件引用数减1,并判断&file->f_count减1后是否为0。若为0则做后面的工作,否则结束该函数。*/
locks_remove_flock(file);
/* 除去文件锁。*/
if (file->f_op && file->f_op->release)
file->f_op->release(inode, file);
/* 如果文件系统有自己的release函数,那直接调用该函数来释放该文件。*/
fops_put(file->f_op);
/* 减少文件操作函数模块的引用次数。*/
file->f_dentry = NULL;
file->f_vfsmnt = NULL;
/* 将file->f_dentry、file->f_vfsmnt置为NULL, 但此时并没有释放相对应的dentry与vfsmount结构,局部变量dentry和mnt还指向这两个结构。*/
if (file->f_mode & FMODE_WRITE)
put_write_access(inode);
/* 如果f_mode的FMODE_WRITE位被置位了,那么调用put_write_access,将文件对应的inode的i_writecount项减1。*/
dput(dentry);
/* 调用dput释放dentry。*/
if(mnt);
/* 如果mnt存在,调用mntput释放vfsmount。*/
mntput(mnt);
file_list_leck();
list_del(&file->f_list);
list_add(&file->f_list, &free_list);
files_stat.nr_free_files++;
/* 将该节点从超级块中的打开文件链表中删除,加到free_list链表上,并对files_stat.nr_free_files做加1操作。*/
file_list_unlock();
}
}
void dput(struct dentry *dentry)
{
if (!dentry)
/* 如果dentry为空,那立刻返回。*/
return;
repeat:
if (!atomic_dec_and_lock(&dentry->d_count, &dcache_lock))
/* 这里只对dentry->d_count做减1操作,如果减1后dentry->d_count不为0,那立刻返回。*/
return;
/* dput on a free dentry? */
if (!list_empty(&dentry->d_lru))
/* 如果该dentry在d_count= =0的队列(即dentry_unused)上,那么调用BUG()
BUG(); */
/*
* AV: ->d_delete() is _NOT_ allowed to block now.
*/
if (dentry->d_op && dentry->d_op->d_delete) {
/* 如果该dentry有自己的d_delete()函数,那直接调用它,指向完之后,调到unhash_it。*/
if (dentry->d_op->d_delete(dentry))
goto unhash_it;
}
/* Unreachable? Get rid of it */
if (list_empty(&dentry->d_hash))
/* 如果该dentry不在hash表上,那跳到kill_it。*/
goto kill_it;
list_add(&dentry->d_lru, &dentry_unused);
/* 将dentry加到dentry_unused队列上。*/
dentry_stat.nr_unused++;
/*
*Update the timestamp
*/
dentry->d_reftime = jiffies;
/* 更新dentry的时间戳,记录它最后一次引用的时间。*/
spin_unlock(&dcache_lock);
return;
unhash_it:
list_del_init(&dentry->d_hash);
/* 将dentry从hash表中删除。*/
kill_it: {
struct dentry *parent;
list_del(&dentry->d_child);
/* 将该dentry从其同一级目录链表中删除。*/
/* drops the lock, at that point nobody can reach this dentry */
dentry_iput(dentry);
/* 用于删除dentry对应的inode。*/
parent = dentry->d_parent;
d_free(dentry);
/* 释放dentry的内存空间。*/
if (dentry == parent)
return;
dentry = parent;
/* 将变量dentry指向目前目录的父目录的dentry结构,跳到repeat。这样是为了将父目录dentry结构的引用次数减1,直到根目录为止。*/
goto repeat;
}
}
static inline void dentry_iput(struct dentry * dentry)
{
struct inode *inode = dentry->d_inode;
/* 判断一下dentry中的inode是否为空。*/
if (inode) {
dentry->d_inode = NULL;
/* 将dentry->d_inode指向空。*/
list_del_init(&dentry->d_alias);
/* 将该dentry结构从inode的i_dentry链表中删去。i_dentry链表将通过硬链接指向同一个inode的dentry结构。*/
spin_unlock(&dcache_lock);
if (dentry->d_op && dentry->d_op->d_iput)
/* 如果该dentry中带有自己的d_iput的函数,则调用它。否则调用iput函数。*/
dentry->d_op->d_iput(dentry, inode);
else
iput(inode);
} else
spin_unlock(&dcache_lock);
}
close操作用到的主要函数的调用关系结构如下图所示:
sys_close
_put_unused_fd
file_close
fput
dput
dentry_iput
mvtput
iput
图7