BBS水木清华站∶精华区

发信人: weix (好好学习), 信区: Linux        
标  题: 代码分析.txt 
发信站: BBS 水木清华站 (Thu Dec 21 18:23:23 2000) 
 
现对Linux核心代码的文件系统(File System)和进程间通信模块的部分系统调用作 
分析如下。所采用核心为2.2.14的版本号。此核心工作在本子网的网关上。这次将 
分析File System模块下的chdir,Process managerment 
下的alarm这三个系统调用。所有路径均为/usr/src/linux下的相对路径。 
本次分析以源代码注释的形式给出。 
1)chdir 
File:fs/open.c 
原型:int sys_chdir(const char *path); 
源代码如下: 
asmlinkage int sys_chdir(const char * filename) 

        int error; 
        struct inode *inode; 
        struct dentry *dentry, *tmp; 
/* 
上面定义了两个结构指针,我就不具体分析这两个结构的内容了。原因有二,这两 
个结构十分庞大,而且 
应该是系统中很著名的结构。 
下面的这个lock_kernel(),实际上是一个macro。不过我找到在 
/usr/include/asm-sparc64/smplock.h里面 
的一段define之后,就不清楚到底在干什么了,hoho.函数末尾处的 
unlock_kernel()也是类似的一个macro。 
偶想他们的功能就是作某些系统级的锁定,防止文件系统的异常。 
*/ 
        lock_kernel();         
        dentry = namei(filename); 
/* 
这里dentry调用namei之后返回的。这个namei实际上调用的是__namei,在文件 
fs/namei.c里面实现的。 
这个namei函数就是根据传入的参数const char *filename,然后通过调用 
getname再转化到相应的dentry 
结构指针。最后通过dput来调整相应的inode的错误。这部分代码实在不想再看了 
。hoho. 
之后的这个PTR_ERR(STR)类型的“函数”实际上是一个macro.它其实就是强制类型 
转换((long)(str)), 
就是把namei返回的东西转化成可以标识某种具体错误的long型数据。 
*/ 
        error = PTR_ERR(dentry); 
/* 
这里说明一下几个相关的macro是怎么定义的。 
#define IS_ERR(ptr)     ((unsigned long)(ptr) > (unsigned  
long)(-1000)) 
#define S_ISDIR(m)      (((m) & S_IFMT) == S_IFDIR) 
#define MAY_EXEC 1 
#define ENOTDIR         20      /* Not a directory */ 
其中: 
        #define S_IFDIR  0040000 
        #define S_IFMT  00170000 
这样一些地方就很明了了。 
*/ 
        if (IS_ERR(dentry)) 
                goto out; 
        inode = dentry->d_inode; 
/* 
如果dentry有错,就unlock_kernel然后退出。 
要不就把刚才在namei里面返回的dentry结构的d_inode元素赋给inode,这是文件 
系统的inode结点。 
*/ 
        error = -ENOTDIR; 
        if (!S_ISDIR(inode->i_mode)) 
                goto dput_and_out; 
/* 
现在本函数中的inode结构已经被赋值了。但是如果inode->i_mode的信息表示这个 
dentry不是一个目录, 
那么就认为是出错,通过goto转跳到dput_and_out去处理。顾名思义也知道这个转 
跳是干什么的。Linux 
Kernel source code的风格真的很好哦,hoho。接下来调用permission来检测这个 
inode所表示的目录对 
于执行该系统调用的进程的属主是否可“执行”的。如果该进程的属主没有权限“ 
执行”这个目录,那么 
返回的error就不是0了。于是接下来的goto就会被执行。 
*/ 
        error = permission(inode,MAY_EXEC); 
        if (error) 
                goto dput_and_out; 
/* 
如果执行到了这里,那就说明一切很顺利。 
下面的就是通过dentry类型的中间变量tmp来实现原先的和现在要change到的两个 
dentry之间的转换。这种 
c=a;a=b;b=c类型的“算法”应该是很常见的。hoho。 
那么在两个相关的dentry的信息交换完了以后,sys_chdir也应该结束了,hoho。 
 
*/ 
        tmp = current->fs->pwd; 
        current->fs->pwd = dentry; 
        dentry = tmp; 
 
