AskOverflow.Dev

AskOverflow.Dev Logo AskOverflow.Dev Logo

AskOverflow.Dev Navigation

  • 主页
  • 系统&网络
  • Ubuntu
  • Unix
  • DBA
  • Computer
  • Coding
  • LangChain

Mobile menu

Close
  • 主页
  • 系统&网络
    • 最新
    • 热门
    • 标签
  • Ubuntu
    • 最新
    • 热门
    • 标签
  • Unix
    • 最新
    • 标签
  • DBA
    • 最新
    • 标签
  • Computer
    • 最新
    • 标签
  • Coding
    • 最新
    • 标签
主页 / unix / 问题 / 553273
Accepted
bem22
bem22
Asked: 2019-11-21 16:32:58 +0800 CST2019-11-21 16:32:58 +0800 CST 2019-11-21 16:32:58 +0800 CST

Linux 字符设备驱动程序问题

  • 772

我正在编写 LKM 来创建字符设备驱动程序。

Linux 内核:VirtualBox 中的 4.4.0-93-generic,2GB 内存,SWAP 为 300Kb

问题 1

如果我编写一个处理 dev_write 中的fd的 C 程序,一切都很好,它应该按原样读取,但是如果我尝试使用head -n 1 < /dev/opsysmem它不会输出任何内容。

从设备读取代码:

int main()
{
    int ret, fd;
    char stringToSend[BUFFER_LENGTH];
    printf("Starting device test code example...\n");
    fd = open("/dev/opsysmem", O_RDWR); // Open the device with read/write access
    if (fd < 0)
    {
        perror("Failed to open the device...");
        return errno;
    }

    printf("Press ENTER to read back from the device...\n");
    getchar();

    printf("Reading from the device...\n");
    ret = read(fd, receive, BUFFER_LENGTH); // Read the response from the LKM
    if (ret < 0)
    {
        perror("Failed to read the message from the device.");
        return errno;
    }
    printf("The received message is: [%s]\n", receive);

    return 0;
}

问题 2

如果我反复发送足够大的消息,一切都很好,我的 2MiB 缓冲区填满,然后丢弃以下消息。但是,如果消息更小(即每个 1 个字符),它会在大约 10000 个节点后停止。这是我的链表实现的问题,一个已知的 linux 问题,还是只是我没有观察到我的代码中的某些内容?

当我遇到问题 2 时,vCPU 以正弦方式节流

这是我的读写功能:

static ssize_t dev_read(struct file *filep, char *buffer, size_t len, loff_t *offset) {
    Node *msg;
    int ret_val;
    char* message;
    unsigned int message_length;

    // Entering critical section
    down(&sem); //wait state

    msg = pop(&l, 0);

    // No message? No wait!
    if(!msg) {
        up(&sem);
        return -EAGAIN;
    }

    if(len < msg->length) {
        up(&sem);
        return -EINVAL;
    }

    // Since we have a message, let's send it!
    current_size -= message_length;

    // copy_to_user has the format ( * to, *from, size) and returns 0 on success
    ret_val = copy_to_user(buffer, msg->string, message_length);

    if (!ret_val) {
        remove_element(&l, 0);
        up(&sem);
        return ret_val;
    } else {
        up(&sem);
        return -EFAULT; // Failed
    }
}


static ssize_t dev_write(struct file *filep, const char *buffer, size_t len, loff_t *offset) {
    Node *n;

    // buffer larger than 2 * 1024 bytes
    if(len > MAX_MESSAGE_SIZE || len == 0) {
        return -EINVAL;
    }

    n = kmalloc(sizeof(Node), GFP_KERNEL);

    if(!n) { 
        return -EAGAIN;
    }

    n->string = (char*) kmalloc(len, GFP_KERNEL);
    n->length = len;

    copy_from_user(n->string, buffer, len);

    // Enter critical section
    down(&sem); //wait state
    if(SLEEP) msleep(100);

    // buffer is larger than the total list memory (2MiB)
    if(current_size + len > MAX_LIST_SIZE) {
        up(&sem);
        return -EAGAIN;
    }

    current_size += len;

    push(&l, n);

    up(&sem);
    // Exit critical section

    return len;
}

这是我的链表及其功能

typedef struct Node {
    unsigned int length;
    char* string;
    struct Node *next;
} Node;

