#include <stdio.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <unistd.h> int main(int argc,char *argv[]){ int fd;//定义 int 类型文件描述符 unsigned int off;//定义读写偏移位置 char readbuf[13] = {0};//定义读取缓冲区 readbuf char readbuf1[19] = {0};//定义读取缓冲区 readbuf1 fd = open("/dev/test",O_RDWR,666);//打开/dev/test 设备 if(fd < 0 ){ printf("file open error \n"); } write(fd,"hello world ",13);//向 fd 写入数据 hello world off = lseek(fd,0,SEEK_CUR);//读取当前位置的偏移量 printf("off is %d\n",off); off = lseek(fd,0,SEEK_SET);//将偏移量设置为 0 printf("off is %d\n",off); read(fd,readbuf,sizeof(readbuf));//将写入的数据读取到 readbuf 缓冲区 printf("read is %s\n",readbuf); off = lseek(fd,0,SEEK_CUR);//读取当前位置的偏移量 printf("off is %d\n",off); off = lseek(fd,-1,SEEK_CUR);//将当前位置的偏移量向前挪动一位 printf("off is %d\n",off); write(fd,"Linux",6);//向 fd 写入数据 Linux off = lseek(fd,0,SEEK_CUR);//读取当前位置的偏移量 printf("off is %d\n",off); off = lseek(fd,0,SEEK_SET);//将偏移量设置为 0 printf("off is %d\n",off); read(fd,readbuf1,sizeof(readbuf1));//将写入的数据读取到 readbuf1 缓冲区 printf("read is %s\n",readbuf1); off = lseek(fd,0,SEEK_CUR);//读取当前位置的偏移量 printf("off is %d\n",off); close(fd); return 0; }#include <linux/module.h> #include <linux/init.h> #include <linux/fs.h> #include <linux/cdev.h> #include <linux/kdev_t.h> #include <linux/uaccess.h> #include <linux/atomic.h> #define BUFSIZE 1024 // 设置最大偏移量为1024 static char mem[BUFSIZE] = {0}; // 设置数据存储数组mem struct device_test { dev_t dev_num; // 设备号 int major; // 主设备号 int minor; // 次设备号 struct cdev cdev_test; // cdev结构体 struct class *class; // 类 struct device *device; // 设备 char kbuf[32]; }; static struct device_test dev1; /* 设备打开函数 */ static int cdev_test_open(struct inode *inode, struct file *file) { file->private_data = &dev1; // 设置私有数据 return 0; } /* 从设备读取数据 */ static ssize_t cdev_test_read(struct file *file, char __user *buf, size_t size, loff_t *off) { loff_t p = *off; // 将读取数据的偏移量赋值给loff_t类型变量p int i; size_t count = size; if (p > BUFSIZE) { // 如果当前偏移值比最大偏移量大则返回错误 return -1; } if (count > BUFSIZE - p) { count = BUFSIZE - p; // 如果要读取的偏移值超出剩余的空间,则读取到最后位置 } if (copy_to_user(buf, mem + p, count)) { // 将mem+p中的值写入buf,并传递到用户空间 printk("copy_to_user error\n"); return -1; } for (i = 0; i < 20; i++) { printk("buf[%d] is %c\n", i, mem[i]); // 将mem中的值打印出来 } printk("mem is %s, p is %llu, count is %d\n", mem + p, p, count); *off = *off + count; // 更新偏移值 return count; } /* 向设备写入数据函数 */ static ssize_t cdev_test_write(struct file *file, const char __user *buf, size_t size, loff_t *off) { loff_t p = *off; // 将写入数据的偏移量赋值给loff_t类型变量p size_t count = size; if (p > BUFSIZE) { // 如果当前偏移值比最大偏移量大则返回错误 return 0; } if (count > BUFSIZE - p) { count = BUFSIZE - p; // 如果要写入的偏移值超出剩余的空间,则写入到最后位置 } if (copy_from_user(mem + p, buf, count)) { // 将buf中的值,从用户空间传递到内核空间 printk("copy_to_user error\n"); return -1; } printk("mem is %s, p is %llu\n", mem + p, p); // 打印写入的值 *off = *off + count; // 更新偏移值 return count; } /* 设备关闭函数 */ static int cdev_test_release(struct inode *inode, struct file *file) { return 0; } /* 设备定位函数 */ static loff_t cdev_test_llseek(struct file *file, loff_t offset, int whence) { loff_t new_offset; // 定义loff_t类型的新的偏移值 switch (whence) { // 对lseek函数传递的whence参数进行判断 case SEEK_SET: if (offset < 0) { return -EINVAL; } if (offset > BUFSIZE) { return -EINVAL; } new_offset = offset; // 如果whence参数为SEEK_SET,则新偏移值为offset break; case SEEK_CUR: if (file->f_pos + offset > BUFSIZE) { return -EINVAL; } if (file->f_pos + offset < 0) { return -EINVAL; } new_offset = file->f_pos + offset; // 如果whence参数为SEEK_CUR,则新偏移值为file->f_pos + offset break; case SEEK_END: if (file->f_pos + offset < 0) { return -EINVAL; } new_offset = BUFSIZE + offset; // 如果whence参数为SEEK_END,则新偏移值为BUFSIZE + offset break; default: break; } file->f_pos = new_offset; // 更新file->f_pos偏移值 return new_offset; } /* 设备操作函数结构体 */ struct file_operations cdev_test_fops = { .owner = THIS_MODULE, // 将owner字段指向本模块,可以避免在模块的操作正在被使用时卸载该模块 .open = cdev_test_open, // 将open字段指向cdev_test_open()函数 .read = cdev_test_read, // 将read字段指向cdev_test_read()函数 .write = cdev_test_write, // 将write字段指向cdev_test_write()函数 .release = cdev_test_release, // 将release字段指向cdev_test_release()函数 .llseek = cdev_test_llseek, }; /* 驱动入口函数 */ static int __init timer_dev_init(void) { int ret; /* 1. 创建设备号 */ ret = alloc_chrdev_region(&dev1.dev_num, 0, 1, "alloc_name"); // 动态分配设备号 if (ret < 0) { goto err_chrdev; } printk("alloc_chrdev_region is ok\n"); dev1.major = MAJOR(dev1.dev_num); // 获取主设备号 dev1.minor = MINOR(dev1.dev_num); // 获取次设备号 printk("major is %d\n", dev1.major); // 打印主设备号 printk("minor is %d\n", dev1.minor); // 打印次设备号 /* 2. 初始化cdev */ dev1.cdev_test.owner = THIS_MODULE; cdev_init(&dev1.cdev_test, &cdev_test_fops); /* 3. 添加一个cdev,完成字符设备注册到内核 */ ret = cdev_add(&dev1.cdev_test, dev1.dev_num, 1); if (ret < 0) { goto err_chr_add; } /* 4. 创建类 */ dev1.class = class_create(THIS_MODULE, "test"); if (IS_ERR(dev1.class)) { ret = PTR_ERR(dev1.class); goto err_class_create; } /* 5. 创建设备 */ dev1.device = device_create(dev1.class, NULL, dev1.dev_num, NULL, "test"); if (IS_ERR(dev1.device)) { ret = PTR_ERR(dev1.device); goto err_device_create; } return 0; err_device_create: class_destroy(dev1.class); err_class_create: cdev_del(&dev1.cdev_test); // 删除cdev err_chr_add: unregister_chrdev_region(dev1.dev_num, 1); // 注销设备号 err_chrdev: return ret; } /* 驱动出口函数 */ static void __exit timer_dev_exit(void) { /* 注销字符设备 */ unregister_chrdev_region(dev1.dev_num, 1); // 注销设备号 cdev_del(&dev1.cdev_test); // 删除cdev device_destroy(dev1.class, dev1.dev_num); // 删除设备 class_destroy(dev1.class); // 删除类 } module_init(timer_dev_init); module_exit(timer_dev_exit); MODULE_LICENSE("GPL v2"); MODULE_AUTHOR("topeet");编译成X86的Makefile
# 移除ARM64架构和交叉编译器配置,默认使用x86本地编译环境 # export ARCH=arm64 # 注释掉ARM64架构设置 # export CROSS_COMPILE=aarch64-linux-gnu- # 注释掉交叉编译器前缀 obj-m += llseek.o # 驱动源文件名称,保持不变 # 修改内核目录为x86系统的本地内核源码/头文件目录 # 方案1:使用系统当前运行内核的头文件(推荐,无需手动下载内核源码) KDIR := /lib/modules/$(shell uname -r)/build # 方案2:如果你有本地下载的x86内核源码,替换成对应的路径,例如: # KDIR := /home/yourname/linux-x86-kernel # 根据实际路径修改 PWD ?= $(shell pwd) all: make -C $(KDIR) M=$(PWD) modules # 本地编译x86内核模块 clean: make -C $(KDIR) M=$(PWD) clean # 清理编译产物失败
要加上权限
dmesg