📄 00000049.htm
字号:
<HTML><HEAD> <TITLE>BBS水木清华站∶精华区</TITLE></HEAD><BODY><CENTER><H1>BBS水木清华站∶精华区</H1></CENTER>发信人: weix (好好学习), 信区: Linux <BR>标 题: 代码分析.txt <BR>发信站: BBS 水木清华站 (Thu Dec 21 18:23:23 2000) <BR> <BR>现对Linux核心代码的文件系统(File System)和进程间通信模块的部分系统调用作 <BR>分析如下。所采用核心为2.2.14的版本号。此核心工作在本子网的网关上。这次将 <BR>分析File System模块下的chdir,Process managerment <BR>下的alarm这三个系统调用。所有路径均为/usr/src/linux下的相对路径。 <BR>本次分析以源代码注释的形式给出。 <BR>1)chdir <BR>File:fs/open.c <BR>原型:int sys_chdir(const char *path); <BR>源代码如下: <BR>asmlinkage int sys_chdir(const char * filename) <BR>{ <BR> int error; <BR> struct inode *inode; <BR> struct dentry *dentry, *tmp; <BR>/* <BR>上面定义了两个结构指针,我就不具体分析这两个结构的内容了。原因有二,这两 <BR>个结构十分庞大,而且 <BR>应该是系统中很著名的结构。 <BR>下面的这个lock_kernel(),实际上是一个macro。不过我找到在 <BR>/usr/include/asm-sparc64/smplock.h里面 <BR>的一段define之后,就不清楚到底在干什么了,hoho.函数末尾处的 <BR>unlock_kernel()也是类似的一个macro。 <BR>偶想他们的功能就是作某些系统级的锁定,防止文件系统的异常。 <BR>*/ <BR> lock_kernel(); <BR> dentry = namei(filename); <BR>/* <BR>这里dentry调用namei之后返回的。这个namei实际上调用的是__namei,在文件 <BR>fs/namei.c里面实现的。 <BR>这个namei函数就是根据传入的参数const char *filename,然后通过调用 <BR>getname再转化到相应的dentry <BR>结构指针。最后通过dput来调整相应的inode的错误。这部分代码实在不想再看了 <BR>。hoho. <BR>之后的这个PTR_ERR(STR)类型的“函数”实际上是一个macro.它其实就是强制类型 <BR>转换((long)(str)), <BR>就是把namei返回的东西转化成可以标识某种具体错误的long型数据。 <BR>*/ <BR> error = PTR_ERR(dentry); <BR>/* <BR>这里说明一下几个相关的macro是怎么定义的。 <BR>#define IS_ERR(ptr) ((unsigned long)(ptr) > (unsigned <BR>long)(-1000)) <BR>#define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR) <BR>#define MAY_EXEC 1 <BR>#define ENOTDIR 20 /* Not a directory */ <BR>其中: <BR> #define S_IFDIR 0040000 <BR> #define S_IFMT 00170000 <BR>这样一些地方就很明了了。 <BR>*/ <BR> if (IS_ERR(dentry)) <BR> goto out; <BR> inode = dentry->d_inode; <BR>/* <BR>如果dentry有错,就unlock_kernel然后退出。 <BR>要不就把刚才在namei里面返回的dentry结构的d_inode元素赋给inode,这是文件 <BR>系统的inode结点。 <BR>*/ <BR> error = -ENOTDIR; <BR> if (!S_ISDIR(inode->i_mode)) <BR> goto dput_and_out; <BR>/* <BR>现在本函数中的inode结构已经被赋值了。但是如果inode->i_mode的信息表示这个 <BR>dentry不是一个目录, <BR>那么就认为是出错,通过goto转跳到dput_and_out去处理。顾名思义也知道这个转 <BR>跳是干什么的。Linux <BR>Kernel source code的风格真的很好哦,hoho。接下来调用permission来检测这个 <BR>inode所表示的目录对 <BR>于执行该系统调用的进程的属主是否可“执行”的。如果该进程的属主没有权限“ <BR>执行”这个目录,那么 <BR>返回的error就不是0了。于是接下来的goto就会被执行。 <BR>*/ <BR> error = permission(inode,MAY_EXEC); <BR> if (error) <BR> goto dput_and_out; <BR>/* <BR>如果执行到了这里,那就说明一切很顺利。 <BR>下面的就是通过dentry类型的中间变量tmp来实现原先的和现在要change到的两个 <BR>dentry之间的转换。这种 <BR>c=a;a=b;b=c类型的“算法”应该是很常见的。hoho。 <BR>那么在两个相关的dentry的信息交换完了以后,sys_chdir也应该结束了,hoho。 <BR> <BR>*/ <BR> tmp = current->fs->pwd; <BR> current->fs->pwd = dentry; <BR> dentry = tmp; <BR> <BR>dput_and_out: <BR> dput(dentry); <BR>out: <BR> unlock_kernel(); <BR> return error; <BR>} <BR> <BR>2)alarm <BR>File:kernel/sched.c <BR>原型:int sys_alarm(long sedonds); <BR>调用的源代码如下: <BR>asmlinkage unsigned int sys_alarm(unsigned int seconds) <BR>{ <BR> struct itimerval it_new, it_old; <BR>/* <BR>这里定义了两个itimerval结构的变量it_new,it_old。 <BR>结构itimerval在/usr/include/time.h中定义了: <BR>struct itimerval { <BR> struct timeval it_interval; <BR> struct timeval it_value; <BR>}; <BR>其中: <BR>struct timeval { <BR> time_t tv_sec; <BR> suseconds_t tv_usec; <BR>}; <BR>在itimerval结构里面的两个元素,分别为时间间隔it_interval和当前时间 <BR>it_value.从代码中可以看出, <BR>它们都是timeval结构的变量,包含了这样两个信息:秒数和毫秒数(seconds, <BR>microseconds)。其实这两 <BR>个数据类型time_t和suseconds_t如果找到最初的定义,会发现他们其实都是long <BR>型的,不过被typedef <BR>了。这样的情况在Linux Kernel的source code中还有很多。 <BR>然后下面定义了一个unsigned int的变量oldalarm用于返回。 <BR>*/ <BR> unsigned int oldalarm; <BR> it_new.it_interval.tv_sec = it_new.it_interval.tv_usec = 0; <BR> it_new.it_value.tv_sec = seconds; <BR> it_new.it_value.tv_usec = 0; <BR>/* <BR>以上的代码是对it_new做了初始化。然后就是调用do_setitimer函数来实现时间的 <BR>设置。这个函数的入口 <BR>参数,我想就不用多说了,只需要说明一下第一个参数是在文件 <BR>/usr/include/time.h中定义的宏: <BR>#define ITIMER_REAL 0 <BR>这里我先不跟踪到do_setitimer函数里面而继续分析sys_alarm这个函数。因为把 <BR>it_old的地址作为参数给 <BR>了函数do_setitimer了,所以就可以实际的改变it_old内个元素的值。调用 <BR>do_setitimer之后就把 <BR>it_old.it_value.tv_sec的值赋给了oldalarm了。之后就是如果it_old. <BR>it_value.tv_sec得到了不为零的值,oldalarm自加1。看来有必要简单的分析一下 <BR>do_setitimer这个函数,不过最好还是不在书面做出了,因为 <BR>涉及调用函数的层次太深了。总的来说就是通过do_setitimer在一较底层上给 <BR>it_old做了一个“初始化”。 <BR>总的情况是sys_alarm=>do_setitimer=>do_getitimer=>jiffiestotv,最后在这个 <BR>jiffiestotv里面实现了 <BR>赋值。 <BR>*/ <BR> do_setitimer(ITIMER_REAL, &it_new, &it_old); <BR> oldalarm = it_old.it_value.tv_sec; <BR>/* <BR>通过逐层分析do_setitimer以及里面的调用,我们可以知道这里的it_old. <BR>it_value.tv_sec实际上是jiffies <BR>除以一个常数。而这个jiffies,在kernel/sched.c里面是这么定义的: <BR>unsigned long volatile jiffies=0 <BR>这样我们就容易理解为什么如果本函数的参数为0的时候本函数返回0了。至于为什 <BR>么实际设置了一个时间的 <BR>情况下返回值oldalarm要自加1,这个我想应该是在系统级上对时间设置之后做的 <BR>变换吧。纯属猜测。 <BR>下面这两行英文是在源代码中作者的注释。我想意思是很清楚的吧?hoho. <BR> ehhh.. We can't return 0 if we have an alarm pending.. <BR> And we'd better return too much than too little anyway <BR>*/ <BR> if (it_old.it_value.tv_usec) <BR> oldalarm++; <BR> return oldalarm; <BR>/* <BR>这样,sys_alarm的分析就结束了。 <BR>*/ <BR>} <BR> <BR>-- <BR> <BR>※ 来源:·BBS 水木清华站 smth.org·[FROM: 202.118.74.64] <BR><CENTER><H1>BBS水木清华站∶精华区</H1></CENTER></BODY></HTML>
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -