文章目录

  • 问题
  • 运行环境
  • 程序组成
  • 实现思路
  • helllo.c
  • Makefile
  • main.c
  • 程序运行

问题

在Linux内核中增加一个系统调用,并编写对应的linux应用程序。利用该系统调用能够遍历系统当前所有进程的任务描述符,并按进程父子关系将这些描述符所对应的进程id显示出来。


运行环境

Ubuntu-20.04 64位虚拟机

程序组成

1,采用hello.c实现新的系统调用的函数 ,按照内核模块编写的规范来编写程序(包含有两部分:1,修改系统调用表;2,编写自己的系统调用服务程序)
      2,main.c 用来进行调用所编写的中断例程

实现思路

因为之前了解过汇编的中断向量表,有过一定的实践,所以直接采用修改linux系统调用表的方法。

1,如何在系统调用表中添加新的表项?
      先找到系统调用表的入口地址,由于现在的linux系统会对给表做一个安全的保护,不让用户轻易的修改系统调用表,所以需要去掉系统的保护之后,才能将我们自己编写的程序地址加载到系统调用表中,接下来才能被调用。

a,查找系统调用表地址 sudo cat /proc/kallsyms | grep sys_call_table 演示如下:

cedric@ubuntu:~/Desktop/C$ sudo cat /proc/kallsyms | grep sys_call_table
ffffffffb7400280 R x32_sys_call_table
ffffffffb74013a0 R sys_call_table
ffffffffb74023e0 R ia32_sys_call_table

