unix系统之系统调用的封装

本文深入解析了系统调用的概念及其实现方式,特别是在Linux系统中如何通过特定的寄存器设置和指令调用来完成系统任务的过程。同时,还提供了一段封装系统调用的示例代码。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

系统调用

每个程序在运行时都可以调用一系列的特殊“函数”。这些特殊的“函数”能够完成各种不同的系统任务——获取当前时间,打开读写文件等。这些特殊的函数有一个特殊的名字——系统调用。

调用系统调用

系统调用不同于普通函数,调用过程十分特殊。(以下讨论基于linux系统)其调用过程是:一 设置系统调用号。每个系统调用都有一个独特的系统调用号。通过系统调用号可以在几百个系统调用中确定系统调用。设置系统调用号要用到EAX寄存器。当调用open系统调用时,便是置EAX寄存器为5。movl %eax,$5。二 设置参数。系统调用都有参数。参数从0个到6个不等。每个参数都对应一个寄存器。参数一对应EBX,参数二对应ECX,参数三对应EDX,参数四对应ESI,参数五对应EDI,参数六对应EBP。有几个参数便设置几个寄存器。三 执行int 0x80。执行int 0x80指令,程序将切入内核执行,执行完成后返回继续执行int 0x80后一条指令。四 取返回值。返回值放在EAX寄存器中。从EAX中取出返回值,判断系统调用执行情况。

系统调用的封装

明白系统调用的调用过程后,可以简单对系统调用的调用过程进行封装。
以下为一段系统调用的封装代码。

#ifndef _SYSCALL_SYSDEP_H
#define _SYSCALL_SYSDEP_H

//使用宏生成函数

#define __syscall_return(type, res) \
do { \
    if ((unsigned long)(res) >= (unsigned long)(-4095)) { \
        errno = -(res); \
        res = -1; \
    } \
    return (type) (res); \
} while (0)


