首页 > 解决方案 > 使用 libevent 读取 chardevice

问题描述

我编写了一个 chardevice 将从网络接收到的一些消息传递给用户空间应用程序。用户空间应用程序必须读取 chardevice 并通过 TCP 套接字向其他用户空间应用程序发送/接收消息。读取和接收都应该是阻塞的。

由于 Libevent 能够同时处理多个事件,我认为为 chardevice 创建的文件注册一个事件并为套接字注册一个事件就可以了,但我错了。但是 chardevice 会创建一个“字符特殊文件”,而 libevent 似乎无法阻止。如果我在 chardevice 内部实现了阻塞机制,即互斥锁或信号量,那么套接字事件也会阻塞,应用程序无法接收消息。

用户空间应用程序必须随时接受外部连接。

你知道如何让它工作吗?也许还使用另一个库,我只想要套接字和文件阅读器的阻塞行为。

先感谢您。

更新:感谢@Ahmed Masud 的帮助。这就是我所做的

内核模块 chardevice:
实现一个轮询函数,等待新数据可用

struct file_operations fops = {
  ...
  .read = kdev_read,
  .poll = kdev_poll,
};

如果用户空间必须停止,我有一个全局变量要处理,还有一个等待队列:

static working = 1;
static wait_queue_head_t access_wait;

这是读取函数,如果 copy_to_user 中有错误,我返回 -1,如果一切顺利,则返回 > 0,如果模块必须停止,则返回 0。used_buff 是原子的,因为它处理由用户应用程序读取并由内核模块写入的共享缓冲区的大小。

ssize_t
kdev_read(struct file* filep, char* buffer, size_t len, loff_t* offset)
{
  int error_count;

  if (signal_pending(current) || !working) { // user called sigint
    return 0;
  }

  atomic_dec(&used_buf);
  size_t llen = sizeof(struct user_msg) + msg_buf[first_buf]->size;
  error_count = copy_to_user(buffer, (char*)msg_buf[first_buf], llen);

  if (error_count != 0) {
    atomic_inc(&used_buf);
    paxerr("send fewer characters to the user");
    return error_count;
  } else
    first_buf = (first_buf + 1) % BUFFER_SIZE;

  return llen;
}

当有数据要读取时,我只需递增used_buf并调用wake_up_interruptible(&access_wait). 这是 poll 函数,我只是等到 used_buff > 0

unsigned int
kdev_poll(struct file* file, poll_table* wait)
{
  poll_wait(file, &access_wait, wait);
  if (atomic_read(&used_buf) > 0)
    return POLLIN | POLLRDNORM;
  return 0;
}

现在,这里的问题是,如果我在用户空间应用程序等待时卸载模块,后者将进入阻塞状态并且无法停止它。这就是为什么我在卸载模块时唤醒应用程序

void
kdevchar_exit(void)
{

  working = 0;
  atomic_inc(&used_buf); // increase buffer size to application is unlocked
  wake_up_interruptible(&access_wait); // wake up application, but this time read will return 0 since working = 0;
  ... // unregister everything
}

用户空间应用程序
Libevent 默认使用轮询,因此只需创建一个 event_base 和一个 reader 事件。

base = event_base_new();
filep = open(fname, O_RDWR | O_NONBLOCK, 0);
evread = event_new(base, filep, EV_READ | EV_PERSIST,
                                on_read_file, base);

其中 on_read_file 只是读取文件,不进行轮询调用(libevent 处理):

static void
on_read_file(evutil_socket_t fd, short event, void* arg)
{
  struct event_base* base = arg;

  int len = read(...);
  if (len < 0) 
    return;

  if (len == 0) {
    printf("Stopped by kernel module\n");
    event_base_loopbreak(base);
    return;
  }
  ... // handle message
}

标签: clinux-kernelkernel-moduleblockinglibevent

解决方案


推荐阅读