其中中间的地址ffffffffb74013a0是我们后面会采用到的。注意:这个地址并非一成不变的,特别的,每次开机都需要执行该命令获得新的系统调用表的地址。
      b,需要修改cr0控制寄存器的第16位以便于用来写系统调用表,将该位清0,禁用系统对调用表的写保护。在此采用汇编语言来实现。注意:在使用内嵌汇编时,在linux下需要采用AT&T的汇编语法,并且在对寄存器操作时应该禁用中断响应,防止出错。禁用写保护的汇编代码如下:

    asm volatile("cli;"       //禁止中断"push %rax;""movq %cr0,%rax;""and $0xfffffffffffeffff,%rax;"  //第16位置0,禁用写保护"movq %rax,%cr0;""popq %rax;"//"sti;");

c,修改系统调用表项,将其替换为自己编写的程序。首先将得到的系统调用表地址保存到变量SYS_CALL_TABLE_ADDRESS中(每次开机都要进行修改),然后将系统调用表223号表项内容保存起来,禁用系统的写保护,将我们编写的函数sys_mycall地址替换223号表项,恢复系统的写保护,整个替换工作就此结束。代码如下:

    sys_call_table_my=(unsigned long*)(SYS_CALL_TABLE_ADDRESS); //初始化全局变量saveAddress=sys_call_table_my[NUM];  //保存223号表项clear_cr0();sys_call_table_my[NUM]=(unsigned long) &sys_mycall;  //设置223号表项为我们自己的函数restore_cr0();

2,实现具体想要的功能,将所有进程按照父子关系进行显示。
      采用list_for_each函数,从init_task的地址开始递归访问即可,代码如下:

static void getTasks(struct task_struct* root)
{struct task_struct* p;struct list_head* list;list_for_each(list,&root->children) {p = list_entry(list,struct task_struct,sibling);printk("parent: id and name: %d %s    myself: id and name: %d %s\n",p->parent->pid,p->parent->comm,p->pid,p->comm);getTasks(p);}
}

helllo.c

这里需要注意一下内核模块的编写规范,包括模块凭证,入口和退出函数,以及printk函数的使用。


#include <linux/init.h>
#include <linux/module.h>
#include <linux/sched.h>  //定义task_struct
#include <linux/list.h>   //定义list_head
#include <linux/init_task.h>  //定义init_task
MODULE_LICENSE("Dual BSD/GPL");#define SYS_CALL_TABLE_ADDRESS 0xffffffff87a013a0  //本电脑sys_call_table对应的地址
#define NUM 223static unsigned long saveAddress;//保存223号表项的入口地址
unsigned long* sys_call_table_my=0;//用来作为系统调用表的首地址 static int clear_cr0(void)//AT&T 64位汇编
{asm volatile("cli;"       //禁止中断"push %rax;""movq %cr0,%rax;""and $0xfffffffffffeffff,%rax;"  //第16位置0,禁用写保护"movq %rax,%cr0;""popq %rax;"//"sti;");printk(KERN_INFO " Hello World enter\n");return 0;
}static void restore_cr0(void)
{asm volatile(//"cli;""pushq %rax;""movq %cr0,%rax;""or $0x0000000000010000,%rax;""movq %rax,%cr0;""popq %rax;""sti;"       //恢复中断);printk(KERN_INFO " Cruel World exit\n");
}static void getTasks(struct task_struct* root)
{struct task_struct* p;struct list_head* list;list_for_each(list,&root->children) {p = list_entry(list,struct task_struct,sibling);printk("parent: id and name: %d %s    myself: id and name: %d %s\n",p->parent->pid,p->parent->comm,p->pid,p->comm);getTasks(p);}
}static int sys_mycall(void)//测试自己的系统调用
{printk(KERN_INFO"The pids will be show...:  \n");getTasks(&init_task);return current->pid;
}static int call_init(void)
{sys_call_table_my=(unsigned long*)(SYS_CALL_TABLE_ADDRESS); //初始化全局变量printk(KERN_INFO"call_init......\n");saveAddress=sys_call_table_my[NUM];  //保存223号表项clear_cr0();sys_call_table_my[NUM]=(unsigned long) &sys_mycall;  //设置223号表项为我们自己的函数restore_cr0();return 0;
}static void call_exit(void)
{printk(KERN_INFO"call_exit......\n");clear_cr0();sys_call_table_my[NUM]=saveAddress;   //恢复223号表项原有内容restore_cr0();
}module_init(call_init);
module_exit(call_exit);MODULE_AUTHOR("cedric");

Makefile

这是一个可用的Makefile文件,文件名就是Makefile,不需要后缀名。它对hello.c程序进行编译。

ifneq ($(KERNELRELEASE),)  # call from kernel build systemobj-m  := hello.o
elseKERNELDIR := /lib/modules/$(shell uname -r)/buildPWD       := $(shell pwd)
default:make -C $(KERNELDIR) M=$(PWD) modules
endif

main.c

它仅用来调用新的223号中断程序,测试最终结果。

#include<unistd.h>
#include<sys/syscall.h>
#include<stdio.h>int main()
{int pid=syscall(223); //在此调用223号中断例程。printf("The current pid is: %d\n",pid);return 0;
}

程序运行

1,采用 sudo cat /proc/kallsyms | grep sys_call_table 命令获取当前电脑的系统调用表地址。
2,修改hello.c 中的对应语句,如下:
#define SYS_CALL_TABLE_ADDRESS 0xffffffff87a013a0
3,进入到root模式下make: sudo su + make
4,加载内核模块 hello.ko: insmod hello.ko
5,编译main.c: gcc –o main main.c
6,通过main调用223号服务例程: ./main
7,在系统日志中查看输出结果: cat /var/log/syslog
8,卸载内核模块: rmmod hello

liunx(3)-内核模块编写与系统调用相关推荐

  1. Linux内核模块编写详解

    内核编程常常看起来像是黑魔法,而在亚瑟 C 克拉克的眼中,它八成就是了.Linux内核和它的用户空间是大不相同的:抛开漫不经心,你必须小心翼翼,因为你编程中的一个bug就会影响到整个系统,本文给大家介 ...

  2. 【Linux 内核】Linux 内核特性 ( 组织形式 | 进程调度 | 内核线程 | 多平台虚拟内存管理 | 虚拟文件系统 | 内核模块机制 | 定制系统调用 | 网络模块架构 )

    文章目录 一.Linux 内核特性 1.Linux 内核组织形式 2.Linux 进程调度 3.Linux 内核线程 4.Linux 内核多平台虚拟内存管理 5.Linux 虚拟文件系统 6.Linu ...

  3. Linux内核模块编程系列1-极简内核模块编写

    1.准备工作 使用如下命令查看自己Linux的内核版本 uname -a 结果如下: Linux VM-73-203-debian 4.9.0-6-amd64 #1 SMP Debian 4.9.88 ...

  4. linux 内核模块 编写例子,Linux内核模块实例

    一个简单的内核模块来读取 timespec 数据结构的数据. "read_kernel_time.c": #include #include #include #include s ...

  5. linux内核模块编写,Linux内核模块编程

    1 总体设计思路 Linux内核是单体式结构,相对于微内核结构而言,其运行效率高,但是系统的可维护性和可扩展性较差.为此,Linux提供了内核模块(module)机制,它不仅可以弥补单体式内核相对于微 ...

  6. linux 内核模块 编写例子,LINUX内核模块编程8

    Chapter 8. System Calls 系统调用 到目前为止,我们所做的只是使用完善的内核机制注册/proc文件和处理设备的对象.如果只是想写一个设备驱动, 这些内核程序员设定的方式已经足够了 ...

  7. 编写函数实现员工信息录入和输出_编写我的第一个Linux 内核模块“hello_module”...

    前言: Linux 内 核 模 块 全 称 为 " 动 态 可 加 载 内 核 模 块 (Loadable Kernel Module,LKM)",是系统内核向外部提供的功能插口. ...

  8. 编写int testsyscall()系统调用–响应函数

    编写一个系统调用意味着要给内核增加1个函数,将新函数放入文件kernel/sys.c中.新函数代码如下: asmlingkage sys_testsyscall() { print("hel ...

  9. Linux内核驱动学习(一)编写最简单Linux内核模块HelloWorld

    文章目录 准备工作 什么是内核模块 编写 hello.c 模块编译 相关指令 测试结果 模块加载 模块卸载 准备工作 在进行以下操作前,首先我准备了一台电脑,并且安装了虚拟机,系统是Ubuntu16. ...

最新文章

  1. 【全网之最】JavaScript中字符串以特定字符分隔开之后,获取最后一个分割出来的字符串,多用于获取文件的后缀名(格式)
  2. CVPR 2021评审出炉,得分惨不忍睹,面对奇葩评审该如何反击?
  3. 皮一皮:看看你有没有在自杀...
  4. JAG Practice Contest for ACM-ICPC Asia Regional 2016.K.Non-redundant Drive(点分治)
  5. Axure在SVN共享项目如何获取历史文件
  6. 【docker】第三节:nginx通过外部无法访问的情况解决方法。
  7. binding.BindingException: Invalid bound statement (not found): xxx → dao接口和mapper.xml映射文件绑定异常
  8. Coinbase在上市前选择在Reddit线上路演
  9. 通过SSH连接远程Jupyter Notebook
  10. Linux文件和目录的属性及权限
  11. matlab veristand,amesim Veristand matlab
  12. Springboot thymeleaf i18n国际化多语言选择
  13. 模拟电路实现延时功能
  14. 华为 QOS服务质量基础知识总结
  15. 一篇文章入门Python
  16. JAVA实现GMT转换北京时间
  17. 【超详细word排版】页眉:宋体五号,居中排列。左面页眉为论文题目,右面页眉为章次和章标题。页眉底划线的宽度为0.75磅。 页码:宋体小五号,排在页眉行的最外侧,不加任何修饰。
  18. cocos creator 加载微信云端图片
  19. 多少秒算长镜头_什么是长镜头画面(电影里的长镜头有哪些分类)
  20. 基于jsp+mysql+Spring+SpringMVC+mybatis的大学生缴费系统-计算机毕业设计

热门文章

  1. 路飞学院-Python爬虫实战密训班-第2章
  2. 如何解决机器学习中数据不平衡问题
  3. 数据库中死锁那些事儿
  4. 05-数据类型、常量、变量
  5. WINDOWS高级窗口的客户区域拖动技术及其应用
  6. jbpm 4.3 与 spring 集成
  7. mysql自定义序号_MySQL数据库之在mysql中给查询的结果添加序号列
  8. 【EventBus】Subscribe 注解分析 ( Subscribe 注解属性 | threadMode 线程模型 | POSTING | MAIN | MAIN_ORDERED | ASYNC)
  9. 【Flutter】底部导航栏实现 ( BottomNavigationBar 底部导航栏 | BottomNavigationBarItem 导航栏条目 | PageView )
  10. 【鸿蒙 HarmonyOS】创建 Java 语言 HarmonyOS 手机应用 ( 首次进入 DevEco Studio 配置环境 | 创建 Java 手机工程 | 鸿蒙工程代码目录简介 )