Linux 系统调用

一、系统调用

        在现代多任务操作系统上会同时有很多进程对硬件进行访问,然而计算机的硬件资源是有限的,所有这些操作都必须由操作系统来控制

1、Linux系统调用

        Linux通过系统调用限制用户空间对硬件设备和其他资源的访问,相当于在用户空间和硬件设备之间抽象出来一个中间层,对硬件具体类型进行屏蔽。主要作用就是为了保证系统稳定,避免用户应用程序肆意操作,导致崩溃

2、系统调用形式

asmlinkage long sys_getpid(void)

        asmlinkage限定词是一个编译指令,通知编译器不要将函数参数放到CPU寄存器中,仅从栈中提取该函数的参数,每个系统调用都需要这个限定词

3、系统调用号

        每个系统调用都有一个系统调用号,内核将所有注册过的系统调用和系统调用号存储在sys_call_table中

二、系统调用处理程序

1、指定恰当的系统调用

        在x86-64中,系统调用号是通过eax寄存器传递给内核的,用户空间需要将调用号放入eax寄存器

2、参数传递

        函数参数通过ebx、ecx、edx、esi、edi寄存器传递,如果参数超过5个,所有参数在用户地址空间的指针会存储在一个单独的寄存器中

3、参数验证

a.指针指向用户空间,进程不能让内核去读内核空间的数据

b.指针指向当前进程的地址空间,不能让内核去读其他进程的数据

c.进程不能绕过内存可读、可写、可执行的访问限制

三、系统调用上下文

        在Linux中,系统调用运行于内核空间,是用户空间访问内核空间的唯一手段,除异常和中断外,是内核唯一的合法入口。内核在执行系统时处于进程上下文,可以休眠和被抢占,新的进程也可能会抢占当前进程并使用同样的系统调用,故必须保证系统调用是可重入的

        系统调用是通过软中断实现的

四、系统调用实现

1、sys_getpid()

        在arm架构中,系统调用号(syscall id)位于下面的文件中

include/uapi/asm-generic/unistd.h

        打开文件搜索,可以看到,sys_getpid系统调用号为172,但不同架构的系统调用号不同,最好是通过SYS_getpid宏作为参数传递,方便移植在不同架构中

#define __NR_getpid 172                                                                                                                                                                                  
__SYSCALL(__NR_getpid, sys_getpid)

        编写应用程序直接调用系统调用获得pid,与getpid()获取的结果进行对比

#include <unistd.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/syscall.h>
#include <string.h>
#include <errno.h>

int main()
{
	int pit_syscall = -1;
	pit_syscall = syscall(SYS_getpid);

	printf("syscall:[%d], syscall_id[%d]\n", pit_syscall, SYS_getpid);
	if (pit_syscall == -1) {
		printf("Error:%s\n", strerror(errno));
	}

	printf("getpid:[%d]\n", getpid());

	return 0;
}
aarch64-linux-gnu-gcc sys_test.c -o sys_test

        在arm64设备运行,可以看到下面结果,表明调用成功

# ./sys_test 
syscall:[1154], syscall_id[172]
getpid:[1154]

2、添加自己的syscall

(1)添加自定义处理函数

        在sys.c文件中添加下面函数

kernel/sys.c
SYSCALL_DEFINE0(call_test)                                                                                                                                                                              
{
    printk("Hello World, sys_call success!\n");
    return 0;
}

        注:可以参照其他syscall写法,如果传递1个参数,就使用SYSCALL_DEFINE1,以此类推

(2)头文件声明

声明位于syscalls.h文件

include/linux/syscalls.h
asmlinkage long sys_call_test(void);
(3)定义系统调用号

        对于ARM架构,系统调用号文件位于

include/uapi/asm-generic/unistd.h
+#define __NR_call_test 451
+__SYSCALL(__NR_call_test, sys_call_test)
+
 #undef __NR_syscalls
-#define __NR_syscalls 451
+#define __NR_syscalls 452

        注意:需要将__NR_syscalls 也同步增加

(4)修改系统调用表

        添加我们自己的系统调用到系统调用表 

arch/x86/entry/syscalls/syscall_64.tbl
 449    common  futex_waitv             sys_futex_waitv
 450    common  set_mempolicy_home_node sys_set_mempolicy_home_node
+451    common  call_test       sys_call_test
(5)C程序测试

        按上述方法修改后,编译并运行内核,我是在QEMU虚拟机中运行的,可以参照QEMU这篇博客搭建运行环境

#include <unistd.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/syscall.h>
#include <string.h>
#include <errno.h>

int main()
{
	int ret = -1;
	ret = syscall(451);

	printf("syscall result[%d]\n", ret);
	if (ret == -1) {
		printf("Error:%s\n", strerror(errno));
		return ret;
	}

	printf("my system call test success!\n");

	return 0;
}
aarch64-linux-gnu-gcc sys_test.c -o sys_test

        运行结果如下,可以看到内核printk出了我们自己编写的syscall

# ./sys_test 
[ 2523.918842] Hello World, sys_call success!
syscall result[0]
my system call test success!

【参考博客】

[1] Linux内核设计与实现

[2] Linux系统调用详解(实现机制分析) - 知乎

[3] Linux内核 | 系统调用 - 世至其美

[4] Linux操作系统实验 —— 增加系统调用_linux编写一个新的系统调用函数-CSDN博客

[5] Linux内核笔记(二) 系统调用_linux 系统调用 不也是以库的方式提供的吗-CSDN博客

[6] 【从源码过反调试】给安卓12内核增加个syscall

相关推荐

  1. linux系统调用介绍

    2024-04-07 14:08:03       42 阅读
  2. Linux 系统调用

    2024-04-07 14:08:03       13 阅读
  3. Linux系统调用mmap

    2024-04-07 14:08:03       12 阅读
  4. OpenHarmony—Linux系统调用

    2024-04-07 14:08:03       30 阅读

最近更新

  1. TCP协议是安全的吗?

    2024-04-07 14:08:03       18 阅读
  2. 阿里云服务器执行yum,一直下载docker-ce-stable失败

    2024-04-07 14:08:03       19 阅读
  3. 【Python教程】压缩PDF文件大小

    2024-04-07 14:08:03       19 阅读
  4. 通过文章id递归查询所有评论(xml)

    2024-04-07 14:08:03       20 阅读

热门阅读

  1. 前2个月湖南外贸进出口总值810.6亿元

    2024-04-07 14:08:03       14 阅读
  2. MyBatis实战:如何将拼接的SQL打印到日志

    2024-04-07 14:08:03       19 阅读
  3. 24双非考研哈尔滨工程大学计算机(@程程笔记)

    2024-04-07 14:08:03       19 阅读
  4. [Pytorch][缘来如此]:PyTorch中的广播机制

    2024-04-07 14:08:03       10 阅读
  5. 【软设】知识点速记3

    2024-04-07 14:08:03       9 阅读
  6. 播放器的音视频不同步问题:ffplay

    2024-04-07 14:08:03       11 阅读
  7. 六、Mybatis-动态SQL

    2024-04-07 14:08:03       11 阅读
  8. RabbitMQ

    RabbitMQ

    2024-04-07 14:08:03      23 阅读