Chinaunix首页 | 论坛 | 博客
  • 博客访问: 1354846
  • 博文数量: 166
  • 博客积分: 46
  • 博客等级: 民兵
  • 技术积分: 4061
  • 用 户 组: 普通用户
  • 注册时间: 2013-01-11 13:45
个人简介

现任职北京某互联网公司运维经理,高级架构师,涉足互联网运维行业已经超过10年。曾服务于京东商城,互动百科等互联网公司,早期运维界新星。 长期专研,C语言开发,操作系统内核,大型互联网架构。http://www.bdkyr.com

文章分类

分类: LINUX

2013-11-15 10:05:59

想学linux  c开发的博友或者正在学的博友,当你们的代码里出现系统调用,比如readwriteopen等。你怎么看、怎么想?有没有想一探究竟,深究一下系统调用的始终。准备好纸和笔,让我们开始系统调用跟踪之旅:

开始之前呢,先花费2分钟概览一下图(1),在你的头脑中做个快照,便于后面分析的理解,以read调用为例,展开分析。
           
                 

read函数的声明位于头文件#include

原型为:ssize_t read(int fd, void *buf, size_t count)

read函数在用户空间的伪代码:
                       

说明:

4行,寄存器eax保存了read函数的系统调用号,在 文件include/asm-i386/unistd.h里有定义(#define __NR_read  3);第57行,将三个参数分别放入三个寄存器(通过寄存器来传递参数)

8行,执行系统调用,进入内核;

9行,获取eax寄存器所保存的函数返 回值。

8行是一个中断,执行完第8行后,已经进入了系统内核,中断向量表中记录0x80号的中断处理程序开始执行,中断向量表的初始化在arch/x86_64/kernel/traps.c中定义:
                   

如红框标注所示。IA32_SYSCALL_VECTOR是系统调用的中断号,在include/asm-x86_64/hw_irq.h中定义:
                   

正好是0x80。而system_call是系统调用的中断处理函数指针,用户执行int $0x80后会执行到这个函数,它在arch/x86_64/kernel/entry.S中定义:
                  
                  
                  

SAVE_ALL是一个宏,在这include/asm-x86_64/calling.h文件里定义:


                  

主要作用就是将各个寄存器压入栈中。

cmpl $(nr_syscalls), %eax比较eax的值是否大于等于nr_syscallsnr_syscalls是比最大有效系统调用号大1的值,在arch/i386/kernel/entry.S中定义:

                 

syscall_table_size就是系统调用表的大小(单位:字节),syscall_table_size其实是一个数组,数组里存放的是各个系统调用函数的地址,元素类型是long型,除以4刚好是系统调用函数的个数。

如果从eax寄存器传进来的系统调用号有效,那么就执行第12行,在系统调用表里找到相应的系统调用服务程序,sys_call_tablearch/i386/kernel/entry.S中定义:

                 





*sys_call_table(,%eax,4)指的是sys_call_table里偏移量为%eax*4上的那个值指向的函数,这里%eax=3,那么第5行的sys_read()函数就会被调用。sys_read()/fs/read_write.c中定义:

                   

asmlingage是一个宏,定义为:__attribute__((regparm(0))),作用是让这个函数只从栈上获取参数(因为之前的SAVE_ALL将参数压到了栈里面)。

 Fget_light:根据fd指定的索引,从当前进程描述符中取出相应的file对象,如果没有找到指定的file对象,则返回错误,如果找到了指定的file对象,则调用file_pos_read函数取出此次读写文件的当前位置。

   然后调用vfs_read执行文件读取操作,而这个函数最终调用file->f_op_read指向的函数[fs/read_write.c文件中],代码如下:

                   

接下来调用file_pos_write()更新文件的当前读写位置,调用fput_light更新文件的引用计数。最后返回读取数据的字节数。

思考:file->f_op_read指向的函数是哪个?鉴于篇幅,我们准备在下一篇博文详解,继而开始真正数据读取之旅。

   

当数据读取完毕,需要返回用户态。以下即为推出内核,恢复各个寄存器的值,然后返回用户态的过程。
                 
                 
                 
                 
当执行完中断处理程序后,后面会调用RESTORE_REGS来恢复各个寄存器:

RESTORE_REGS的定义:


                 
                 
                 

                RESTORE_INT_REGS的定义:


                 


       当你耐着性子看到这里,真诚的说一声谢谢。我的劳作是有意义的。我写这篇博文的本意就是让自己在头脑形成一条清晰的脉络,read系统调用的清晰脉络。在平时分析问题的时候,沿着这条清晰的脉络,将疑难一一排除。对了,写这篇博文时,我参照的系统内核源码版本是:
linux-2.6.0



      Good luck for you!

阅读(3732) | 评论(0) | 转发(5) |
给主人留下些什么吧!~~