system call open()
from: http://linux2fork.blogspot.com/2013/04/system-call-open.html
今天review linux character driver的時候,發現往往定義的file_operations的.open成員,參數擁有file以及inode參數,可是這個參數哪邊來的?
其實會對open()有好奇,主要也是之前一直在trace linux kernel source內一些socket的東西,socket本身由sockfs支援,不使用open()而用socket()來開啟inode,所以想說兩者的區別是?
kernel內用 inode 結構體用來表示data。因此,它和 file structure 用來表示一個打開了的fd並不相同。對於一個inde,可能會有多個 file structure 對應著多個已打開的多個fd,但是這都只能指向同一個 inode 結構。
回頭看open(),大多數的system call使用interrupt 0x80,所以很快地追蹤到sys_open()是理所當然的入口
簡化整個call stack為sys_open()=>filp_open()=>dentry_open()=>dentry_open()
在呼叫register_chrdev()的時候將有覆寫的file_operations賦予device
底下是一些重要的工作,完整的source code就不列了
sys_open() : 配置fd
filp_open() : 依賴路徑取得nameidata結構,跟著呼叫dentry_open()取回file instance
dentry_open() : 配置file instance,nameidata結構內包含了inode instance,此時將一些必要資料由inode拷貝到file instance,f->f_op = fops_get(inode->i_fop);,此時f->f_op->open(inode,f)就是當時register_chrdev()所註冊的file_operations。
其實會對open()有好奇,主要也是之前一直在trace linux kernel source內一些socket的東西,socket本身由sockfs支援,不使用open()而用socket()來開啟inode,所以想說兩者的區別是?
kernel內用 inode 結構體用來表示data。因此,它和 file structure 用來表示一個打開了的fd並不相同。對於一個inde,可能會有多個 file structure 對應著多個已打開的多個fd,但是這都只能指向同一個 inode 結構。
回頭看open(),大多數的system call使用interrupt 0x80,所以很快地追蹤到sys_open()是理所當然的入口
簡化整個call stack為sys_open()=>filp_open()=>dentry_open()=>dentry_open()
在呼叫register_chrdev()的時候將有覆寫的file_operations賦予device
底下是一些重要的工作,完整的source code就不列了
sys_open() : 配置fd
filp_open() : 依賴路徑取得nameidata結構,跟著呼叫dentry_open()取回file instance
dentry_open() : 配置file instance,nameidata結構內包含了inode instance,此時將一些必要資料由inode拷貝到file instance,f->f_op = fops_get(inode->i_fop);,此時f->f_op->open(inode,f)就是當時register_chrdev()所註冊的file_operations。
file struct里的f_op是inode里的i_fop,文件系统的read_iter/write_iter等file_operations函数集合是先赋值给inode的i_fop成员,然后在dentry_open()里将inode.i_fop赋值给file的f_op. 如下是ext4 fs file_operations函数集赋值给inode.i_fop:
fs/ext4/namei.c
static int ext4_create(struct inode *dir, struct dentry *dentry, umode_t mode, bool excl) { handle_t *handle; struct inode *inode; int err, credits, retries = 0; err = dquot_initialize(dir); if (err) return err; credits = (EXT4_DATA_TRANS_BLOCKS(dir->i_sb) + EXT4_INDEX_EXTRA_TRANS_BLOCKS + 3); retry: inode = ext4_new_inode_start_handle(dir, mode, &dentry->d_name, 0, NULL, EXT4_HT_DIR, credits); handle = ext4_journal_current_handle(); err = PTR_ERR(inode); if (!IS_ERR(inode)) { inode->i_op = &ext4_file_inode_operations; inode->i_fop = &ext4_file_operations; ext4_set_aops(inode); err = ext4_add_nondir(handle, dentry, inode); if (!err && IS_DIRSYNC(dir)) ext4_handle_sync(handle); } if (handle) ext4_journal_stop(handle); if (err == -ENOSPC && ext4_should_retry_alloc(dir->i_sb, &retries)) goto retry; return err; }
vfs_read
如果file.f_op.read没有实现,实现了file.f_op.read_iter,则vfs_read会调用new_sync_read(),在这个函数里,会构建一个kiocb struct,其中的ki_pos即是ppos,即read file的offset:
fs/read_write.c
static ssize_t new_sync_read(struct file *filp, char __user *buf, size_t len, loff_t *ppos) { struct iovec iov = { .iov_base = buf, .iov_len = len }; struct kiocb kiocb; struct iov_iter iter; ssize_t ret; init_sync_kiocb(&kiocb, filp); kiocb.ki_pos = *ppos; iov_iter_init(&iter, READ, &iov, 1, len); ret = call_read_iter(filp, &kiocb, &iter); BUG_ON(ret == -EIOCBQUEUED); *ppos = kiocb.ki_pos; return ret; }
參考資料:
http://hi.baidu.com/potyzhang/item/ae9993a919e86f17a8cfb793
http://hi.baidu.com/heiyebujianwo/item/fa7fe543d99b73ab61d7b9cb