用户通过open/read/ioctrl函数打开设备文件,库文件会通过会产生一个软中断,通过系统调用进入到内核,通过系统调用中断号,就可以跳到该中断的人口地址。
下面来看看open的调用过程,open的格式如下:
- int open(const char * pathname,int oflag, mode_t mode )
当open系统调用产生时候,就会进入下面的这个函数
- SYSCALL_DEFINE3(open, const char __user *, filename, int, flags, int, mode)
- {
- long ret;
- if (force_o_largefile())//检查是否应该不考虑用户层传递的标志
- flags |= O_LARGEFILE;
- ret = do_sys_open(AT_FDCWD, filename, flags, mode);
- /* avoid REGPARM breakage on x86: */
- asmlinkage_protect(3, ret, filename, flags, mode);
- return ret;
- }
下面看看SYSCALL_DEFINEx是怎么用的
- #define SYSCALL_DEFINE3(name, ...) SYSCALL_DEFINEx(3, _##name, __VA_ARGS__)
- #ifdef CONFIG_FTRACE_SYSCALLS
- #define SYSCALL_DEFINEx(x, sname, ...) \
- static const char *types_##sname[] = { \
- __SC_STR_TDECL##x(__VA_ARGS__) \
- }; \
- static const char *args_##sname[] = { \
- __SC_STR_ADECL##x(__VA_ARGS__) \
- }; \
- SYSCALL_METADATA(sname, x); \
- __SYSCALL_DEFINEx(x, sname, __VA_ARGS__)
- #else
- #define SYSCALL_DEFINEx(x, sname, ...) \
- __SYSCALL_DEFINEx(x, sname, __VA_ARGS__)
- #endif
- #define SYSCALL_DEFINE(name) asmlinkage long sys_##name
- #define __SYSCALL_DEFINEx(x, name, ...) \
- asmlinkage long sys##name(__SC_DECL##x(__VA_ARGS__))
由上可以看出open对应的是3,看看__SC_DECL3是怎么用的
- #define __SC_DECL1(t1, a1) t1 a1
- #define __SC_DECL2(t2, a2, ...) t2 a2, __SC_DECL1(__VA_ARGS__)
- #define __SC_DECL3(t3, a3, ...) t3 a3, __SC_DECL2(__VA_ARGS__)
我们一步步展开SYSCALL_DEFINE3(open, const char __user *, filename, int, flags, int, mode)
代替进去,可以得到
- SYSCALL_DEFINE3(open, const char __user *, filename, int, flags, int, mode)
- =__SYSCALL_DEFINEx(3, sname, __VA_ARGS__)
- =asmlinkage long sys_open(__SC_DECL3(__VA_ARGS__))
- =asmlinkage long sys_open(const char __user* filename, int flags, int mode)
在sys_open里面接着调用do_sys_open
- long do_sys_open(int dfd, const char __user *filename, int flags, int mode)
- {
- struct open_flags op;
- int lookup = build_open_flags(flags, mode, &op);
- char *tmp = getname(filename);//从进程地址空间读取文件的路径名
- int fd = PTR_ERR(tmp);
- if (!IS_ERR(tmp)) {
- fd = get_unused_fd_flags(flags);
- if (fd >= 0) {
- struct file *f = do_filp_open(dfd, tmp, &op, lookup);//fd获取成功则开始打开文件
- if (IS_ERR(f)) { //完成打开功能函数
- put_unused_fd(fd);
- fd = PTR_ERR(f);
- } else {
- fsnotify_open(f);//如果打开成功,调用fsnotify_open,根据inode所指定的信息进行打开函
- fd_install(fd, f); //数,将该文件加入到文件监控系统中。
- }
- }
- putname(tmp);
- }
- return fd;
- }
do_file_open函数的一个重要的作用就是根据传递进来的权限进行分析,并且分析传递进来的路径名,根据路径名逐个解析成dentry,并且通过dentry找到inode,inode就是记录着该文件的相关信息,包括文件的创建时间和文件属性的所有者等等信息,根据这些信息可以找到对应的文件操作的方法。
- struct file *do_filp_open(int dfd, const char *pathname,
- const struct open_flags *op, int flags)
- {
- struct nameidata nd;
- struct file *filp;
- filp = path_openat(dfd, pathname, &nd, op, flags | LOOKUP_RCU);
- if (unlikely(filp == ERR_PTR(-ECHILD)))
- filp = path_openat(dfd, pathname, &nd, op, flags);
- if (unlikely(filp == ERR_PTR(-ESTALE)))
- filp = path_openat(dfd, pathname, &nd, op, flags | LOOKUP_REVAL);
- return filp;
- }
- static struct file *path_openat(int dfd, const char *pathname,
- struct nameidata *nd, const struct open_flags *op, int flags)
- {
- struct file *base = NULL;
- struct file *filp;
- struct path path;
- int error;
- filp = get_empty_filp();
- if (!filp)
- return ERR_PTR(-ENFILE);
- filp->f_flags = op->open_flag;
- nd->intent.open.file = filp;
- nd->intent.open.flags = open_to_namei_flags(op->open_flag);
- nd->intent.open.create_mode = op->mode;
- error = path_init(dfd, pathname, flags | LOOKUP_PARENT, nd, &base);
- if (unlikely(error))
- goto out_filp;
- current->total_link_count = 0;
- error = link_path_walk(pathname, nd);
- if (unlikely(error))
- goto out_filp;
- filp = do_last(nd, &path, op, pathname);
- while (unlikely(!filp)) { /* trailing symlink */
- struct path link = path;
- void *cookie;
- if (!(nd->flags & LOOKUP_FOLLOW)) {
- path_put_conditional(&path, nd);
- path_put(&nd->path);
- filp = ERR_PTR(-ELOOP);
- break;
- }
- nd->flags |= LOOKUP_PARENT;
- nd->flags &= ~(LOOKUP_OPEN|LOOKUP_CREATE|LOOKUP_EXCL);
- error = follow_link(&link, nd, &cookie);
- if (unlikely(error))
- filp = ERR_PTR(error);
- else
- filp = do_last(nd, &path, op, pathname);
- put_link(nd, &link, cookie);
- }
- out:
- if (nd->root.mnt && !(nd->flags & LOOKUP_ROOT))
- path_put(&nd->root);
- if (base)
- fput(base);
- release_open_intent(nd);
- return filp;
- out_filp:
- filp = ERR_PTR(error);
- goto out;
- }
当内核要访问一个文件的时候,第一步要做的就是要找到这个文件,而查找这个文件在VFS里面是由link_path_walk的时候可以看到传进去的参数,接着调用do_last返回最后一个分量对应的file指针。
在do_last函数中进行一些判断,然后看是否需要创建文件,如果需要穿件,则创建文件,如果文件存在的话,就直接调用nameidata_to_filp完成文件打开。
- struct file *nameidata_to_filp(struct nameidata *nd)
- {
- const struct cred *cred = current_cred();
- struct file *filp;
- /* Pick up the filp from the open intent */
- filp = nd->intent.open.file;
- nd->intent.open.file = NULL;
- /* Has the filesystem initialised the file for us? */
- if (filp->f_path.dentry == NULL) {
- path_get(&nd->path);
- filp = __dentry_open(nd->path.dentry, nd->path.mnt, filp,
- NULL, cred);
- }
- return filp;
- }
该函数主要是调用__dentry_open函数,在这个函数中,如果相应的file_operations结构存在的话就调用它的open函数。
阅读(848) | 评论(0) | 转发(0) |