5.1 Open操作
系統調用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;
/* 擷取目前程序打開檔案表。*/
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;
* 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);
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;
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);
goto cleanup_all;
if (f->f_op && f->f_op->open) {
error = f->f_op->open(inode,f);
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);
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結構指針為空,那傳回出錯。*/
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:
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;
if (!atomic_dec_and_lock(&dentry->d_count, &dcache_lock))
/* 這裡隻對dentry->d_count做減1操作,如果減1後dentry->d_count不為0,那立刻傳回。*/
/* 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
close操作用到的主要函數的調用關系結構如下圖所示:
sys_close
_put_unused_fd
file_close
fput
dput
dentry_iput
mvtput
iput
圖7
<a href="http://hi.baidu.com/tag/%E5%9C%A8csdn%E6%91%98%E5%BD%95%E7%9A%84unix%E6%96%87%E4%BB%B6%E7%B3%BB%E7%BB%9F%E6%A8%A1%E6%8B%9F%E7%9A%84%E5%AE%9E%E7%8E%B0/feeds">#在csdn摘錄的unix檔案系統模拟的實作</a>