Linux内核开发——新添内核用户接口

1. 前言

用户态的程序如果想访问内核态的程序,并不能直接访问,而是通过中断处理机制来完成的。CPU提供一个陷入指令(Traps),亦称访管指令。用户态程序通过syscall附加系统调用号以及参数来完成对内核接口的调用。
syscall是glibc封装的接口,其底层实现主要是发起一个陷入指令中断int $0x80,然后内核处理处理此中断函数。
系统调用号,是一系列从0开始递增的数字,其实质是一组数组的下标,而这个数组存放一内核接口函数的地址。这样在内核响应陷入指令后,就可以通过系统调用号找到相应的函数地址,然后来调用相应的内核函数。

2. 实现代码

不同架构的实现略有不同,此处主要针对x86_64架构。在相关模块中添加源文件fio_test.c(此处添加到fs目录下),并添加相应的代码:

#include <linux/init.h>
	#include <linux/syscalls.h>
	#include <linux/time_namespace.h>
	
	
	#define KTP_COUNT  128
	struct timespec64 kernel_tp[KTP_COUNT] = {0};
	
	void clock_time_ex(size_t idx)
	{
	    if (idx >= KTP_COUNT)
	        return;
	
		ktime_get_ts64(&kernel_tp[idx]);
		timens_add_monotonic(&kernel_tp[idx]);
	}
	
	SYSCALL_DEFINE2(get_fio_time, struct __kernel_timespec __user *, tp, size_t, idx)
	{
	    int error = 0;
	    if (idx >= KTP_COUNT)
	        return -EINVAL;
	    printk("hello World!\n");
	    clock_time_ex(idx);
			if (put_timespec64(&kernel_tp[idx], tp))
					error = -EFAULT;
	
		return error;
	}

SYSCALL_DEFINE2宏展开大概是:
asmlinkage long sys_get_fio_time(struct __kernel_timespec __user * tp, size_t idx);
将源文件添加到fs目录下makefile文件。
在这里插入图片描述
makefile此处是利用隐式编译,即只需要知道目录文件名,就会自动去找同名的源文件并进行编译。

3. 函数声明

函数声明一般放在/include/linux/syscalls.h文件中,也可以放在arch/x86目录下的syscalls.h文件中。
其中的asmlinkage是一个必需的限定词,用于通知gcc编译器从堆栈中提取函数的参数,而不是从寄存器传递。这样无论是标准C还是汇编代码,都可以调用此函数。

asmlinkage long sys_get_fio_time(const __kernel_timespec __user * tp, size_t idx);

4. 配置syscall.tbl

不同的架构,会有不同的系统调用表文件,此处针对x86_64,则位于/arch/x86/entry/syscalls目录,目录中有两个文件,分别为syscall_32.tbl和syscall_64.tbl,前者针对i386架构的cpu,后者针对的是x86_64架构的cpu。此处在syscall_64.tbl中添加。函数声明和syscall.tbl一起组成一个索引对应函数地址的数组。因为Linux内核文件为elf文件,其中不包括函数符号,只能直接调用函数地址。为了更方便地调用函数地址,提供一个系统调用号来与函数地址配对。
在这里插入图片描述
在这里插入图片描述
syscall.tbl中的abi类型,有i386,64,x32,分别对应gcc的-m32,-m64,mx32。abi类型还有一个common,其兼容-m64和-mx32编译的版本。默认一般填写common即可。

5. 编译内核

下面是直接编译内核,并将相关文件拷贝到系统目录,并更新grub默认项为当前编译版本。

sudo make -j6
sudo mkinitramfs /lib/modules/5.17.12 -o /boot/initrd.img-5.17.12
sudo cp arch/x86/boot/bzImage /boot/vmlinuz-5.17.12
sudo cp System.map /boot/System.map-5.17.12

sudo update-grub

6. 测试代码

335是函数get_fio_time对应的系统调用号,glibc封装了一个标准函数syscall可以调用系统调用号。
可以直接通过系统调用号来调用:

    struct timespec ts = {0}; 
    syscall(335, &tS, 0);

可以参考glibc将其封装为一个接口,方便调用:

long get_fio_time(struct timespec *tp, int idx)
{
     return syscall(335, tp, idx);
}

完整的测试代码:

#include <stdio.h>
#include <unistd.h>
#include <sys/syscall.h>
#include <time.h>

long get_fio_time(struct timespec *tp, int idx)
{
     return syscall(335, tp, idx);
}

int main(void)
{
        char buf[256] = {0};
        struct timespec ts = {0};
        get_fio_time(&ts, 0);
        printf("time1:%ld-%lu\n",ts.tv_sec, ts.tv_nsec);
        clock_gettime(1, &ts);
        printf("time2:%ld-%lu\n",ts.tv_sec, ts.tv_nsec);
        return 0;
}

get_fio_time的作用与clock_gettime一样的。
我们还可以在在dmesg内核日志中看到内核接口中的hello World的打印信息。

7. 附加

  1. i386或x86,泛指以前32位架构的cpu.
  2. x86-64或x64,兼容32位指令的64位的新架构。
  3. gcc编译版本
    a. -m32,指定编译运行在i386架构cpu上的程序,如果想运行在x64架构上,需要安装gcc-multilib来兼容老接口。
    b. -m64,编译64位的版本,运行在x64架构CPU上。
    c. -mx32,编译32位的版本,但是可以直接在x64架构上运行。
  4. linux内核中的syscall.tbl中的abi类型,有i386,64,x32,分别对应gcc的-m32,-m64,mx32。abi类型还有一个common,其兼容-m64和-mx32编译的版本。
  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值