#define _syscall0(syscall_name,type,name) \
type name(void) \
{ \
long __res; \
__asm__ volatile ("int $0x80" \
    : "=a" (__res) \
    : "0" (__NR_##syscall_name)); \
__syscall_return(type,__res); \
}

#define _syscall1(syscall_name,type,name,type1,arg1) \
type name(type1 arg1) \
{ \
long __res; \
__asm__ volatile ("int $0x80" \
    : "=a" (__res) \
    : "0" (__NR_##syscall_name),"b" ((long)(arg1))); \
__syscall_return(type,__res); \
}

#define _syscall2(syscall_name,type,name,type1,arg1,type2,arg2) \
type name(type1 arg1,type2 arg2) \
{ \
long __res; \
__asm__ volatile ("int $0x80" \
    : "=a" (__res) \
    : "0" (__NR_##syscall_name),"b" ((long)(arg1)),"c" ((long)(arg2))); \
__syscall_return(type,__res); \
}

#define _syscall3(syscall_name,type,name,type1,arg1,type2,arg2,type3,arg3) \
type name(type1 arg1,type2 arg2,type3 arg3) \
{ \
long __res; \
__asm__ volatile ("int $0x80" \
    : "=a" (__res) \
    : "0" (__NR_##syscall_name),"b" ((long)(arg1)),"c" ((long)(arg2)), \
          "d" ((long)(arg3))); \
__syscall_return(type,__res); \
}

#define _syscall4(syscall_name,type,name,type1,arg1,type2,arg2,type3,arg3,type4,arg4) \
type name (type1 arg1, type2 arg2, type3 arg3, type4 arg4) \
{ \
long __res; \
__asm__ volatile ("int $0x80" \
    : "=a" (__res) \
    : "0" (__NR_##syscall_name),"b" ((long)(arg1)),"c" ((long)(arg2)), \
      "d" ((long)(arg3)),"S" ((long)(arg4))); \
__syscall_return(type,__res); \
} 

#define _syscall5(syscall_name,type,name,type1,arg1,type2,arg2,type3,arg3,type4,arg4, \
      type5,arg5) \
type name (type1 arg1,type2 arg2,type3 arg3,type4 arg4,type5 arg5) \
{ \
long __res; \
__asm__ volatile ("int $0x80" \
    : "=a" (__res) \
    : "0" (__NR_##syscall_name),"b" ((long)(arg1)),"c" ((long)(arg2)), \
      "d" ((long)(arg3)),"S" ((long)(arg4)),"D" ((long)(arg5))); \
__syscall_return(type,__res); \
}

#define _syscall6(syscall_name,type,name,type1,arg1,type2,arg2,type3,arg3,type4,arg4, \
      type5,arg5,type6,arg6) \
type name (type1 arg1,type2 arg2,type3 arg3,type4 arg4,type5 arg5,type6 arg6) \
{ \
long __res; \
__asm__ volatile ("push %%ebp ; movl %%eax,%%ebp ; movl %1,%%eax ; int $0x80 ; pop %%ebp" \
    : "=a" (__res) \
    : "i" (__NR_##syscall_name),"b" ((long)(arg1)),"c" ((long)(arg2)), \
      "d" ((long)(arg3)),"S" ((long)(arg4)),"D" ((long)(arg5)), \
      "0" ((long)(arg6))); \
__syscall_return(type,__res); \
}

#endif

_syscall0用于封装参数为0的系统调用;_syscall1用于封装参数为1的系统调用,以此类推。
我们以_syscall6为例来说明代码是如何封装系统调用的。
_syscall6是一个宏定义,其功能用于构建一个函数,函数名字是name,返回值类型为type。函数有6个参数,分别为arg1,arg2,arg3,arg4,arg5,arg6。参数类型分别是type1,type2,type3,type4,type5,type6。构建的函数为:

type name(type1 arg1,type2 arg2,type3 arg3,type4 arg4,type5 arg5,type6 arg6)
{
    ...
}

函数内是嵌入式汇编代码。

__asm__ volatile ("push %%ebp ; movl %%eax,%%ebp ; movl %1,%%eax ; int $0x80 ; pop %%ebp" \
    : "=a" (__res) \
    : "i" (__NR_##syscall_name),"b" ((long)(arg1)),"c" ((long)(arg2)), \
      "d" ((long)(arg3)),"S" ((long)(arg4)),"D" ((long)(arg5)), \
      "0" ((long)(arg6))); \

3-5行是执行汇编语句前的操作,2行是执行汇编语句后的操作。1行是执行的汇编语句。

3-5行中”b” ((long)(arg1))指将arg1的值放入EBX寄存器中,b对应EBX寄存器。”c” ((long)(arg2))指将arg2的值放入ECX寄存器中,c对应ECX寄存器。”d” ((long)(arg3))指将arg3的值放入EDX寄存器中,d对应EDX寄存器。”S” ((long)(arg4))指将arg4的值放入ESI寄存器中,S对应ESI寄存器。”D” ((long)(arg5))指将arg5的值放入EDI寄存器中,D对应EDI寄存器。”0” ((long)(arg6))指将arg6的值放入EAX寄存器中,0对应EAX寄存器(”=a”是0个出现的寄存器,a指EAX寄存器)。

第1行中指令将EAX寄存器的值放入EBP寄存器中,并置EAX的值为系统调用号。其中movl %1,%%eax指将_NR##syscall_name的值放入EAX中。##把参数变成字符串,_NR和syscall_name为##的参数。若syscall_name为open,则其_NR##syscall_name为__NR_open。__NR_open是定义在内核头文件unistd.h文件中的宏,对应数字5。参数设置完毕,执行int 0x80正式切入内核执行系统调用。
第2行”=a” (__res)指将EAX寄存器的值赋值给__res。也就是将系统调用的返回值给__res。

__syscall_return(type,__res)也是宏定义,它主要用于判断系统调用执行是否出错。若出错则置errno为-res,并返回-1。判断出错的方式是判断res作为无符号长整型是否大于-4095。若大于则出错。这是linux内核做出的规定。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值