linux驅動開發
阿新 • • 發佈:2022-01-06
linux驅動簡單案例
環境
[root@hgx driver_test]# cat /etc/redhat-release
CentOS Linux release 8.2.2004 (Core)
[root@hgx driver_test]# uname -r
4.18.0-305.3.1.el8.x86_64
驅動環境搭建
核心原始碼下載 https://vault.centos.org
cat /etc/redhat-release #檢視Centos版本
uname -r #檢視核心版本
基本命令
lsmod #現實已經安裝的驅動 rmmod #解除安裝驅動 insmod #安裝驅動。insmod xxx.ko dmesg #顯示開機資訊,核心載入的資訊
實現一個簡單驅動程式程式碼
示例程式
- driver_hello.c
- Makefile
其中KDIR =/usr/src/kernels/$(shell uname -r)
KDIR 是驅動程式的核心目錄, 這是centos的核心標頭檔案目錄。
最簡單的例子
driver_hello.c
#include <linux/init.h> #include <linux/module.h> #include <linux/fs.h> #include <asm/uaccess.h> static int my_init(void) { return 0; } static void my_exit(void) { return; } module_init(my_init); module_exit(my_exit); // MODULE_xxx這種巨集作用是用來新增模組描述資訊 // 描述模組的許可證 MODULE_LICENSE("GPL"); // 描述模組的作者 MODULE_AUTHOR("aston"); // 描述模組的介紹資訊 MODULE_DESCRIPTION("module test"); // 描述模組的別名資訊 MODULE_ALIAS("alias xxx");
Makefile
CONFIG_MODULE_SIG=n
obj-m += driver_hello.o
KDIR =/usr/src/kernels/$(shell uname -r)
all:
$(MAKE) -C $(KDIR) \
SUBDIRS=$(PWD) \
modules
clean:
rm -rf *.o *.ko *.mod.* *.symvers *.order
rm -rf .tmp_versions .driver_hello.*
載入驅動
Make編譯,編譯之後會生成.order,.ko,.o等檔案,使用 insmod xxx.ko
載入驅動
同樣使用 rmmod xxx
使用者態如何呼叫驅動程式
修改driver_hello.c程式碼
實現open, write函式,實現一個簡單的核心態到使用者態的資料拷貝
driver_hello.c
#include <linux/init.h>
#include <linux/module.h>
#include <linux/fs.h>
// copy_to_user, copy_from_user 所在的函式庫
#include <linux/uaccess.h>
// printk 日誌是寫在核心日誌裡面的,使用 dmesg 檢視
static int driver_hello_open(struct inode *inode, struct file *file)
{
printk(KERN_INFO "driver_hello_open \n");
return 0;
}
static int driver_hello_release(struct inode *inode, struct file *file)
{
printk(KERN_INFO "driver_hello_release \n");
return 0;
}
char kbuf[100];
static ssize_t driver_hello_write(struct file *file, const char __user *ubuf,
size_t count, loff_t *ppos)
{
// 使用該函式將應用層傳過來的ubuf中的內容拷貝到驅動空間中的一個buf中
// memcpy(kbuf, ubuf);不行,因為2個buf不在一個地址空間中
int ret = copy_from_user(kbuf, ubuf, count);
if (ret)
{
printk(KERN_ERR "copy_from_user fail\n");
return -EINVAL;
}
printk(KERN_INFO "copy_from_user success..\n");
return count;
}
ssize_t driver_hello_read(struct file *file, char __user *ubuf,
size_t count, loff_t *ppos)
{
int ret = copy_to_user(ubuf, kbuf, count);
if(ret) {
printk(KERN_ERR "copy_from_user fail\n");
return -EINVAL;
}
printk(KERN_INFO "copy_to_user success..\n");
return count;
}
// file_operations 函式講解。https://www.cnblogs.com/chen-farsight/p/6181341.html
static const struct file_operations driver_hello_ops =
{
.owner = THIS_MODULE,
.open = driver_hello_open,
.release = driver_hello_release,
.write = driver_hello_write,
.read = driver_hello_read,
};
#define MYNAME "driver_hello"
static int mymajor;
// __init關鍵字告訴連結器將程式碼放在核心物件檔案中的專用部分中。
//本節事先對核心所知,並在模組載入和init函式完成後釋放。這僅適用於內建驅動程式,不適用於可載入模組。核心將在啟動序列期間首次執行驅動程式的init函式。
static int __init my_init(void)
{
mymajor = register_chrdev(0, MYNAME, &driver_hello_ops);
if (mymajor < 0)
{
printk(KERN_ERR "register_chrdev fail\n");
return -EINVAL;
}
printk(KERN_INFO "register_chrdev success... mymajor = %d.\n", mymajor);
return 0;
}
static void __exit my_exit(void)
{
printk(KERN_INFO "chrdev_exit helloworld exit\n");
// 在module_exit巨集呼叫的函式中去登出字元裝置驅動
unregister_chrdev(mymajor, MYNAME);
return;
}
module_init(my_init);
module_exit(my_exit);
// MODULE_xxx這種巨集作用是用來新增模組描述資訊
// 描述模組的許可證
MODULE_LICENSE("GPL");
// 描述模組的作者
// MODULE_AUTHOR("aston");
// 描述模組的介紹資訊
// MODULE_DESCRIPTION("module test");
// 描述模組的別名資訊
// MODULE_ALIAS("alias xxx");
編譯,建立驅動裝置的檔案節點
make #會根據 Makefile 檔案編譯驅動檔案
dmesg #檢視驅動裝置是否載入正確
cat /proc/devices | grep driver_hello #檢視註冊的裝置編號
mknod /dev/driver_hello c 243 0 #給驅動裝置driver_hello編號234建立檔案裝置
#mknod的裝置檔案可以使用rm -rf 刪除
rm -rf /dev/driver_hello
使用者態呼叫
測試程式碼 driver_hello_test.c
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>
#include <stdlib.h>
int main(int argc, char const *argv[])
{
int fd = open("/dev/driver_hello", O_RDWR);
printf("file fd: %d \n", fd);
if(fd < 0) {
printf("error to open file \n");
}
char *buff = "This is my frist driver for linux \n";
int count = write(fd, buff, strlen(buff) + 1);
printf("write count: %d \n", count);
char rbuf[50];
count = read(fd, &rbuf, sizeof(rbuf));
printf("read count: %d \n", count);
printf("read rbuf: %s \n", rbuf);
return 0;
}
適用 gcc編譯執行即可
核心API文件
中文文件:https://www.kernel.org/doc/html/latest/translations/zh_CN/core-api/kernel-api.html
英文文件:https://www.kernel.org/doc/html/latest/search.html?q=malloc&check_keywords=yes&area=default
問題記錄
module verification failed: signature and/or required key missing - tainting kernel
該錯誤為核心沒有簽名造成的,linux核心從3.7 開始加入模組簽名檢查機制, 校驗簽名是否與已編譯的核心公鑰匹配。目前只支援RSA X.509驗證, 模組簽名驗證並非強制使用, 可在編譯核心時配置是否開啟
事實上, linux 的kernel module 載入過程中存在諸多檢查, 模組簽名驗證只是第一步, 後面還會有 vermagic 和 CRC驗證