Chinaunix首页 | 论坛 | 博客
  • 博客访问: 3519697
  • 博文数量: 1805
  • 博客积分: 135
  • 博客等级: 入伍新兵
  • 技术积分: 3345
  • 用 户 组: 普通用户
  • 注册时间: 2010-03-19 20:01
文章分类

全部博文(1805)

文章存档

2017年(19)

2016年(80)

2015年(341)

2014年(438)

2013年(349)

2012年(332)

2011年(248)

分类: LINUX

2015-04-23 23:46:19

原文地址:Linux系统调用 作者:_ChinaUnix

    用户通过open/read/ioctrl函数打开设备文件,库文件会通过会产生一个软中断,通过系统调用进入到内核,通过系统调用中断号,就可以跳到该中断的人口地址。
    下面来看看open的调用过程,open的格式如下:

点击(此处)折叠或打开

  1. int open(const char * pathname,int oflag, mode_t mode )
当open系统调用产生时候,就会进入下面的这个函数

点击(此处)折叠或打开

  1. SYSCALL_DEFINE3(open, const char __user *, filename, int, flags, int, mode)
  2. {
  3.     long ret;

  4.     if (force_o_largefile())//检查是否应该不考虑用户层传递的标志
  5.         flags |= O_LARGEFILE;

  6.     ret = do_sys_open(AT_FDCWD, filename, flags, mode);
  7.     /* avoid REGPARM breakage on x86: */
  8.     asmlinkage_protect(3, ret, filename, flags, mode);
  9.     return ret;
  10. }
下面看看SYSCALL_DEFINEx是怎么用的

点击(此处)折叠或打开

  1. #define SYSCALL_DEFINE3(name, ...) SYSCALL_DEFINEx(3, _##name, __VA_ARGS__)

点击(此处)折叠或打开

  1. #ifdef CONFIG_FTRACE_SYSCALLS
  2. #define SYSCALL_DEFINEx(x, sname, ...)                \
  3.     static const char *types_##sname[] = {            \
  4.         __SC_STR_TDECL##x(__VA_ARGS__)            \
  5.     };                            \
  6.     static const char *args_##sname[] = {            \
  7.         __SC_STR_ADECL##x(__VA_ARGS__)            \
  8.     };                            \
  9.     SYSCALL_METADATA(sname, x);                \
  10.     __SYSCALL_DEFINEx(x, sname, __VA_ARGS__)
  11. #else
  12. #define SYSCALL_DEFINEx(x, sname, ...)                \
  13.     __SYSCALL_DEFINEx(x, sname, __VA_ARGS__)
  14. #endif

点击(此处)折叠或打开

  1. #define SYSCALL_DEFINE(name) asmlinkage long sys_##name
  2. #define __SYSCALL_DEFINEx(x, name, ...)                    \
  3.     asmlinkage long sys##name(__SC_DECL##x(__VA_ARGS__))
由上可以看出open对应的是3,看看__SC_DECL3是怎么用的

点击(此处)折叠或打开

  1. #define __SC_DECL1(t1, a1)    t1 a1
  2. #define __SC_DECL2(t2, a2, ...) t2 a2, __SC_DECL1(__VA_ARGS__)
  3. #define __SC_DECL3(t3, a3, ...) t3 a3, __SC_DECL2(__VA_ARGS__)
我们一步步展开SYSCALL_DEFINE3(open, const char __user *, filename, int, flags, int, mode)
代替进去,可以得到

点击(此处)折叠或打开

  1. SYSCALL_DEFINE3(open, const char __user *, filename, int, flags, int, mode)
  2. =__SYSCALL_DEFINEx(3, sname, __VA_ARGS__)
  3. =asmlinkage long sys_open(__SC_DECL3(__VA_ARGS__))
  4. =asmlinkage long sys_open(const char __user* filename, int flags, int mode)
在sys_open里面接着调用do_sys_open

点击(此处)折叠或打开

  1. long do_sys_open(int dfd, const char __user *filename, int flags, int mode)
  2. {
  3.     struct open_flags op;
  4.     int lookup = build_open_flags(flags, mode, &op);
  5.     char *tmp = getname(filename);//从进程地址空间读取文件的路径名
  6.     int fd = PTR_ERR(tmp);

  7.     if (!IS_ERR(tmp)) {
  8.         fd = get_unused_fd_flags(flags);
  9.         if (fd >= 0) {
  10.             struct file *f = do_filp_open(dfd, tmp, &op, lookup);//fd获取成功则开始打开文件
  11.             if (IS_ERR(f)) {                                    //完成打开功能函数
  12.                 put_unused_fd(fd);
  13.                 fd = PTR_ERR(f);
  14.             } else {
  15.                 fsnotify_open(f);//如果打开成功,调用fsnotify_open,根据inode所指定的信息进行打开函
  16.                 fd_install(fd, f); //数,将该文件加入到文件监控系统中。
  17.             }
  18.         }
  19.         putname(tmp);
  20.     }
  21.     return fd;
  22. }
    do_file_open函数的一个重要的作用就是根据传递进来的权限进行分析,并且分析传递进来的路径名,根据路径名逐个解析成dentry,并且通过dentry找到inode,inode就是记录着该文件的相关信息,包括文件的创建时间和文件属性的所有者等等信息,根据这些信息可以找到对应的文件操作的方法。

