通过调用C语言的库函数与在C代码中使用内联汇编两种方式来使用同一个系统调用来分析系统调用的工作机制

前言说明

本篇为网易云课堂Linux内核分析课程的第四周作业,我将通过调用C语言的库函数与在C代码中使用内联汇编两种方式来使用同一个系统调用来分析系统调用的工作机制,本篇中,我将分别使用两个典型的系统调用(getpid,open)来进行实例分析,意图通过这两个不同的系统调用来阐述Linux中的系统调用的工作方式。


本篇关键词:系统调用,内联汇编


运行环境:

  • Ubuntu 14.04 LTS x64
  • gcc 4.9.2
  • gdb 7.8
  • vim 7.4 with vundle

分析过程

系统调用(System Call)是操作系统为在用户态运行的进程与硬件设备(如CPU、磁盘、打印机等)进行交互提供的一组接口。当用户进程需要发生系统调用时,CPU 通过软中断切换到内核态开始执行内核系统调用函数,一般而言,在Linux中会有三种系统调用:

  • 通过标准C库(libc)的API
  • 通过Syscall直接调用
  • int 0x80中断指令陷入

getpid

getpid的函数原型为:

pid getpid(void)

其功能为返回一个进程的进程ID,该函数没有参数

#include <stdio.h>
#include <unistd.h> int main(void) {
pid_t process_id; #if 1
process_id = getpid(); #else
asm volatile (
"movl $0, %%ebx\n\t"
"movl $0x14, %%eax\n\t" //getpid的系统调用号是0x14
"int $0x80\n\t" //中断向量陷入指令
"movl %%eax, %0\n\t"
:"=r"(process_id)
);
#endif printf("process is %u\n", process_id);
return 0;
}
  • 首先,从C代码分析,从内联汇编可以看出,当进行系统调用时,首先应该把系统调用号放入eax寄存器中,然后通过int 0x80中断向量指令来使用户态进程陷入内核态,参数的传递是通过寄存器,eax传递的是系统调用号,ebx,ecx,edx,exi,edi来传递其他参数,同时eax也负责保存系统调用后的返回值
//调用C库的API
main:
leal 4(%esp), %ecx
andl $-16, %esp
pushl -4(%ecx)
pushl %ebp
movl %esp, %ebp
pushl %ecx
subl $20, %esp
call getpid
movl %eax, -12(%ebp)
subl $8, %esp
pushl -12(%ebp)
pushl $.LC0
call printf
addl $16, %esp
movl $0, %eax
movl -4(%ebp), %ecx
leave
leal -4(%ecx), %esp
ret //内联汇编方式
main:
leal 4(%esp), %ecx
andl $-16, %esp
pushl -4(%ecx)
pushl %ebp
movl %esp, %ebp
pushl %ecx
subl $20, %esp
movl $0, %ebx
movl $0x14, %eax
int $0x80
movl %eax, %eax
movl %eax, -12(%ebp)
subl $8, %esp
pushl -12(%ebp)
pushl $.LC0
call printf
addl $16, %esp
movl $0, %eax
movl -4(%ebp), %ecx
leave
leal -4(%ecx), %esp
ret
  • 从以上代码可以看出除了内联汇编与call getpid外,其他部分完全相同,这里需要指出的一点是:
	movl $0, %ebx
movl $0x14, %eax
int $0x80
movl %eax, %eax

这里由于我是选择了在输出时,使用'=r'约束条件,即输出的值可以保存在任何可用的全局的寄存器中,所以这里会有一步movl %eax, %eax


open

open的函数原型为:

int open(const char *pathname, int flags, mode_t mode)

其功能是打开一个特定路径下的文件,返回一个文件描述符

int Open(const char *pathname, int flags, mode_t mode) {
int fd_t; asm volatile (
"movl S0, %%ebi\n\t"
"movl $1, %%ebx\n\t" //pathname
"movl $2, %%ecx\n\t" //flags
"movl $3, %%edx\n\t" //mode
"movl $0x5, %%eax\n\t"
"int $0x80\n\t"
"movl %%eax, $0\n\t"
:"=r"(fd_t)
:"b"(pathname), "c"(flags), "d"(mode)
); return fd_t;
}
  • 这里我封装了一个Open系统调用,与第一个实例中相同的部分这里就不再赘述了,仅仅说一下不同的地方,:"b"(pathname), "c"(flags), "d"(mode),这里是输入参数,表示pathnameflagsmode,这三个参数分别传递到ebxecxedx三个寄存器中
Open:
pushl %ebp
movl %esp, %ebp
pushl %ebx
subl $16, %esp
movl 8(%ebp), %eax
movl 12(%ebp), %ecx
movl 16(%ebp), %edx
movl %eax, %ebx
movl S0, %ebi
movl $1, %ebx
movl $2, %ecx
movl $3, %edx
movl $0x5, %eax
int $0x80
movl %eax, $0
movl %eax, -8(%ebp)
movl -8(%ebp), %eax
addl $16, %esp
popl %ebx
popl %ebp
ret

我的总结

  • 经过以上的分析,我们可以得知系统调用的一个基本过程,当用户态进程调用API时,int 0x80中断指令触发用户态向内核态转换,同时根据相应的系统调用号执行系统调用,最后返回系统调用结果
  • 通过这一周的学习,我更加熟悉了系统调用的本质,也更加熟悉了内联汇编,系统调用是用户进程与操作系统进行交互的核心,通过int 0x80中断向量指令实现从用户态进入内核态,系统调用过程中,eax寄存器负责传递系统调用号,ebx,ecx等其他寄存器负责传递其他参数

参考资料

  • Understanding The Linux Kernel, the 3rd edtion

署名信息

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

