谈谈从库函数API和C代码中嵌入汇编两种方式来实现系统调用

  程  序 原创作品转载请注明出处 《Linux内核分析》MOOC课程http://mooc.study.163.com/course/USTC-1000029000

        先来谈谈什么是系统调用,系统调用中涉及到三个概念,分别是系统调用,系统调用函数,系统调用表。

       系统调用:是通过软件中断向内核发送一个明确的请求,系统调用实现是在内核完成的,而用户态的函数的是在函数中实现的。系统调用的实质是函数调用,只是调用的函数是系统调用,处于内核态而已。用户在调用系统调用时会向内核传递一个系统调用号,然后系统调用处理程序通过此号从系统调用表中找到相应的内核函数执行,最后返回。可以说,操作系统为用户态进程与硬件设备进行交互提供了一组接口。

 

       系统调用号:在Linux3.16中有382个函数调用,加上自己写的这个,一共有383个系统调用,Linux为每一个系统调用定义了唯一的编号,叫做系统调用号。它定义在arch/arm/include/asm/unistd.h。

 

       系统调用表:为了将系统调用号和相应的服务例程结合起来内核利用了系统调用表,这个表放在sys_call_table数组中,它是一个函数指针,每一个函数指针都指向系统调用的封装例程。它定义在arch/arm/kernel/call.S中。

 

       熟悉了系统调用的三个概念,接下来我们用图来说明系统调用是怎么回事?本次选择getpid(void)这个函数来进行说明,getpid(void)是用来获取进程的id值的一个函数。使用系统调用方法有两种,一种是通过库函数API进行调用,另一种是C代码中嵌入汇编代码进行调用。同时,一个单独的API可能调用几个系统调用,不同的API可能调用了同一个系统调用。

       系统调用的工作流程如下图所示:首先,我们调用系统函数,然后系统函数中执行到int 0x80语句的时候进行陷入系统调用,然后在系统调用表中找到系统调用功能函数,执行完功能函数依次返回,然后中断结束。

       

                                                                                图1.系统调用执行流程

          了解完系统调用的流程,我们来看下系统调用在直接调用和c语言嵌入汇编两种使用方法的具体操作,编写文件chengxu.c。

#include <stdio.h>

#include <unistd.h>

int main(){

    int volatile clanguage_id,emberred_id;

    clanguage_id = getpid();

    printf("clanguage_id = %u\n",clanguage_id);//输出直接调用系统函数的进程id

    asm volatile(

    "movl $0,%%ebx\n\t"

    "movl $20,%%eax\n\t"  //eax存放系统调用号

    "int $0x80\n\t"

    "movl %%eax,%0\n\t"

    :"=m"(emberred_id)

    );

    printf("emberred_id = %u\n",emberred_id);//输出通过c代码嵌入汇编方式的系统调用id进程

}

        下面来说说这个函数的过程,第一步定义了两个int型变量,第二步直接调用getpid()函数,第三步将调用系统调用的返回值进行输出。然后换c语言嵌入汇编执行,第一步将0放入ebx作为传递编号,第二步将调用号传给eax,第三步执行陷入指令,128,第四步将返回值放入emberred_id中,第五步将返回值进行打印。真个流程就是这样,我们在这个文件中进行了两次系统调用,当然两次的调用结果是一样的。

 

      下面来看看在实际操作效果怎么样?


                                                                                         图2.编写的源文件,包含直接调用和嵌入汇编两种


                                                                                         图3.编译后运行的结果,显示两者的打印结果

         总结:上面的输出结果显示,我们的两种调用的效果是一样的。总的来说,操作系统为用户态进程与硬件设备进行交互提供了一组接口。系统调用是系统提供的一系列函数,目的主要是为了保护系统的安全。Linux内核是系统最为复杂的地方,当一般程序员对其进行操作的时候是没有权限执行内核态任务,主要是怕一般程序员学的不是很好,破坏了系统。如果一般程序员想要操作内核态的某些功能怎么办呢?这个时候就要用到我们的系统调用,你只需要说出你的需求,系统去帮你实现,而用户不需要关心实现细节。加上理解系统调用的流程,我想系统调用你应该就掌握的差不多了。







  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
#include"..\ucos-ii\includes.h" /* uC/OS interface */ #include "..\ucos-ii\add\osaddition.h" #include "..\inc\drv.h" #include <string.h> #include <math.h> #pragma import(__use_no_semihosting_swi) // ensure no functions that use semihosting ///******************任务定义***************/// OS_STK Main_Stack[STACKSIZE*8]={0, }; //Main_Test_Task堆栈 void Main_Task(void *Id); //Main_Test_Task #define Main_Task_Prio 12 /**************已经定义的OS任务************* tcp监控任务 11 以太网物理层监控任务 8 触摸屏任务 9 键盘任务 10 lcd刷新任务 59 系统任务 1 *****************************************************/ ///*****************事件定义*****************/// OS_EVENT *Nand_Rw_Sem; //Nand_Flash读写控制权旗语 //and you can use it as folloeing: // Nand_Rw_Sem=OSSemCreate(1); //创建Nand-Flash读写控制权旗语,初值为1满足互斥条件// // OSSemPend(Nand_Rw_Sem,0,&err); // OSSemPost(Nand_Rw_Sem); OS_EVENT *Uart_Rw_Sem; //Uart读写控制权旗语 //and you can use it as folloeing: // Uart_Rw_Sem=OSSemCreate(1); //创建Uart读写控制权旗语,初值为1满足互斥条件// // OSSemPend(Uart_Rw_Sem,0,&err); // OSSemPost(Uart_Rw_Sem); ////////////////////////////////////////////////////////// void initOSGUI() //初始化操作系统的图形界面 { initOSMessage(); initOSList(); initOSDC(); initOSCtrl(); initOSFile(); } ///////////////////////////////////////////////////// // Main function. // ////////////////////////////////////////////////////

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值