系统调用Open()函数的内核追踪(上篇)

来源:互联网 发布:vb.net key value 编辑:程序博客网 时间:2024/05/17 02:33

From: http://blog.chinaunix.net/uid-24585858-id-2125500.html

open函数相信大家都用过,这里就不多说它的使用方法等事项,现直接进入正题...


用户态程序调用open函数时,会产生一个中断号为5的中断请求,其值以该宏__NR__open进行标示.而后该进程上下文(processcontext)将会被切换到内核空间。待内核中的相关操作完成后,就会从内核返回,此时还需要一次进程上下文切换(processcontext switch)


待进程执行流进入内核后,会通过一系列转换(这里我们不关心),最终进入SYSCALL_DEFINE3(open,...)函数中。看起来该函数定义比较特殊,其实SYSCALL_DRFINE3是一个宏,它被定义成如下形式:

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


SYSCALL_DEFINEx宏具有如下形式:


#ifdef CONFIG_FTRACE_SYSCALLS
#define SYSCALL_DEFINEx(x, sname,...)                \
    static constchar *types_##sname[]= {            \
        __SC_STR_TDECL##x(__VA_ARGS__)            \
    };                            \
    static constchar *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


可以看到,不论是何种形式的宏定义,最终都会进入__SYSCALL_DEFINEx中,而__SYSCALL_DEFINEx的定义如下:


#ifdef CONFIG_HAVE_SYSCALL_WRAPPERS

#define SYSCALL_DEFINE(name)static inlinelong SYSC_##name

#define __SYSCALL_DEFINEx(x, name,...)                    \
    asmlinkage long sys##name(__SC_DECL##x(__VA_ARGS__));        \
    static inlinelong SYSC##name(__SC_DECL##x(__VA_ARGS__));    \
    asmlinkage long SyS##name(__SC_LONG##x(__VA_ARGS__))        \
    {                                \
        __SC_TEST##x(__VA_ARGS__);                \
        return (long) SYSC##name(__SC_CAST##x(__VA_ARGS__));    \
    }                                \
    SYSCALL_ALIAS(sys##name, SyS##name);                \
    static inlinelong SYSC##name(__SC_DECL##x(__VA_ARGS__))

#else /* CONFIG_HAVE_SYSCALL_WRAPPERS */

#define SYSCALL_DEFINE(name) asmlinkagelong sys_##name
#define __SYSCALL_DEFINEx(x, name,...)                    \
    asmlinkage long sys##name(__SC_DECL##x(__VA_ARGS__))

#endif /* CONFIG_HAVE_SYSCALL_WRAPPERS */


经过该宏的替换作用以后,最终我们就会得到sys_openSYS_open所对应的函数原型。如下:


asmlinkage long sys_open(const char __user filename,int flags, int mode);


这也就是我们最常见到的open函数所对应的在内核中的实现部份。其实,对于linux下所有的系统调用函数,采用上述方法均可找到与其对应的内核函数sys_xxx().


接下来我们来看sys_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;
}


在函数中首先调用force_o_largefile()宏进行LARGEFILE?确认。若是LARGEFILE则将其在flags中置位。随后调用主处理函数do_sys_open进行后续处理。其实,open的工作也就是在该函数中进行的。该函数原型如下:


long do_sys_open(int dfd,const char __user*filename, int flags, int mode);


在该函数中,如果通过getname()得到filename变量中文件名的指针没有错误的话,接下来就会掉用get_unused_fd_flags()函数获得一个没被使用的文件描述符fd。注意,对于文件描述符fd来讲,它只对本进程有效,也即它只在该进程中可见而在其它进程中代表着完全不同的文件。在32位系统中,一个进程最多打开32个文件,而在64位系统中可以打开64个文件。该函数就是用来获得一个未被使用的文件描述符fd.至于它的获取过程是很复杂的,这里不进行讲述。有兴趣的话,可以到www.kernel.org下载最新的内核源码进行研究。


在获得了有效的fd之后,我们通过do_filp_open()函数打开或者创建相应的文件,并且返回与之对应的文件结构structfile*f。如果函数返回的结构地址有效的话,那么就会调用fsnotify_open()函数(参数为f)将该文件加入到文件监控的系统中。该系统是用来监控文件被打开,创建,读写,关闭,修改等操作的,具体工作原理见后面文章。本文中不做讲述。随后调用fd_install()函数将structfile*f加入到fd索引位置处的数组中。如果后续过程中,有对该文件描述符的操作的话,就会通过查找该数组得到对应的文件结构,而后在进行相关操作。完成这些工作之后,open函数就返回了。返回值也就是刚才得到的fd.


那么,在do_filp_open()函数中有具体做了哪些工作呢?文件是如何被创建的呢?以及文件若存在的话,又是怎样被找到,而后被打开的呢?在中篇中我们将会回答这些问题。


0 0
原创粉丝点击