通过调用C语言的库函数与在C代码中使用内联汇编两种方式来使用同一个系统调用来分析系统调用的工作机制的更多相关文章

  1. C语言的本质(32)——C语言与汇编之C语言内联汇编

    用C写程序比直接用汇编写程序更简洁,可读性更好,但效率可能不如汇编程序,因为C程序毕竟要经由编译器生成汇编代码,尽管现代编译器的优化已经做得很好了,但还是不如手写的汇编代码.另外,有些平台相关的指令必 ...

  2. 通过库函数API和C代码中嵌入汇编代码剖析系统调用的工作机制

    作者:吴乐 山东师范大学<Linux内核分析>MOOC课程http://mooc.study.163.com/course/USTC-1000029000 本次实验的主要内容就是分别采用A ...

  3. 实验--使用库函数API和C代码中嵌入汇编代码两种方式使用同一个系统调用(杨光)

    使用库函数API和C代码中嵌入汇编代码两种方式使用同一个系统调用 攥写人:杨光  学号:20135233 ( *原创作品转载请注明出处*) ( 学习课程:<Linux内核分析>MOOC课程 ...

  4. linux内核分析作业4:使用库函数API和C代码中嵌入汇编代码两种方式使用同一个系统调用

    系统调用:库函数封装了系统调用,通过库函数和系统调用打交道 用户态:低级别执行状态,代码的掌控范围会受到限制. 内核态:高执行级别,代码可移植性特权指令,访问任意物理地址 为什么划分级别:如果全部特权 ...

  5. LInux内核分析--使用库函数API和C代码中嵌入汇编代码两种方式使用同一个系统调用

    实验者:江军 ID:fuchen1994 实验描述: 选择一个系统调用(13号系统调用time除外),系统调用列表参见http://codelab.shiyanlou.com/xref/linux-3 ...

  6. 实验四——使用库函数API和C代码中嵌入汇编代码两种方式使用同一个系统调用

    实验目的: 使用库函数API和C代码中嵌入汇编代码两种方式使用同一个系统调用 实验过程: 查看系统调用列表 get pid 函数 #include <stdio.h> #include & ...

  7. Linux内核设计第四周学习总结 使用库函数API和C代码中嵌入汇编代码两种方式使用同一个系统调用

    陈巧然原创作品 转载请注明出处 <Linux内核分析>MOOC课程http://mooc.study.163.com/course/USTC-1000029000 实验目的: 使用库函数A ...

  8. C语言中递归什么时候能够省略return引发的思考:通过内联汇编解读C语言函数return的本质

    事情的经过是这种,博主在用C写一个简单的业务时使用递归,因为粗心而忘了写return.结果发现返回的结果依旧是正确的.经过半小时的反汇编调试.证明了我的猜想,如今在博客里分享.也是对C语言编译原理的一 ...

  9. C#动态调用WCF接口,两种方式任你选。

    写在前面 接触WCF还是它在最初诞生之处,一个分布式应用的巨作. 从开始接触到现在断断续续,真正使用的项目少之又少,更谈不上深入WCF内部实现机制和原理去研究,最近自己做一个项目时用到了WCF. 从这 ...

随机推荐

  1. Vue 2.0入门基础知识之内部指令

    1.Vue.js介绍 当前前端三大主流框架:Angular.React.Vue.React前段时间由于许可证风波,使得Vue的热度蹭蹭地上升.另外,Vue友好的API文档更是一大特色.Vue.js是一 ...

  2. Java Web 开发中路径相关问题小结

    Java Web开发中路径问题小结 (1) Web开发中路径的几个基本概念 假设在浏览器中访问了如下的页面,如图1所示: 图1 Eclipse中目录结构如图2所示: 图2 那么针对这个站点的几个基本概 ...

  3. c# 从DataGridVieew导出到excel

    public static bool DataGridViewToExcel(DataGridView dataGridView, bool isShowExcel) { int rowsQty = ...

  4. UVA 1479 Graph and Queries (Treap)

    题意: 给一个无向图,再给一系列操作(以下3种),输出最后的平均查询结果. (1)D X 删除第x条边. (2)Q X k  查询与点X相连的连通分量中第k大的点的权值. (3)C X v  将点X的 ...

  5. pycharm激活码 pycharm安装后激活方式 pycharm汉化包安装

    汉化包 下载地址: 链接:http://pan.baidu.com/s/1pL6xWl9 密码:x1fh 将下载好的文件解压:将resources_cn.jar放到安装目录下的lib目录下即可 重启 ...

  6. (译)IOS block编程指南 1 介绍

    Introduction(介绍) Block objects are a C-level syntactic and runtime feature. They are similar to stan ...

  7. TIOJ1208 第K大连续和

    第k大的题一般都有点麻烦 pbds库的tree,需要研究一下https://codeforces.com/blog/entry/11080find_by_order() and order_of_ke ...

  8. light oj 1336 sigma function

    常用的化简方法(高中就常用了):     p^(e+1)-1/p-1=             [ p^(e+1) -p + (p-1) ]/ (p-1) = p*(p^e-1)/(p-1) + 1  ...

  9. 记忆化搜索 || POJ 1088 滑雪

    从任意一点可以往上下左右比它小的数那里走,问最远长度是多少 *解法:每一点dfs搜索一遍 记忆化搜索:http://blog.csdn.net/acmer_sly/article/details/53 ...

  10. 编写一个函数,输入n为偶数时,调用函数求1/2+1/4+...+1/n,当输入n为奇数时,调用函数1/1+1/3+...+1/n(利用指针函数)

    *题目:编写一个函数,输入n为偶数时,调用函数求1/2+1/4+...+1/n,当输入n为奇数时,调用函数1/1+1/3+...+1/n(利用指针函数) public class 第三十九题按条件计算 ...