linux系统调用分析

来源:互联网 发布:大数据 应用 编辑:程序博客网 时间:2024/05/22 23:16

一、系统调用:

1、由操作系统内核提供,使得用户程序可以访问硬件设备和其它操作系统资源,即为用户空间提供一种硬件的抽象接口;

2、程序中所有与系统有关的代码的实现都必须通过这些接口来完成,保证了系统的安全与稳定;

3、对于用户空间的进程,在一般情况下是通过应用编程接口(API)而不是系统调用来进行编程,有些API往往直接封装了系统调用,

     但这并不意味着两者是一一对应的。当前最流行的API是基于POSIX标准的。

4、系统调用涉及的两个文件:   /usr/src/linux/include/asm/unistd.h:系统调用号

/usr/src/linux/arch/x86/kernel/entry_32.S: 系统调用的汇编实现


二、AT&T嵌入式汇编扫盲

I 、和Intelx86汇编的区别:

1、立即数之前要加$符号;
2、 寄存器前要加%;
3、()表示用括号内的值作为地址进行间接寻址;
4、*表示取出该地址下的内容;

5、格式为: 指令  源操作数 -> 目的操作数


       II、gnu C语言中嵌入汇编代码的常见格式如下:

__asm__ volatile ("int $0x80" 
: "=a" (__res) 
: "0" (__NR_##name), "b" ((long)(arg1)),"c" ((long)(arg2)), 
"d" ((long)(arg3))); 

各种符号的含义如下:

__asm__:嵌入汇编标志,语句之间使用“;”、“\n”或“\n\t”分开 ,指令中的操作数可以使用占位符引用C语言变量 %0-%9,

          为了与此占位符区分,寄存器前应该加%%,如%%eax;

"=a" (__res):将调用int 0x80之后寄存器eax作为返回值传给变量__res,  每个输出操作数的限定字符串必须包含“=”表示他是一个输出操作数;
"0" (__NR_##name):将__NR_与变量name做字符串拼接后传给寄存器eax作为调用int 0x80的参数;
"b" ((long)(arg1)):将变量arg1强转化成long型变量后传给寄存器ebx作为调用int 0x80的参数;

其余的字符与汇编中寄存器之间的关系如下表:
        “S”:  寄存器esi“D”: 寄存器edi
        “d”:  寄存器edx“c”: 寄存器ecx

三、系统调用过程源码分析:

I、__syscallN()宏,其中N表示参数的个数。

以__syscall1为例:

#define _syscall1(type,name,type1,arg1) \
type name(type1 arg1) \
{ \
long __res; \
__asm__ volatile ("int $0x80" \//调用软中断0x80
: "=a" (__res) \
: "0" (__NR_##name),"b" ((long)(arg1))); \
if (__res >= 0) \
return (type) __res; \//调用成功
errno = -__res; \//调用失败
return -1; \//返回-1报错         //这里大概就是为什么大多数调用后面都有if(-1 == res)的奥密
}


      II、entry.s源码轮廓[hint: 内核版本为2.6.38]

1、首先int 0x80中断会被系统处理,调用到entry(system_call);

2、执行到syscall_call,执行对应的系统调用;   //主要任务也是我们关心的任务在此完成。

3、执行syscall_exit,可能跳转到syscall_exit_work;

4、执行syscall_exit_work,可能跳转到work_pending,也可能跳转到resume_userspace;

5、执行resume_userspace,如果还有工作要做则跳转到work_pending;

6、执行work_pending,如果有pending的信号则调用信号处理,然后跳到resume_userspace;

7、执行resume_userspace,如果还有工作要做则跳转到work_pending;

8、执行work_pending,此时没有信号要处理,再看是否还有工作要完成,如果没有了跳到restore_all, 如果还有工作要做,则看是否需要进行进程调度;

9、执行restore_all,最终调用iret指令返回到用户态;


0 0