点击(此处)折叠或打开

  1. struct file *do_filp_open(int dfd, const char *pathname,
  2.         const struct open_flags *op, int flags)
  3. {
  4.     struct nameidata nd;
  5.     struct file *filp;

  6.     filp = path_openat(dfd, pathname, &nd, op, flags | LOOKUP_RCU);
  7.     if (unlikely(filp == ERR_PTR(-ECHILD)))
  8.         filp = path_openat(dfd, pathname, &nd, op, flags);
  9.     if (unlikely(filp == ERR_PTR(-ESTALE)))
  10.         filp = path_openat(dfd, pathname, &nd, op, flags | LOOKUP_REVAL);
  11.     return filp;
  12. }

点击(此处)折叠或打开

  1. static struct file *path_openat(int dfd, const char *pathname,
  2.         struct nameidata *nd, const struct open_flags *op, int flags)
  3. {
  4.     struct file *base = NULL;
  5.     struct file *filp;
  6.     struct path path;
  7.     int error;

  8.     filp = get_empty_filp();
  9.     if (!filp)
  10.         return ERR_PTR(-ENFILE);

  11.     filp->f_flags = op->open_flag;
  12.     nd->intent.open.file = filp;
  13.     nd->intent.open.flags = open_to_namei_flags(op->open_flag);
  14.     nd->intent.open.create_mode = op->mode;

  15.     error = path_init(dfd, pathname, flags | LOOKUP_PARENT, nd, &base);
  16.     if (unlikely(error))
  17.         goto out_filp;

  18.     current->total_link_count = 0;
  19.     error = link_path_walk(pathname, nd);
  20.     if (unlikely(error))
  21.         goto out_filp;

  22.     filp = do_last(nd, &path, op, pathname);
  23.     while (unlikely(!filp)) { /* trailing symlink */
  24.         struct path link = path;
  25.         void *cookie;
  26.         if (!(nd->flags & LOOKUP_FOLLOW)) {
  27.             path_put_conditional(&path, nd);
  28.             path_put(&nd->path);
  29.             filp = ERR_PTR(-ELOOP);
  30.             break;
  31.         }
  32.         nd->flags |= LOOKUP_PARENT;
  33.         nd->flags &= ~(LOOKUP_OPEN|LOOKUP_CREATE|LOOKUP_EXCL);
  34.         error = follow_link(&link, nd, &cookie);
  35.         if (unlikely(error))
  36.             filp = ERR_PTR(error);
  37.         else
  38.             filp = do_last(nd, &path, op, pathname);
  39.         put_link(nd, &link, cookie);
  40.     }
  41. out:
  42.     if (nd->root.mnt && !(nd->flags & LOOKUP_ROOT))
  43.         path_put(&nd->root);
  44.     if (base)
  45.         fput(base);
  46.     release_open_intent(nd);
  47.     return filp;

  48. out_filp:
  49.     filp = ERR_PTR(error);
  50.     goto out;
  51. }
当内核要访问一个文件的时候,第一步要做的就是要找到这个文件,而查找这个文件在VFS里面是由link_path_walk的时候可以看到传进去的参数,接着调用do_last返回最后一个分量对应的file指针。
在do_last函数中进行一些判断,然后看是否需要创建文件,如果需要穿件,则创建文件,如果文件存在的话,就直接调用nameidata_to_filp完成文件打开。

点击(此处)折叠或打开

  1. struct file *nameidata_to_filp(struct nameidata *nd)
  2. {
  3.     const struct cred *cred = current_cred();
  4.     struct file *filp;

  5.     /* Pick up the filp from the open intent */
  6.     filp = nd->intent.open.file;
  7.     nd->intent.open.file = NULL;

  8.     /* Has the filesystem initialised the file for us? */
  9.     if (filp->f_path.dentry == NULL) {
  10.         path_get(&nd->path);
  11.         filp = __dentry_open(nd->path.dentry, nd->path.mnt, filp,
  12.                  NULL, cred);
  13.     }
  14.     return filp;
  15. }
该函数主要是调用__dentry_open函数,在这个函数中,如果相应的file_operations结构存在的话就调用它的open函数。
阅读(842) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~