typedef struct list{
    struct Node *node;
} list;

static void init(list * l){
    l->node = NULL;
}

static void destroyNode(Node *n) {
    if(n) {
        destroyNode(n->next);
        kfree(n->string);
        n->string = NULL;
        kfree(n);
        n = NULL;
    }
}

static void destroy(list *l){
    if(l) {
        destroyNode(l->node);
    }
}

static Node* pop(list *l, unsigned int index) {
    struct Node *_current = l->node;

    // Cut down index until reaching the desired position
    while(_current) {
        if(index) {
            _current = _current->next;
            index--;
        } else { return _current; }
    }

    // If you are here, the node does not exist
    return NULL;
}

static int push(list * l, Node *n) {
    if(!n) { return -1; }

    // Initialize the string

    // Do we have a node in the list?
    if (l->node) { 
        // Virtually add it as a head
        n->next = l->node;
    } else {
        n->next = NULL;
    } // Otherwise prepare the list to have no tail

    // Now make the list point to the head
    l->node = n;

    return 0;
}

static int remove_element(list * l, unsigned int index){
    // Get the reference for head
    struct Node *previous;
    struct Node *_current;

    previous = NULL;
    _current = (Node*) l->node;

    // Swap _current until index
    while(_current) {
        // Is the index !0 and we have more nodes?
        if(index) {
            previous = _current;
            _current = _current->next;
            index--;
        } else {
            if(previous) {
                previous->next = _current->next;
            } else {
                l->node = _current->next;
            }
            // Free memory, assign NULL pointer
            kfree(_current->string);
            _current-> string = NULL;
            kfree(_current);
            _current = NULL;

            // Return success
            return 0;
        }
    }

    // No _current? No problem!
    return -1;
}

__ 问题 2 的更新 __ 我为输入字符串尝试了不同的大小,我发现:在对设备驱动程序进行大约 650 次调用后,大小为 3.3k,消息列表大小变为 4MiB(这是最大值)。对设备进行了几次调用,然后内核冻结。

编辑 1:我根据评论更新了 te dev_write 并删除了调试代码 编辑 2:添加了更多功能:push/pop/destroy 编辑 3:我检查了缓冲区长度与消息长度

linux ubuntu
  • 3 3 个回答
  • 848 Views

3 个回答

  • Voted
  1. Best Answer
    Murray Jensen
    2019-11-22T18:41:46+08:002019-11-22T18:41:46+08:00

    我认为问题 1可能是因为head没有看到行尾字符(例如换行符,'\n'),或者它使用了 seek 系统调用,而您忽略了and函数offset中的参数(这意味着 seek 将不起作用,如果我正确理解它)...检查一下- head 确实尝试使用搜索来优化事物,但不确定它是否适用于您的情况。dev_read()dev_write()

    也不确定您关于问题 2是由时间不同步引起的答案是否正确(除非它与...有关msleep())...我的猜测是内存分配问题或竞争条件,但您没有向我们展示来源代码push(),pop()所以我们不知道。

    看起来您只是存储bufferandlen参数dev_write(),然后使用它们dev_read()传递给copy_to_user()...该缓冲区中的数据仍将在用户空间中,因此您可能正在尝试从用户空间复制到用户空间。阅读本文可能会有所帮助。

    push()您应该使用and ...的代码更新您的问题,pop()但至少push()需要为要插入列表中的链表元素和用于保存写入数据的缓冲区分配内存,然后用于copy_from_user()获取数据超出用户空间并进入内核缓冲区......然后,在完成msgin之后dev_read(),您将需要释放包含在其中的内核缓冲区msg,然后释放msg自身。

    我知道这里有很多复制,但要避免这种情况,您必须非常努力地使用虚拟内存系统和代码设计(即零复制实现)。

    还有一件小但非常重要的事情,dev_read()因为您没有检查,message_length即<= len缓冲区中有足够的空间用于消息。例如,就您的代码而言,您的驱动程序可能会尝试复制大于可用空间的消息。copy_to_user()应该抓住这一点,但话又说回来,这可能是您的问题 2的根源。

    • 2
  2. bem22
    2019-11-22T04:56:37+08:002019-11-22T04:56:37+08:00

    我解决了问题 2。发生这种情况是因为 virtualbox 与主机不同步。

    在这里找到解决方案 https://stackoverflow.com/questions/5308492/virtualbox-synchronisation-problems

    这是:

    VBoxManage guestproperty set "/VirtualBox/GuestAdd/VBoxService/--timesync-set-threshold" 1000

    • 0
  3. bem22
    2019-11-22T20:08:30+08:002019-11-22T20:08:30+08:00

    问题 1

    我搜索了我的提交历史,发现了一条提交消息,上面写着“Working test.sh”,这意味着它正在通过写入echo和读取head -n 1

    在此提交中,我的 dev_read 函数返回消息长度。

    这似乎适用于上述 C 程序的和缓冲读取head -n 1。cat

    我在这里找到了答案: https ://www.tldp.org/LDP/lkmpg/2.4/html/c577.htm

    • 0

