统计linux系统调用的次数

         Linux课程实践的一次作业,主要是统计自开机以来每个系统调用次数的作业。但是苦于对汇编的恐惧,迟迟没有丝毫进展。

         现将大致步骤说一下:

         我从官网上下载的是2.6.33.4的内核版本,说起内核版本我就上火,教材上用的都是一些很老的版本了,每次进一个文件都要连蒙带猜出它的文件名。要说明的一点是,该实验是在x86平台32位系统上的。

(1)       解压完后进入到arch/x86/kernel目录,进入syscall_table_32.S文件中添加如下至最后一行:

         .long sys_mycall

         顺便说一下,这个文件是系统的系统调用表,它其实就是一个数组,里面存放了__NR_syscalls个表项(__NR_syscalls定义在arch/x86/include/asm/unistd_32.h里面,表征系统调用的个数,下面会有说明),每个表项在unistd_32.h中都会有一个宏定义。

         ‘.’表示的是当前的地址,sys_call_table代表数组首地址。以我们准备写的系统调用mycall为例,在系统调用表中的第339个表项是系统调用号为338的mycall()服务例程地址。

         syscall_table_32.S这个表的主要作用就是将系统调用号和系统调用的内核服务例程联系在一起。

         接着,到arch/x86/include/asm/unistd_32.h里面定义添加的该系统调用号的宏(这里应该就是上面的338),在文件尾添加如下一行:

         #define __NR_mycall 338

         不过,__NR_syscall这个宏要相应地改为339。

(2)       在系统调用表中创建完一个系统调用号之后,就要实现它所指向的服务例程。在实现这个函数之前,先在相关头文件中声明一下这个函数:

         进入include/linux/syscalls.h,这个文件里面包含了所有系统调用的声明,在合适位置添加如下声明:

         asmlinkage long sys_mycall(void);

        

         下面便是本次实验的关键点,我们要统计的是所有系统调用(339个)自系统启动以来每个被调用的次数,所以我们很有必要构造一个数组来记录每个系统调用的被调用次数。考虑到要让该数组元素每次开机启动时都要初始化为0,我们可以将该数组设置为全局的。

         进kernel/sys.c文件首先创建一个全局数组:

         long mycount[400];

        

         接下来便是sys_mycall的具体实现,这个很简单,只要将上面的数组中的每个元素打印出来即可。

         long mycount[400];

SYSCALL_DEFINE0(mycall)

{

                  int i;

                  for(i = 0; i < __NR_mycall; i++)

                  {

                            printk(KERN_INFO "syscall%d: %ldtime(s).\n", i, mycount[i]);

                  }

                  return 1;

}

(3)       系统调用的添加工作算是完成了,不过现在还需要的是对上面定义的数组在每次进行系统调用时能够及时更新该数组。那这个就必须得看系统调用过程的汇编代码了:

         进入到arch/x86/kernel/entry_32.S

         在call *sys_call_table(,%eax,4)语句之前添加如下一行:

                   incl mycount(,%eax,4)

         我记得当时助教还问了我一下这条汇编语句的含义,可能是我当时说的不够清楚,助教貌似不太满意,我这里再说一遍:

         incl的意思当然就是对后面的地址内容进行自增操作,然后mycount当然就是数组名啦,括号里面第一个参数没有,表示默认从数组第一项(首地址)算起,具体的地址就是(%eax*4),从这个地址开始的四个字节表示的一个long类型整数自增加一。(现在应该够清楚了吧(*^__^*) 嘻嘻……)。

        

         一切搞定以后,接下来的工作便是make all -> make modules_install -> make install的编译工作了,这不是我们这篇文章的重点。

        

         经过漫长的内核编译的等待,重启之后,写一个测试函数如下:

#include <stdio.h>

#include <linux/unistd.h>

#define __NR_mycall 338

int main()

{

         if(syscall(__NR_mycall))

                   printf("ok!\n");

         else

                   printf("failed!\n");

         return 0;

}

         编译运行,以下便是最终的结果(在/var/log/messages日志文件里):

May 17 15:48:39 localhost kernel: syscall 0: 0 time(s)

May 17 15:48:39 localhost kernel: syscall 1: 340 time(s)

May 17 15:48:39 localhost kernel: syscall 2: 0 time(s)

May 17 15:48:39 localhost kernel: syscall 3: 212814 time(s)

May 17 15:48:39 localhost kernel: syscall 4: 0 time(s)

May 17 15:48:39 localhost kernel: syscall 5: 245476 time(s)

May 17 15:48:39 localhost kernel: syscall 6: 225497 time(s)

May 17 15:48:39 localhost kernel: syscall 7: 0 time(s)

May 17 15:48:39 localhost kernel: syscall 8: 0 time(s)

May 17 15:48:39 localhost kernel: syscall 9: 0 time(s)

May 17 15:48:39 localhost kernel: syscall 10: 0 time(s)

         只列出了前10个系统调用的调用情况。

         Over….

你可能感兴趣的:(统计linux系统调用的次数)