目录
使用库函数 API 和 C 代码中嵌入汇编代码两种方式使用同一个系统调用
使用库函数 API 和 C 代码中嵌入汇编代码两种方式使用同一个系统调用
选择的系统调用为getpid,返回值是当前进程的pid号
1.使用库函数API使用getpid
编写C函数如下
运行效果如下
2.C代码中嵌入汇编代码使用getpid
首先使用grep命令获取自己Linux的系统调用号
grep "__NR_getpid" /usr/include/x86_64-linux-gnu/asm/unistd_64.h
效果如下
C中嵌入汇编代码之后如下
运行效果如下(w4是C库API,w42是汇编)
3.汇编代码分析
#include <stdio.h>
int main() {
long pid;/*
使用汇编语言内联在64位Linux上进行系统调用。
rax寄存器包含系统调用号
系统调用的结果将返回在rax寄存器中。
syscall指令执行系统调用。
*/
__asm__ (
"mov $39, %%rax;" // 'getpid'的系统调用号是39
"syscall;" // 执行系统调用
"mov %%rax, %0;" // 从'rax'移动结果到'pid'变量
: "=r" (pid) // 输出到'pid'
:
: "%rax" // 使用了'rax'寄存器
);
// 打印进程ID
printf("Process is: %ld\n", pid);
return 0;
}
4.总结
(1)什么是系统调用
在 Linux 操作系统中,系统调用是运行在用户空间的程序请求运行在内核空间的服务的主要方式,例如文件操作、进程控制、通信等。由于安全和稳定性的需要,用户空间的程序并不能直接访问内核空间,系统调用为用户空间和内核空间提供了一个清晰界定的接口。
(2)工作机制
-
系统调用接口(API):
我们通常不会直接写系统调用,而是使用封装了系统调用的库函数(例如 C 库),这些函数会执行实际的系统调用。 -
触发系统调用:
系统通过特殊的指令(如int 0x80
、syscall(本次实验用到的)
、sysenter
等)来触发系统调用,这些指令会使处理器从用户模式切换到内核模式并执行中断处理程序,系统调用号和参数会通过寄存器传递给内核,指明了要执行的特定服务和传递给该服务的参数。 -
内核中断处理:
处理器切换到内核模式时会开始执行内核中的中断处理程序。这个处理程序会检查寄存器中的系统调用号,并且通过一个称为系统调用表的内部数据结构将调用号映射到相应的内核函数。 -
执行系统调用:
一旦确定了要执行的具体函数,内核就会以特权模式运行这个函数。这个函数可以访问受保护的内核内存,并且可以执行通常不能在用户空间执行的操作(比如直接与硬件通信),且函数执行所需的任何参数都是从之前保存在寄存器中的值复制的。 -
返回用户模式:
系统调用结束后,控制权会返回给用户空间程序,处理器从内核模式切换回用户模式,系统调用的结果(比如函数的返回值)会通过寄存器返回,用户空间程序可以检查这个值来确定操作是否成功。