dput_and_out: 
        dput(dentry); 
out: 
        unlock_kernel(); 
        return error; 

 
2)alarm 
File:kernel/sched.c 
原型:int sys_alarm(long sedonds); 
调用的源代码如下: 
asmlinkage unsigned int sys_alarm(unsigned int seconds) 

        struct itimerval it_new, it_old; 
/* 
这里定义了两个itimerval结构的变量it_new,it_old。 
结构itimerval在/usr/include/time.h中定义了: 
struct  itimerval { 
        struct  timeval it_interval; 
        struct  timeval it_value; 
}; 
其中: 
struct timeval { 
        time_t          tv_sec; 
        suseconds_t     tv_usec; 
}; 
在itimerval结构里面的两个元素,分别为时间间隔it_interval和当前时间 
it_value.从代码中可以看出, 
它们都是timeval结构的变量,包含了这样两个信息:秒数和毫秒数(seconds, 
microseconds)。其实这两 
个数据类型time_t和suseconds_t如果找到最初的定义,会发现他们其实都是long 
型的,不过被typedef 
了。这样的情况在Linux Kernel的source code中还有很多。 
然后下面定义了一个unsigned int的变量oldalarm用于返回。 
*/ 
        unsigned int oldalarm; 
        it_new.it_interval.tv_sec = it_new.it_interval.tv_usec = 0; 
        it_new.it_value.tv_sec = seconds; 
        it_new.it_value.tv_usec = 0; 
/* 
以上的代码是对it_new做了初始化。然后就是调用do_setitimer函数来实现时间的 
设置。这个函数的入口 
参数,我想就不用多说了,只需要说明一下第一个参数是在文件 
/usr/include/time.h中定义的宏: 
#define ITIMER_REAL 0 
这里我先不跟踪到do_setitimer函数里面而继续分析sys_alarm这个函数。因为把 
it_old的地址作为参数给 
了函数do_setitimer了,所以就可以实际的改变it_old内个元素的值。调用 
do_setitimer之后就把 
it_old.it_value.tv_sec的值赋给了oldalarm了。之后就是如果it_old. 
it_value.tv_sec得到了不为零的值,oldalarm自加1。看来有必要简单的分析一下 
do_setitimer这个函数,不过最好还是不在书面做出了,因为 
涉及调用函数的层次太深了。总的来说就是通过do_setitimer在一较底层上给 
it_old做了一个“初始化”。 
总的情况是sys_alarm=>do_setitimer=>do_getitimer=>jiffiestotv,最后在这个 
jiffiestotv里面实现了 
赋值。 
*/ 
        do_setitimer(ITIMER_REAL, &it_new, &it_old); 
        oldalarm = it_old.it_value.tv_sec; 
/*  
通过逐层分析do_setitimer以及里面的调用,我们可以知道这里的it_old. 
it_value.tv_sec实际上是jiffies 
除以一个常数。而这个jiffies,在kernel/sched.c里面是这么定义的: 
unsigned long volatile jiffies=0 
这样我们就容易理解为什么如果本函数的参数为0的时候本函数返回0了。至于为什 
么实际设置了一个时间的 
情况下返回值oldalarm要自加1,这个我想应该是在系统级上对时间设置之后做的 
变换吧。纯属猜测。 
下面这两行英文是在源代码中作者的注释。我想意思是很清楚的吧?hoho. 
        ehhh.. We can't return 0 if we have an alarm pending.. 
        And we'd better return too much than too little anyway 
*/ 
        if (it_old.it_value.tv_usec) 
                oldalarm++; 
        return oldalarm; 
/* 
这样,sys_alarm的分析就结束了。 
*/ 

 
-- 
 
※ 来源:·BBS 水木清华站 smth.org·[FROM: 202.118.74.64] 

BBS水木清华站∶精华区