添加系统调用
本文地址:https://blog.csdn.net/xiangnengji/article/details/113738848
测试环境为: Ubuntu18.04 (VMware Workstation Pro 15)
按 ctrl + alt 弹出鼠标
//UY758-0RXEQ-M81WP-8ZM7Z-Y3HDA
//VF750-4MX5Q-488DQ-9WZE9-ZY2D6
//UU54R-FVD91-488PP-7NNGC-ZFAX6
//YC74H-FGF92-081VZ-R5QNG-P6RY4
//YC34H-6WWDK-085MQ-JYPNX-NZRA2
1 安装依赖
sudo apt-get update #更新
sudo apt-get install build-essential
sudo apt-get install libncurses5-dev
sudo apt-get install libssl-dev
sudo apt-get install libelf-dev
sudo apt-get install bison
sudo apt-get install flex
2 下载并解压内核
uname -a # 查看系统内核版本
Linux 100ask 4.18.0-15-generic #16~18.04.1-Ubuntu SMP Thu Feb 7 14:06:04 UTC 2019 x86_64 x86_64 x86_64 GNU/Linux
ls /usr/src
linux-headers-4.18.0-15 linux-headers-4.18.0-15-generic
ls /boot/ -l
total 51320
-rw-r--r-- 1 root root 217065 Feb 7 2019 config-4.18.0-15-generic #系统原本的配置文件
drwxr-xr-x 5 root root 4096 Feb 20 2019 grub # grub引导
-rw-r--r-- 1 root root 38936146 Mar 26 2019 initrd.img-4.18.0-15-generic
drwx------ 2 root root 16384 Feb 20 2019 lost+found
-rw-r--r-- 1 root root 182704 Jan 28 2016 memtest86+.bin
-rw-r--r-- 1 root root 184380 Jan 28 2016 memtest86+.elf
-rw-r--r-- 1 root root 184840 Jan 28 2016 memtest86+_multiboot.bin
-rw------- 1 root root 4268435 Feb 7 2019 System.map-4.18.0-15-generic
-rw-r--r-- 1 root root 8543992 Feb 9 2019 vmlinuz-4.18.0-15-generic #原来的内核映象,make instsll 内核后,会多出来一个
sudo apt-get install linux-source #在/usr/src下
sudo apt-get install linux-source-4.15.0 #获取批定版本内核
ls /usr/src #目录下多出来二个4.15
linux-headers-4.18.0-15 **linux-source-4.15.0**
linux-headers-4.18.0-15-generic **linux-source-4.15.0.tar.bz2**
cd /usr/src
sudo tar -jxvf linux-source-4.15.0.tar.bz2
3 修改文件添加系统调用
源码目录下涉及内核的三个文件有:
/kernel/sys.c //定义系统调用
/include/linux/syscalls.h //系统调用的头文件
/arch/x86/entry/syscalls/syscall_64.tbl //设置系统调用号,如果是32位就用syscall_32.tbl
系统调用:在/usr/src/linux-source-4.15.0/kernel/sys.c最后加入
asmlinkage int sys_mycall(int number)
{
printk("这是我添加的第一个系统调用");
return number;
}
系统调用函数申明:在/usr/src/linux-source-4.15.0/arch/x86/include/asm/vim syscalls.h后面
asmlinkage int sys_mycall(int number);
添加一个系统调用的id:/arch/x86/entry/syscalls/syscall_64.tbl
cd /usr/src/linux-source-4.15.0/arch/x86/entry/syscalls
sudo vim syscall_64.tbl
333 64 mycall sys_mycall
系统调用编号:333 <number:可以理解为系统调用的标识符>
适用位数: common <abi:x86/64/common>
名称: mycall <name:自定义名称>
函数名: sys_mycall <entry point:自己添加的系统调用的函数名>
// cd /usr/src/linux-source-4.15.0/include/uapi/asm-generic/unistd.h 中添加
#define __NR_mycall 293 // 这个在这里用不到
__SYSCALL(__NR_mycall , sys_mycall) // 这个在这里用不到
4 编译内核
cd /usr/src/linux-source-4.15.0
sudo make mrproper #清除内核中不稳定的目标文件,附属文件及内核配置文件
sudo make clean #清除以前生成的目标文件和其他文件
#分步
sudo make oldconfig #采用默认的内核配置,如果这里出现选项,选择默认的选项,就是方括号内的第一个字母,不过我这里没出现选项
sudo make bzImage #编译内核,大概需要半小时
sudo make modules #编译模块,大概需要两小时,如果出现错误,看看是不是因为上面的第3步的(make oldconfig)没做
sudo make modules_install #安装模块,比较快
#一起make
sudo make menuconfig #也可用这个命令配置,进去后不作修改直接保存退出
sudo make -j4 #多核编译(内核和模块一起编译)
sudo make modules_install #安装一些驱动和功能
sudo make install #把内核安装到系统中
# 安装完了需要重启,
sudo reboot #重启进入新内核就好
uname -a #查看内核版本
9、重启
编辑 /etc/default/grub (比如命令:baisudo gedit /etc/default/grub)
找到 hidden_timeout 数字改为du10,保存
终端执zhi行命令:sudo update-grub
sudo reboot重启 ,正常的话会有一个倒计时 那个时候按住shift 然后在菜单栏里选就ok了
uname -r
4.15.18
10、检查系统调用 test.c
#include <linux/kernel.h>
#include <sys/syscall.h>
#include <unistd.h>
#include <stdio.h>
int main(int argc,char **argv)
{
//333:asmlinkage int sys_mycall(int number);
printf("System call sys_helloworld return %ld\n",syscall(333));
return 0;
}
编译运行,然后打开终端输入
sudo gcc -o test test.c
sudo ./test
sudo dmesg --clear #清除一下信息
dmesg #查看是否有添加系统调用成功信息
sudo ./test #运行测试程序
System call sys_helloworld return 788858216
sudo ./test
System call sys_helloworld return 2239072600
驱动开发
1 简单的驱动程序 hello_drv.c
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/errno.h>
#include <linux/miscdevice.h>
#include <linux/kernel.h>
#include <linux/major.h>
#include <linux/mutex.h>
#include <linux/proc_fs.h>
#include <linux/seq_file.h>
#include <linux/stat.h>
#include <linux/init.h>
#include <linux/device.h>
#include <linux/tty.h>
#include <linux/kmod.h>
#include <linux/gfp.h>
/* 1. 确定主设备号 */
static int major = 0;
static char kernel_buf[1024];
static struct class *hello_class;
#define MIN(a, b) (a < b ? a : b)
/* 3. 实现对应的open/read/write等函数,填入file_operations结构体 */
static ssize_t hello_drv_read (struct file *file, char __user *buf, size_t size, loff_t *offset)
{
int err;
printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
err = copy_to_user(buf, kernel_buf, MIN(1024, size));
return MIN(1024, size);
}
static ssize_t hello_drv_write (struct file *file, const char __user *buf, size_t size, loff_t *offset)
{
int err;
printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
err = copy_from_user(kernel_buf, buf, MIN(1024, size));
return MIN(1024, size);
}
static int hello_drv_open (struct inode *node, struct file *file)
{
printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
return 0;
}
static int hello_drv_close (struct inode *node, struct file *file)
{
printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
return 0;
}
/* 2. 定义自己的file_operations结构体 */
static struct file_operations hello_drv = {
.owner = THIS_MODULE,
.open = hello_drv_open,
.read = hello_drv_read,
.write = hello_drv_write,
.release = hello_drv_close,
};
/* 4. 把file_operations结构体告诉内核:注册驱动程序 */
/* 5. 谁来注册驱动程序啊?得有一个入口函数:安装驱动程序时,就会去调用这个入口函数 */
static int __init hello_init(void)
{
int err;
printk(KERN_ALERT "%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
major = register_chrdev(0, "hello", &hello_drv); /* /dev/hello */
hello_class = class_create(THIS_MODULE, "hello_class");
err = PTR_ERR(hello_class);
if (IS_ERR(hello_class)) {
printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
unregister_chrdev(major, "hello");
return -1;
}
device_create(hello_class, NULL, MKDEV(major, 0), NULL, "hello"); /* /dev/hello */
return 0;
}
/* 6. 有入口函数就应该有出口函数:卸载驱动程序时,就会去调用这个出口函数 */
static void __exit hello_exit(void)
{
printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
device_destroy(hello_class, MKDEV(major, 0));
class_destroy(hello_class);
unregister_chrdev(major, "hello");
}
/* 7. 其他完善:提供设备信息,自动创建设备节点 */
module_init(hello_init);
module_exit(hello_exit);
MODULE_LICENSE("GPL");
2 简单的APP程序
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
/* hello_drv_test.c
* ./hello_drv_test -w abc
* ./hello_drv_test -r
*/
int main(int argc, char **argv)
{
int fd;
char buf[1024];
int len;
/* 1. 判断参数 */
if (argc < 2)
{
printf("Usage: %s -w <string>\n", argv[0]);
printf(" %s -r\n", argv[0]);
return -1;
}
/* 2. 打开文件 */
fd = open("/dev/hello", O_RDWR);
if (fd == -1)
{
printf("can not open file /dev/hello\n");
return -1;
}
/* 3. 写文件或读文件 */
if ((0 == strcmp(argv[1], "-w")) && (argc == 3))
{
len = strlen(argv[2]) + 1;
len = len < 1024 ? len : 1024;
write(fd, argv[2], len);
}
else
{
len = read(fd, buf, 1024);
buf[1023] = '\0';
printf("APP read : %s\n", buf);
}
close(fd);
return 0;
}
3 Makefile
KERN_DIR = /usr/src/linux-source-4.15.0
all:
make -C $(KERN_DIR) M=`pwd` modules
$(CROSS_COMPILE)gcc -o hello_drv_test hello_drv_test.c
clean:
make -C $(KERN_DIR) M=`pwd` modules clean
rm -rf modules.order
rm -f hello_drv_test
obj-m += hello_drv.o
4 编译
make
make -C /usr/src/linux-source-4.15.0 M=`pwd` modules
make[1]: Entering directory '/usr/src/linux-source-4.15.0'
CC [M] /home/book/nfs_rootfs/hello_drv.o
Building modules, stage 2.
MODPOST 1 modules
CC /home/book/nfs_rootfs/hello_drv.mod.o
LD [M] /home/book/nfs_rootfs/hello_drv.ko
make[1]: Leaving directory '/usr/src/linux-source-4.15.0'
gcc -o hello_drv_test hello_drv_test.c
5 测试
# 加载驱动
sudo insmod hello_drv.ko #转载驱动
sudo ls /dev/hello #查看有没有产生设备节点
sudo rmmod hello #卸载驱动
#应用程序测式
sudo ./hello_drv_test -w abc #运行应用程序 写数据
sudo ./hello_drv_test -r #读数据
# 终端输出
#由于ubuntu 系统有时默认没有没有打开printk输出信息,可以下面二条批令查看
sudo dmesg --clear #清除一下信息
dmesg #查看是否有添加系统调用成功信息
sudo cat /var/log/kern.log #也可以用这个命令查看
6 内核日志
6.1 日志级别
序号 | 描述 | 等级 | 说明 |
---|---|---|---|
1 | KERN_EMERG | 0 | 突发性事件消息,通常在系统崩溃之前报告此类信息 |
2 | KERN_ALERT | 1 | 需要立即操作的情况下使用此消息 |
3 | KERN_CRIT | 2 | 临界条件,遇到严重软硬件错误时使用 |
4 | KERN_ERR | 3 | 报告错误条件,设备驱动经常使用该级别报告硬件问题 |
5 | KERN_WARNING | 4 | 问题警告,一般不会引起系统严重问题 |
6 | KERN_NOTICE | 5 | 普通通知,许多安全性相关的情况会使用这个级别报告 |
7 | KERN_INFO | 6 | 信息,驱动程序启动时获取硬件信息 |
8 | KERN_DEBUG | 7 | 调试信息 |
6.2 打印日志配置
# 查看级别
cat /proc/sys/kernel/printk
4 4 1 7 #系统默认级别
序号 | 级别 | 说明 |
---|---|---|
1 | 4 | 内核打印函数printk的打印级别,只有级别比他高的信息才能在控制台打印,即0-3级别 |
2 | 4 | 默认消息日志级别,该优先级打印没有优先级的消息 |
3 | 1 | 最低的控制台日志级别,控制台日志级别可别设置的最小值(最高优先级) |
4 | 7 | 默认的控制台日志级别,控制台日志级别的缺省值 |
# 修改日志级别
sudo dmesg -n 8
echo 8 >/proc/sys/kernel/printk # 权限不够
sudo echo 8 > printk # 使用sudo依旧权限不够
sudo sh -c 'echo 8 > printk' # X86_64机器依旧不能显示printk信息
dmesg # 以上操作在ubuntu 上都不管用,dmesg最方便
7 总结
- 明确部署系统的配置,安装相应依赖及核心文件;
- 日志信息在X86平台不能终端输出,需查看日志文件;
- 调试过程注意语法格式,规范编码;
- 记录异常信息,做好笔记,方便后续查阅;
- 积累,就是练本事。
[参考文献]
[1]https://blog.csdn.net/weixin_42921195/article/details/89950973
[2]https://blog.csdn.net/wr132/article/details/73825888
[3]https://www.cnblogs.com/wmx-learn/p/5344821.html