关于系统调用(二)

Linux系统调用实践

怎么用?(实践性) 以x86体系下linux内核为例

正如《系统调用一》里所讲,执行系统调用只有3个步骤:选系统调用号,传入系统调用参数,执行特权指令。只要正确的执行这3个步骤便可以完成对所有系统调用的执行。

3个步骤简单明了,但实现却纷繁复杂。本文以下内容将以原始,进阶,高阶,三个阶段去了解怎么调用系统调用。三个阶段都能完成系统调用的执行,但抽象程度一个比一个高,实际中的使用程度也一个比一个高。

开始之前

首先,必须明白只使用C语言是无法完成系统调用的执行的。C语言无法明确的设置寄存器,且无法执行特权指令。而这两部分是执行系统调用必须的。所以需要借助更底层的语言—汇编。而本文是以linux系统为例的,所以使用Gnu C的嵌入式汇编功能。如果对嵌入式汇编有疑惑可自行查找资料查看。

原始(一)

当需要执行某个系统调用最原始也是最直观的是使用嵌入式汇编直接调用。

/*
 *  执行系统调用write 与系统调用exit
 */

int main(int argc,char *argv[])
{
    int ret=0;
    char *str="hello world \n";
    int status=0;

    //完成write系统调用
    asm("int $0x80"
    :"=a"(ret)
    : "a"(4),"b"(1),"c"(str),"d"(13)
    );

    //使用汇编解释以上代码
    //movl $4,%eax
    //movl $1,%ebx
    //movl str,%ecx  将str的值赋给ecx str为局部变量 实际中不能如此书写 
    //movl $13,%edx
    //int $0x80
    //mov %eax,ret   将eax的值赋给ret ret为局部变量 实际中不能如此书写


    //完成exit系统调用    
    asm("int $0x80"
    :"=a"(ret)
    :"a"(1),"b"(status)
    );

    //使用汇编解释以上代码
        //movl $1,%eax
        //movl status,%ebx 将status的值赋给ebx status为局部变量 实际中不能如此书写 
        //int $0x80
        //mov %eax,ret   将eax的值赋给ret ret为局部变量 实际中不能如此书写


    return 0;
}

代码执行了2个系统调用,系统调用write以及exit ,输出 “hello world \n”后退出程序。

原始(二)

直接使用嵌入式汇编,虽然能够完成对系统调用的执行,但存在许多问题。
嵌入式汇编代码不易阅读,不易修改,易出错,太依赖机器,从x86体系移植到其他体系下,嵌入式汇编代码都需要修改。
所以glibc提供了syscall函数,封装了系统调用过程。

int syscall(int number,...);

number为系统调用号,而省略号表示参数可变。
不同系统调用的参数不同,系统调用存在几个参数,便写入几个参数。
若执行成功则返回0或正数,失败则返回-1,并将错误号放入全局变量errno中。

使用syscall重写以上示例

int main(int argc,char *argv[])
{
    int ret=0;
    char *str="hello world \n";
    int status=0;

    //完成write系统调用
    ret=syscall(4,1,str,13);

    //完成exit系统调用
    ret=syscall(1,status);

    return 0;
}

不同体系下都可以调用syscall函数来完成系统调用的执行,syscall相对于程序员来说是与系统无关的。

进阶

不管是syscall还是嵌入式汇编都需要我们记住每个系统调用对应的系统调用号以及对应的参数类型与个数。这极其困难,也极易出错。所以系统开发者将每个系统调用封装成一个函数。
《系统调用(一)》中写到系统调用本质上是函数列表,而用函数封装系统调用其实还原了系统调用原本的样子。
如系统调用write可以封装成int write(int fd,char *buf,int size)函数。当需要调用write系统调用直接查看write函数,马上可以获取参数类型以及个数。

封装可以借助于嵌入式汇编或syscall

//封装write系统调用

//借助于嵌入式汇编
int write(int fd,char *buf,int size)
{
    int ret;

    asm("int $0x80"
    :"=a"(ret)
    : "a"(4),"b"(fd),"c"(buf),"d"(size)
    );

    return ret;
} 

//借助于syscall
int write(int fd,char *buf,int size)
{
    int ret;    
    ret=syscall(4,fd,buf,size);
    return ret;
} 

封装后重写上面示例

int main(int argc,char *argv[])
{
    int ret=0;
    char *str="hello world \n";
    int status=0;

    //完成write系统调用
    ret=write(1,str,13);

    //完成exit系统调用
    ret=exit(status);

    return 0;
}

linux内核中使用一种方式:嵌入式汇编加上宏完成系统调用的封装。如感兴趣可参考系统调用的封装

高阶

实际开发中使用最多的方式是调用glibc中对系统调用的封装。

glibc 是 Linux 下使用的开源的标准 C 库,它是 GNU 发布的 libc 库,即运行时库。glibc 为程序员提供丰富的 API(Application Programming Interface),除了例如字符串处理、数学运算等用户态服务之外,最重要的是封装了操作系统提供的系统服务,即系统调用的封装。许多系统调用在glibc中都有同名的封装函数。一般这些函数的语义与系统调用的语义是一致的,但也有些进行了改变,比如系统调用brk与glibc提供的brk的语义是不一致的(主要是返回值进行了改变)。也存在一些系统调用是glibc未提供的,比如_llseek,glibc提供了lseek64对其进行了封装。glibc不提供一些系统调用,主要出于移植的考虑,比如_llseek在64位中并不存在,所以用lseek64函数替代了它。

对于不存在或者语义不同的系统调用可以使用上面的方式执行系统调用。

AI-PPT 一键生成 PPT:用户输入主题关键词,AI-PPT 可快速生成完整 PPT,涵盖标题、正文、段落结构等,还支持对话式生成,用户可在 AI 交互窗口边查看边修改。 文档导入转 PPT:支持导入 Word、Excel、PDF 等多种格式文档,自动解析文档结构,将其转换为结构清晰、排版规范的 PPT,有保持原文和智能优化两种模式。 AI-PPT 对话 实时问答:用户上传 PPT 或 PPTX 文件后,可针对演示内容进行提问,AI 实时提供解答,帮助用户快速理解内容。 多角度内容分析:对 PPT 内容进行多角度分析,提供全面视野,帮助用户更好地把握内容结构和重点。 多语言对话支持:支持多语言对话,打破语言障碍,方便不同语言背景的用户使用。 AI - 绘图 文生图:用户输入文字描述,即可生成符合语义的不同风格图像,如油画、水彩、中国画等,支持中英文双语输入。 图生图:用户上传图片并输入描述,AI - 绘图能够根据参考图和描述生成新的风格化图像,适用于需要特定风格或元素的创作需求。 图像编辑:提供如 AI 超清、AI 扩图、AI 无痕消除等功能,用户可以上传图片进行细节修改和优化,提升图片质量。 AI - 文稿 文案生成:能够根据用户需求生成多种类型的文章,如市场营销文案、技术文档、内部沟通内容等,提升文案质量和创作效率。 文章润色:对已有文章进行改善和优化,包括语言表达、逻辑连贯性、内容流畅度等方面,使文章更符合用户期望和风格。 文章续写:AI 技术理解文本语境,为用户提供新的想法、补充资料或更深层次的见解,帮助用户丰富文档内容。 AI - 医生 智能健康咨询:包括症状自查,用户输入不适症状,AI 结合病史等信息提供疾病可能性分析与初步建议;用药指导,支持查询药品适应症、禁忌症等,并预警潜在冲突;中医辨证,提供体质辨识与调理建议。 医学报告解读:用户上传体检报告
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值