相关问题

  • 使用键盘快捷键启动/停止 systemd 服务 [关闭]

  • 自编译 OpenVPN 不会从 systemd 启动

  • 需要一些系统调用

  • astyle 不会更改源文件格式

  • 通过标签将根文件系统传递给linux内核

Sidebar

Stats

  • 问题 205573
  • 回答 270741
  • 最佳答案 135370
  • 用户 68524
  • 热门
  • 回答
  • Marko Smith

    模块 i915 可能缺少固件 /lib/firmware/i915/*

    • 3 个回答
  • Marko Smith

    无法获取 jessie backports 存储库

    • 4 个回答
  • Marko Smith

    如何将 GPG 私钥和公钥导出到文件

    • 4 个回答
  • Marko Smith

    我们如何运行存储在变量中的命令?

    • 5 个回答
  • Marko Smith

    如何配置 systemd-resolved 和 systemd-networkd 以使用本地 DNS 服务器来解析本地域和远程 DNS 服务器来解析远程域?

    • 3 个回答
  • Marko Smith

    dist-upgrade 后 Kali Linux 中的 apt-get update 错误 [重复]

    • 2 个回答
  • Marko Smith

    如何从 systemctl 服务日志中查看最新的 x 行

    • 5 个回答
  • Marko Smith

    Nano - 跳转到文件末尾

    • 8 个回答
  • Marko Smith

    grub 错误:你需要先加载内核

    • 4 个回答
  • Marko Smith

    如何下载软件包而不是使用 apt-get 命令安装它?

    • 7 个回答
  • Martin Hope
    user12345 无法获取 jessie backports 存储库 2019-03-27 04:39:28 +0800 CST
  • Martin Hope
    Carl 为什么大多数 systemd 示例都包含 WantedBy=multi-user.target? 2019-03-15 11:49:25 +0800 CST
  • Martin Hope
    rocky 如何将 GPG 私钥和公钥导出到文件 2018-11-16 05:36:15 +0800 CST
  • Martin Hope
    Evan Carroll systemctl 状态显示:“状态:降级” 2018-06-03 18:48:17 +0800 CST
  • Martin Hope
    Tim 我们如何运行存储在变量中的命令? 2018-05-21 04:46:29 +0800 CST
  • Martin Hope
    Ankur S 为什么 /dev/null 是一个文件?为什么它的功能不作为一个简单的程序来实现? 2018-04-17 07:28:04 +0800 CST
  • Martin Hope
    user3191334 如何从 systemctl 服务日志中查看最新的 x 行 2018-02-07 00:14:16 +0800 CST
  • Martin Hope
    Marko Pacak Nano - 跳转到文件末尾 2018-02-01 01:53:03 +0800 CST
  • Martin Hope
    Kidburla 为什么真假这么大? 2018-01-26 12:14:47 +0800 CST
  • Martin Hope
    Christos Baziotis 在一个巨大的(70GB)、一行、文本文件中替换字符串 2017-12-30 06:58:33 +0800 CST

热门标签

linux bash debian shell-script text-processing ubuntu centos shell awk ssh

Explore

  • 主页
  • 问题
    • 最新
    • 热门
  • 标签
  • 帮助

Footer

AskOverflow.Dev

关于我们

  • 关于我们
  • 联系我们

Legal Stuff

  • Privacy Policy

Language

  • Pt
  • Server
  • Unix

© 2023 AskOverflow.DEV All Rights Reserve