博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Linux添加系统调用
阅读量:2135 次
发布时间:2019-04-30

本文共 3138 字,大约阅读时间需要 10 分钟。

Linux添加系统调用

  1. 前提知识(系统调用如何执行) 先看文章

    • 我们通常写的c,有相对应的开源的标准库glibc,(2.23的glibc中还是有很多缺陷的)其中的头文件unistd.h就包含了许多的系统调用,如read/write/open等等
    • 系统调用是通过的修改寄存器eax的值,然后通过触发 软中断使系统进入内核空间,比如32位下经典的int 80以及64位的syscall。如果你想更清楚的了解系统调用可以查看我之前写的
    • 所以内核的中断处理函数是根据系统调用号来调用相应的内核函数,c中的read/write,其实调用了内核函数sys_read/sys_write
    • 因此,添加一个系统调用需要注册 系统调用号 以及相应的 中断处理函数,如此,内核才能找到这个系统调用和执行对应的内核函数。内核的汇编代码最终会在include/generated/asm/syscalls_64.h中查找调用号,不过我们并不修改这里,x86平台提供了一个专门用来注册系统调用的文件/arch/x86/entry/syscalls/syscall_64.tbl,在编译时会运行同目录下的syscalltbl.sh脚本,将这个文件中登记过的系统调用都生成到前面的syscalls_64.h文件中。因此我们后面要添加系统调用就是修改这个tbl文件。
    • 系统执行相应功能,调用的是C代码编写的函数,而不是汇编代码,减轻了编写实现系统调用的负担。系统调用的函数定义include/linux/syscalls.h中,因为汇编代码到C代码的参数传递是通过栈实现的,所以可以看到所有系统调用的函数前面都使用了asmlinkage宏,它意味着编译时限制只使用栈来传递参数。
    • 系统调用函数的实现kernel/sys.c 中,当我们注册了相应的系统调用号以及定义了 系统调用的函数 之后,我们就可以在此文件的代码的最后添加自己的函数,这个文件中有很多已经实现的系统调用的函数作为参考。
  2. 添加系统调用号

    • 编辑调用号注册表
    cd  arch/x86/entry/syscalls/vi syscall_64.tbl
    • 添加系统调用号

      #上面省略328     64      pwritev2                sys_pwritev2329     common  pkey_mprotect           sys_pkey_mprotect330     common  pkey_alloc              sys_pkey_alloc331     common  pkey_free               sys_pkey_free332     common  statx                   sys_statx333     common  ps_counter              sys_ps_counter# 下面的我们x32先不管,系统调用号,就在330左右,在此行上面添加# x32-specific system call numbers start at 512 to avoid cache impact# for native 64-bit operation.#

      在这里插入图片描述

  3. 定义系统调用函数

    • 编辑系统调用函数原型定义

      vi include/linux/syscalls.h
    • 添加函数原型(__user 是为了向用户空间传值)

      #以上省略asmlinkage long sys_pkey_mprotect(unsigned long start, size_t len,                                  unsigned long prot, int pkey);asmlinkage long sys_pkey_alloc(unsigned long flags, unsigned long init_val);asmlinkage long sys_pkey_free(int pkey);asmlinkage long sys_ps_counter(int __user *num);asmlinkage long sys_statx(int dfd, const char __user *path, unsigned flags,                          unsigned mask, struct statx __user *buffer);#endif

    在这里插入图片描述

  4. 编写实现函数

    • 实现函数文件

      vi kernel/sys.c
    • 实现函数(这里就只要统计task数量到用户空间,我顺便输出了内核相关的task的信息,可以做调试用)

      SYSCALL_DEFINE1(ps_counter, int __user*, num){
      struct task_struct *task; int counter=0; printk("[Syscall] ps_counter\n"); printk("Now pid = %ld",current->pid);#current是宏,指向当前task printk("Pid Parent->pid"); for_each_process(task){
      counter++; printk(" %ld %ld\n",task->pid,task->parent->pid); } copy_to_user(num, &counter, sizeof(int)); #结果显示到用户空间 return 0;}

      在这里插入图片描述

  5. 编写测试代码

    • 源文件
    vi test_syscall.c
    #include 
    #include
    #include
    int main(void){
    int result; syscall(333,&result); //系统调用号,是你自己在`tbl`中注册的 printf("process number is %d\n",result); return 0;}
    • 编译(一定静态编译,因为你的qemu下的linux可能没有这些库)
    gcc -static -o get_ps_num get_ps_num.c
    • 将编译好的二进制,放到busybox下的_install 下的tmp文件夹中,重新压缩gpio文件,这一步看之前的文章
    find . -print0 | cpio --null -ov --format=newc   | gzip -9 > ../initramfs.cpio.gz
  6. 运行测试程序

    • 编译添加完系统调用号,定义了系统调用函数,完成了函数实现之后,编译linux内核

      sudo make -j8
    • 使用新编译的内核以及刚打包的根文件系统,启动虚拟机

      qemu-system-x86_64 -kernel ~/Desktop/linux-4.14/arch/x86/boot/bzImage -initrd ~/Desktop/busybox-1.32.1/initramfs.cpio

      在这里插入图片描述

转载地址:http://jtugf.baihongyu.com/

你可能感兴趣的文章
检测iOS的网络可用性并打开网络设置
查看>>
简单封装FMDB操作sqlite的模板
查看>>
iOS开发中Instruments的用法
查看>>
iOS常用宏定义
查看>>
什么是ActiveRecord
查看>>
有道词典for mac在Mac OS X 10.9不能取词
查看>>
关于“团队建设”的反思
查看>>
利用jekyll在github中搭建博客
查看>>
Windows7中IIS简单安装与配置(详细图解)
查看>>
linux基本命令
查看>>
BlockQueue 生产消费 不需要判断阻塞唤醒条件
查看>>
强引用 软引用 弱引用 虚引用
查看>>
数据类型 java转换
查看>>
"NetworkError: 400 Bad Request - http://172.16.47.117:8088/rhip/**/####t/approval?date=976
查看>>
mybatis 根据 数据库表 自动生成 实体
查看>>
win10将IE11兼容ie10
查看>>
checkbox设置字体颜色
查看>>
第一篇 HelloWorld.java重新学起
查看>>
ORACLE表空间扩张
查看>>
orcal 循环执行sql
查看>>