- c - 在位数组中找到第一个零
- linux - Unix 显示有关匹配两种模式之一的文件的信息
- 正则表达式替换多个文件
- linux - 隐藏来自 xtrace 的命令
我正在尝试编写一个简单的字符设备/LKM 来读取、写入和查找。我在这方面遇到了很多问题,但已经进行了数周的处理/故障排除,但一直无法使其正常工作。目前,我的模块正常运行并正确安装和卸载,但如果我尝试回显设备驱动程序文件,终端就会崩溃,当我尝试使用 cat 从它读取时,它会返回 killed。
本模块的步骤:
首先,我通过运行 make -C/lib/modules/$(uname -r)/build M=$PWD modules 创建模块
对于我的内核,uname -r 是 4.10.17newkernel
我使用 sudo insmod simple_char_driver.ko 安装模块
如果我运行 lsmod,则会列出该模块
如果我运行 dmesg,我的初始化函数“This device is now open”中的 KERN_ALERT 会正确触发。
此外,如果我运行 sudo rmmod,该功能“此设备现已关闭”KERN_ALERT 也会正确触发。
该模块也正确显示在 cat/proc/devices 中
我使用 sudo mknod -m 777/dev/simple_char_driver c 240 0 在/dev 中创建了设备驱动程序文件
在制作这个文件之前,我确保 240 主号码尚未被使用。
我的设备驱动c文件有如下代码:
#include<linux/init.h>
#include<linux/module.h>
#include<linux/fs.h>
#include<linux/slab.h>
#include<asm/uaccess.h>
#define BUFFER_SIZE 1024
MODULE_LICENSE("GPL");
//minor nunmber 0;
static int place_in_buffer = 0;
static int end_of_buffer = 1024;
static int MAJOR_NUMBER = 240;
char* DEVICE_NAME = "simple_char_driver";
typedef struct{
char* buf;
}buffer;
char *device_buffer;
static int closeCounter=0;
static int openCounter=0;
ssize_t simple_char_driver_read (struct file *pfile, char __user *buffer, size_t length, loff_t *offset){
int bytesRead = 0;
if (*offset >=BUFFER_SIZE){
bytesRead = 0;
}
if (*offset + length > BUFFER_SIZE){
length = BUFFER_SIZE - *offset;
}
printk(KERN_INFO "Reading from device\n");
if (copy_to_user(buffer, device_buffer + *offset, length) != 0){
return -EFAULT;
}
copy_to_user(buffer, device_buffer + *offset, length);
*offset += length;
printk(KERN_ALERT "Read: %s", buffer);
printk(KERN_ALERT "%d bytes read\n", bytesRead);
return 0;
}
ssize_t simple_char_driver_write (struct file *pfile, const char __user *buffer, size_t length, loff_t *offset){
int nb_bytes_to_copy;
if (BUFFER_SIZE - 1 -*offset <= length)
{
nb_bytes_to_copy= BUFFER_SIZE - 1 -*offset;
printk("BUFFER_SIZE - 1 -*offset <= length");
}
else if (BUFFER_SIZE - 1 - *offset > length)
{
nb_bytes_to_copy = length;
printk("BUFFER_SIZE - 1 -*offset > length");
}
printk(KERN_INFO "Writing to device\n");
if (*offset + length > BUFFER_SIZE)
{
printk("sorry, can't do that. ");
return -1;
}
printk("about to copy from device");
copy_from_user(device_buffer + *offset, buffer, nb_bytes_to_copy);
device_buffer[*offset + nb_bytes_to_copy] = '\0';
*offset += nb_bytes_to_copy;
return nb_bytes_to_copy;
}
int simple_char_driver_open (struct inode *pinode, struct file *pfile)
{
printk(KERN_ALERT"This device is now open");
openCounter++;
printk(KERN_ALERT "This device has been opened this many times: %d\n", openCounter);
return 0;
}
int simple_char_driver_close (struct inode *pinode, struct file *pfile)
{
printk(KERN_ALERT"This device is now closed");
closeCounter++;
printk(KERN_ALERT "This device has been closed this many times: %d\n", closeCounter);
return 0;
}
loff_t simple_char_driver_seek (struct file *pfile, loff_t offset, int whence)
{
printk(KERN_ALERT"We are now seeking!");
switch(whence){
case 0:{
if(offset<= end_of_buffer && offset >0){
place_in_buffer = offset;
printk(KERN_ALERT" this is where we are in the buffer: %d\n", place_in_buffer);
}
else{
printk(KERN_ALERT"ERROR you are attempting to go ouside the Buffer");
}
break;//THIS IS SEEK_SET
}
case 1:{
if(((place_in_buffer+offset)<= end_of_buffer)&&((place_in_buffer+offset)>0)){
place_in_buffer = place_in_buffer+offset;
printk(KERN_ALERT" this is where we are in the buffer: %d\n", place_in_buffer);
}
else{
printk(KERN_ALERT"ERROR you are attempting to go ouside the Buffer");
}
break;
}
case 2:{//THIS IS SEEK END
if((end_of_buffer-offset)>=0&& offset>0){
place_in_buffer = end_of_buffer-offset;
printk(KERN_ALERT" this is where we are in the buffer: %d\n", place_in_buffer);
}
else{
printk(KERN_ALERT"ERROR you are attempting to go ouside the Buffer");
}
break;
}
default:{
}
}
printk(KERN_ALERT"I sought %d\n", whence);
return place_in_buffer;
}
struct file_operations simple_char_driver_file_operations = {
.owner = THIS_MODULE,
.read = simple_char_driver_read,
.write = simple_char_driver_write,
.open = simple_char_driver_open,
.llseek = &simple_char_driver_seek,
.release = simple_char_driver_close,
};
static int simple_char_driver_init(void)
{
printk(KERN_ALERT "inside %s function\n",__FUNCTION__);
register_chrdev(MAJOR_NUMBER,DEVICE_NAME, &simple_char_driver_file_operations);
device_buffer = kmalloc(BUFFER_SIZE, GFP_KERNEL);
return 0;
}
static void simple_char_driver_exit(void)
{
printk(KERN_ALERT "inside %s function\n",__FUNCTION__);
unregister_chrdev(MAJOR_NUMBER, DEVICE_NAME);
kfree(device_buffer);
}
module_init(simple_char_driver_init);
module_exit(simple_char_driver_exit);
正如我之前所说,此文件生成正确,没有错误或警告。但是,目前如果我尝试回显到设备文件
使用:echo "hello world">>/dev/simple_char_driver
我正在使用的终端崩溃了
如果我然后重新打开一个终端,并使用:cat/dev/simple_char_driver
然后终端返回被杀死。
我完全不知道出了什么问题,而且我一直在寻找解决方案很长时间都没有成功。如果有人知道出了什么问题,请告诉我。
编辑:正如下面的一位用户所建议的,我从读写方法中删除了除 printk 和 return 之外的所有代码,以确保函数被触发。然后当我使用 echo 时,dmesg 显示 write printk 被触发,并且设备(我已经打开)关闭了。当我随后尝试 cat 设备文件时,dmesg 显示设备重新打开,“设备就绪”printk 成功出现,然后设备再次关闭。然而,echo 实际上并没有从设备文件中找到任何可读取的内容,尽管我之前已经将“Hello world”回显到其中。
编辑
最终运行的读写函数如下:
ssize_t simple_char_driver_read (struct file *pfile, char __user *buffer, size_t length, loff_t *offset)
{
if (*offset > BUFFER_SIZE)
{
printk("offset is greater than buffer size");
return 0;
}
if (*offset + length > BUFFER_SIZE)
{
length = BUFFER_SIZE - *offset;
}
if (copy_to_user(buffer, device_buffer + *offset, length) != 0)
{
return -EFAULT;
}
*offset += length;
return length;
}
ssize_t simple_char_driver_write (struct file *pfile, const char __user *buffer, size_t length, loff_t *offset){
/* *buffer is the userspace buffer where you are writing the data you want to be written in the device file*/
/* length is the length of the userspace buffer*/
/* current position of the opened file*/
/* copy_from_user function: destination is device_buffer and source is the userspace buffer *buffer */
int nb_bytes_to_copy;
if (BUFFER_SIZE - 1 -*offset <= length)
{
nb_bytes_to_copy= BUFFER_SIZE - 1 -*offset;
printk("BUFFER_SIZE - 1 -*offset <= length");
}
else if (BUFFER_SIZE - 1 - *offset > length)
{
nb_bytes_to_copy = length;
printk("BUFFER_SIZE - 1 -*offset > length");
}
printk(KERN_INFO "Writing to device\n");
if (*offset + length > BUFFER_SIZE)
{
printk("sorry, can't do that. ");
return -1;
}
printk("about to copy from device");
copy_from_user(device_buffer + *offset, buffer, nb_bytes_to_copy);
device_buffer[*offset + nb_bytes_to_copy] = '\0';
*offset += nb_bytes_to_copy;
return nb_bytes_to_copy;
}
最佳答案
一般来说,您的代码还有很多不足之处,但我现在能看到的是您的 .write
实现可能是可疑的。有两个可能的错误——缺少缓冲区边界检查和忽略空终止,这可能导致 strlen()
的未定义行为。 .
首先,您知道缓冲区的大小 - BUFFER_SIZE
.因此,您应该检查 *offset + length < BUFFER_SIZE
.应该是<
而不是 <=
因为无论如何,最后一个字节应保留用于空终止。因此,如果没有可用空间(else
分支或 >=
),这样的检查应使方法立即返回。我不能确定你是否应该返回 0
报告未写入任何内容或使用负值返回错误代码,例如-ENOBUFS
或 -ENOSPC
.总之,该方法的返回值为ssize_t
这意味着负值可能被返回。
其次,如果您的第一次检查成功,您的方法将计算可用于写入的实际空间。即,您可以使用 MIN(A, B)
宏来做到这一点。换句话说,你最好创建一个变量,比如说 nb_bytes_to_copy
并将其初始化为 nb_bytes_to_copy = MIN(BUFFER_SIZE - 1 - *offset, length)
以便您以后可以在 copy_from_user()
中使用它称呼。例如,如果用户请求写入 5
从 1021
的偏移量开始的数据字节字节,那么你的驱动程序将只允许写入 2
数据字节 - 例如 he
而不是 hello
.此外,返回值应设置为 nb_bytes_to_copy
以便调用者能够检测到缓冲区空间不足。
最后,不要忘记空终止。一旦你完成了
copy_from_user(device_buffer + *offset, buffer, nb_bytes_to_copy);
你应该注意做类似的事情
device_buffer[*offset + nb_bytes_copy] = '\0';
或者,如果我没记错的话,你可以使用像strncopy_from_user()
这样的特殊函数。以确保使用隐式空终止复制数据。
此外,尽管空终止写入不会导致后续 strlen()
出现问题,我怀疑你是否需要它。你可以简单地做 *offset += nb_bytes_to_copy
.
顺便说一句,我建议以更具描述性的方式命名参数/变量。 *offset
很碍眼。如果命名为*offsetp
会更好看.如果您的方法变得庞大,普通读者将不太可能记住 offset
。是指针而不是值。 offsetp
其中 p
代表“指针”将减轻将来支持您的代码的任何人的工作。
放在一起,我怀疑你的.write
实现并建议您返工。如果其他一些错误仍然存在,您将需要进一步调试它们。添加调试打印输出可能会派上用场,但请先重新审视基本要点,例如空终止和缓冲区边界保护。为了使我的回答对您更有用,我提供了指向 section 3.7 的链接。 “Linux Device Drivers 3”一书的一部分,它将阐明所讨论的主题。
关于linux - 构建一个简单的字符设备,但设备驱动程序文件不会写入或读取,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/47005279/
我是 C 语言新手,我编写了这个 C 程序,让用户输入一年中的某一天,作为返回,程序将输出月份以及该月的哪一天。该程序运行良好,但我现在想简化该程序。我知道我需要一个循环,但我不知道如何去做。这是程序
我一直在努力找出我的代码有什么问题。这个想法是创建一个小的画图程序,并有红色、绿色、蓝色和清除按钮。我有我能想到的一切让它工作,但无法弄清楚代码有什么问题。程序打开,然后立即关闭。 import ja
我想安装screen,但是接下来我应该做什么? $ brew search screen imgur-screenshot screen
我有一个在服务器端工作的 UDP 套接字应用程序。为了测试服务器端,我编写了一个简单的 python 客户端程序,它发送消息“hello world how are you”。服务器随后应接收消息,将
我有一个 shell 脚本,它运行一个 Python 程序来预处理一些数据,然后运行一个 R 程序来执行一些长时间运行的任务。我正在学习使用 Docker 并且我一直在运行 FROM r-base:l
在 Linux 中。我有一个 c 程序,它读取一个 2048 字节的文本文件作为输入。我想从 Python 脚本启动 c 程序。我希望 Python 脚本将文本字符串作为参数传递给 c 程序,而不是将
前言 最近开始整理笔记里的库存草稿,本文是 23 年 5 月创建的了(因为中途转移到 onedrive,可能还不止) 网页调起电脑程序是经常用到的场景,比如百度网盘下载,加入 QQ 群之类的 我
对于一个类,我被要求编写一个 VHDL 程序,该程序接受两个整数输入 A 和 B,并用 A+B 替换 A,用 A-B 替换 B。我编写了以下程序和测试平台。它完成了实现和行为语法检查,但它不会模拟。尽
module Algorithm where import System.Random import Data.Maybe import Data.List type Atom = String ty
我想找到两个以上数字的最小公倍数 求给定N个数的最小公倍数的C++程序 最佳答案 int lcm(int a, int b) { return (a/gcd(a,b))*b; } 对于gcd,请查看
这个程序有错误。谁能解决这个问题? Error is :TempRecord already defines a member called 'this' with the same paramete
当我运行下面的程序时,我在 str1 和 str2 中得到了垃圾值。所以 #include #include #include using namespace std; int main() {
这是我的作业: 一对刚出生的兔子(一公一母)被放在田里。兔子在一个月大时可以交配,因此在第二个月的月底,每对兔子都会生出两对新兔子,然后死去。 注:在第0个月,有0对兔子。第 1 个月,有 1 对兔子
我编写了一个程序,通过对字母使用 switch 命令将十进制字符串转换为十六进制,但是如果我使用 char,该程序无法正常工作!没有 switch 我无法处理 9 以上的数字。我希望你能理解我,因为我
我是 C++ 新手(虽然我有一些 C 语言经验)和 MySQL,我正在尝试制作一个从 MySQL 读取数据库的程序,我一直在关注这个 tutorial但当我尝试“构建”解决方案时出现错误。 (我正在使
仍然是一个初学者,只是尝试使用 swift 中的一些基本函数。 有人能告诉我这段代码有什么问题吗? import UIKit var guessInt: Int var randomNum = arc
我正在用 C++11 编写一个函数,它采用 constant1 + constant2 形式的表达式并将它们折叠起来。 constant1 和 constant2 存储在 std::string 中,
我用 C++ 编写了这段代码,使用运算符重载对 2 个矩阵进行加法和乘法运算。当我执行代码时,它会在第 57 行和第 59 行产生错误,非法结构操作(两行都出现相同的错误)。请解释我的错误。提前致谢:
我是 C++ 的初学者,我想编写一个简单的程序来交换字符串中的两个字符。 例如;我们输入这个字符串:“EXAMPLE”,我们给它交换这两个字符:“E”和“A”,输出应该类似于“AXEMPLA”。 我在
我需要以下代码的帮助: 声明 3 个 double 类型变量,每个代表三角形的三个边中的一个。 提示用户为第一面输入一个值,然后 将用户的输入设置为您创建的代表三角形第一条边的变量。 将最后 2 个步
我是一名优秀的程序员,十分优秀!