Ubuntu18.04 添加系统调用及简单驱动

添加系统调用

本文地址: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了
启动时不停按shift
选择我们新编译的内核

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 日志级别

序号描述等级说明
1KERN_EMERG0突发性事件消息,通常在系统崩溃之前报告此类信息
2KERN_ALERT1需要立即操作的情况下使用此消息
3KERN_CRIT2临界条件,遇到严重软硬件错误时使用
4KERN_ERR3报告错误条件,设备驱动经常使用该级别报告硬件问题
5KERN_WARNING4问题警告,一般不会引起系统严重问题
6KERN_NOTICE5普通通知,许多安全性相关的情况会使用这个级别报告
7KERN_INFO6信息,驱动程序启动时获取硬件信息
8KERN_DEBUG7调试信息

6.2 打印日志配置

# 查看级别
cat /proc/sys/kernel/printk
4	4	1	7  #系统默认级别
序号级别说明
14内核打印函数printk的打印级别,只有级别比他高的信息才能在控制台打印,即0-3级别
24默认消息日志级别,该优先级打印没有优先级的消息
31最低的控制台日志级别,控制台日志级别可别设置的最小值(最高优先级)
47默认的控制台日志级别,控制台日志级别的缺省值
# 修改日志级别
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

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值