我如何从 Execute/Call 内核 space 模块中 Execute/Call 用户 space 定义的函数?
How can I Execute/Call a user-space defined function from Linux kernel space module?
我正在开发一个 Linux 模块,我想在内核模式下将其用于 运行 我的 C 程序 .
我的问题在这里,在模块的函数 read()
中,我需要使用一个名为 eval_keycode()
的函数,该函数在我的用户 space 程序中定义。
当我尝试编译我的模块时,出现此错误:
error: implicit declaration of function ‘eval_keycode’
这证实了我的上述问题。
这是我模块的 read()
功能:
ssize_t exer_read(struct file *pfile, char __user *buffer, size_t length, loff_t *offset) {
struct file *f = pfile->private_data;
enum { MAX_BUF_SIZE = 4096 };
size_t buf_size = 0;
char *buf = NULL;
ssize_t total = 0;
ssize_t rc = 0;
struct input_event *ev;
int yalv;
/* Allocate temporary buffer. */
if (length) {
buf_size = min_t(size_t, MAX_BUF_SIZE, length);
ev = kmalloc(buf_size, GFP_KERNEL);
if (ev == NULL) {
return -ENOMEM;
}
}
/* Read file to buffer in chunks. */
do {
size_t amount = min_t(size_t, length, buf_size);
rc = kernel_read(f, ev, amount, offset);
if (rc > 0) {
/* Have read some data from file. */
if (copy_to_user(buffer, ev, rc) != 0) {
/* Bad user memory! */
rc = -EFAULT;
} else {
/* Update totals. */
total += rc;
buffer += rc;
*offset += rc;
length -= rc;
for (yalv = 0; yalv < (int) (rc / sizeof(struct input_event)); yalv++) {
if (ev[yalv].type == EV_KEY) {
if (ev[yalv].value == 0)
eval_keycode(ev[yalv].code);
}
}
if (rc < amount) {
/* Didn't read the full amount, so terminate early. */
rc = 0;
}
}
}
}
while (rc > 0 && length > 0);
/* Free temporary buffer. */
kfree(buf);
if (total > 0) {
return total;
}
return rc;
}
这是我的用户 space eval_keycode()
定义的函数:
void eval_keycode(int code)
{
static int red_state = 0;
static int green_state = 0;
switch (code) {
case 260:
printf("BTN left pressed\n");
/* figure out red state */
red_state = red_state ? 0 : 1;
change_led_state(LED_PATH "/" red "/brightness", red_state);
break;
case BTN_RIGHT:
printf("BTN right pressed\n");
/* figure out green state */
green_state = green_state ? 0 : 1;
change_led_state(LED_PATH "/" green "/brightness", green_state);
break;
}
}
如何从用户 space 调用 eval_keycode
函数来解决这个问题?
谢谢。
"call" 用户 space "function".
没有传统的(以图书馆的工作方式)方式
您的用户 space 代码应该 运行 在它自己的进程(或另一个用户 space 进程)中,您将在其中实现通信(通过共享内存、进程间调用[IPC]、设备文件、中断..)你处理数据交换的地方,并对数据采取行动(例如调用你的 eval_keycode 函数)。
你可以,但这是一个非常糟糕的主意。您需要建立一个指向您的用户模式函数的指针,在您调用它时将包含该函数的进程安排为 运行 (在内核中)。这是一项繁重的工作,并且由于它造成的安全漏洞,从根本上说它是恶意软件。此外,在 spectre et al 之后疯狂冲刺锁上现在空荡荡的谷仓的门,新的 hackery 层被部署在更新的 CPU 中,使这变得更加困难。
另一种方法:
在你原来的查询中,你是运行这个driver作为一个"tee";也就是说,您获取从设备接收到的输入,将副本提供给调用者,然后对每个输入调用 eval_keycode。 Eval_keycode 不修改数据,内核模块随后将其丢弃。所以 Eval_keycode 并不真的需要是一个函数;或者更确切地说,可能有一个用户函数:
void ProcessEvents(int fd) {
struct input_event ev;
while (read(fd, &ev, sizeof ev) == sizeof ev) {
eval_keycode(&ev);
}
}
如果您可以安排将所有事件输入该 fd。使用此设置,您的问题变得比内核更新更麻烦。用户创建一个 pipe/socket/fifo/... 并将写入端传递给您的内核模块(是的,更多的 ioctl()s)。然后,您的内核模块可以小心地使用 kernel_write()(如果您还停留在过去,则可以使用 vfs_write)使这些事件对用户处理程序可用。它要小心它的阻塞点在哪里。
您可以将其扩展为转换;这就是您的 driver 通过用户模式处理程序转换事件的地方;但到那时,您可能真的会认为 FUSE 是更好的解决方案。
您基本上是想上门拜访。你可以找到一些关于 here, but it doesn't seem 的解释,比如 Linux 有一个官方的 upcall API.
但是,正如其他人已经提到的,这不是很好的设计。 Upcalls 对在内核中实现的服务器很有用。
如果您的 exer_read()
只为您自己的代码调用(在您为其实现驱动程序的文件上),那么 inotify 可能是更好的设计。
如果您的 exer_read()
可以为任何文件调用(例如,您希望在机器上写入任何文件以更改 LED 状态),那么您希望包含 eval_keycode()
的用户空间进程轮询一些字符设备,并且您希望模块将代码写入此字符设备而不是调用 eval_keycode()
.
但是,如果 change_led_state()
是同步的,并且您实际上需要阻塞读取直到 returns,那么建议您重新考虑您的设计...但这是一个有效的用例电话。
我正在开发一个 Linux 模块,我想在内核模式下将其用于 运行 我的 C 程序 .
我的问题在这里,在模块的函数 read()
中,我需要使用一个名为 eval_keycode()
的函数,该函数在我的用户 space 程序中定义。
当我尝试编译我的模块时,出现此错误:
error: implicit declaration of function ‘eval_keycode’
这证实了我的上述问题。
这是我模块的 read()
功能:
ssize_t exer_read(struct file *pfile, char __user *buffer, size_t length, loff_t *offset) {
struct file *f = pfile->private_data;
enum { MAX_BUF_SIZE = 4096 };
size_t buf_size = 0;
char *buf = NULL;
ssize_t total = 0;
ssize_t rc = 0;
struct input_event *ev;
int yalv;
/* Allocate temporary buffer. */
if (length) {
buf_size = min_t(size_t, MAX_BUF_SIZE, length);
ev = kmalloc(buf_size, GFP_KERNEL);
if (ev == NULL) {
return -ENOMEM;
}
}
/* Read file to buffer in chunks. */
do {
size_t amount = min_t(size_t, length, buf_size);
rc = kernel_read(f, ev, amount, offset);
if (rc > 0) {
/* Have read some data from file. */
if (copy_to_user(buffer, ev, rc) != 0) {
/* Bad user memory! */
rc = -EFAULT;
} else {
/* Update totals. */
total += rc;
buffer += rc;
*offset += rc;
length -= rc;
for (yalv = 0; yalv < (int) (rc / sizeof(struct input_event)); yalv++) {
if (ev[yalv].type == EV_KEY) {
if (ev[yalv].value == 0)
eval_keycode(ev[yalv].code);
}
}
if (rc < amount) {
/* Didn't read the full amount, so terminate early. */
rc = 0;
}
}
}
}
while (rc > 0 && length > 0);
/* Free temporary buffer. */
kfree(buf);
if (total > 0) {
return total;
}
return rc;
}
这是我的用户 space eval_keycode()
定义的函数:
void eval_keycode(int code)
{
static int red_state = 0;
static int green_state = 0;
switch (code) {
case 260:
printf("BTN left pressed\n");
/* figure out red state */
red_state = red_state ? 0 : 1;
change_led_state(LED_PATH "/" red "/brightness", red_state);
break;
case BTN_RIGHT:
printf("BTN right pressed\n");
/* figure out green state */
green_state = green_state ? 0 : 1;
change_led_state(LED_PATH "/" green "/brightness", green_state);
break;
}
}
如何从用户 space 调用 eval_keycode
函数来解决这个问题?
谢谢。
"call" 用户 space "function".
没有传统的(以图书馆的工作方式)方式您的用户 space 代码应该 运行 在它自己的进程(或另一个用户 space 进程)中,您将在其中实现通信(通过共享内存、进程间调用[IPC]、设备文件、中断..)你处理数据交换的地方,并对数据采取行动(例如调用你的 eval_keycode 函数)。
你可以,但这是一个非常糟糕的主意。您需要建立一个指向您的用户模式函数的指针,在您调用它时将包含该函数的进程安排为 运行 (在内核中)。这是一项繁重的工作,并且由于它造成的安全漏洞,从根本上说它是恶意软件。此外,在 spectre et al 之后疯狂冲刺锁上现在空荡荡的谷仓的门,新的 hackery 层被部署在更新的 CPU 中,使这变得更加困难。
另一种方法:
在你原来的查询中,你是运行这个driver作为一个"tee";也就是说,您获取从设备接收到的输入,将副本提供给调用者,然后对每个输入调用 eval_keycode。 Eval_keycode 不修改数据,内核模块随后将其丢弃。所以 Eval_keycode 并不真的需要是一个函数;或者更确切地说,可能有一个用户函数:
void ProcessEvents(int fd) {
struct input_event ev;
while (read(fd, &ev, sizeof ev) == sizeof ev) {
eval_keycode(&ev);
}
}
如果您可以安排将所有事件输入该 fd。使用此设置,您的问题变得比内核更新更麻烦。用户创建一个 pipe/socket/fifo/... 并将写入端传递给您的内核模块(是的,更多的 ioctl()s)。然后,您的内核模块可以小心地使用 kernel_write()(如果您还停留在过去,则可以使用 vfs_write)使这些事件对用户处理程序可用。它要小心它的阻塞点在哪里。
您可以将其扩展为转换;这就是您的 driver 通过用户模式处理程序转换事件的地方;但到那时,您可能真的会认为 FUSE 是更好的解决方案。
您基本上是想上门拜访。你可以找到一些关于 here, but it doesn't seem 的解释,比如 Linux 有一个官方的 upcall API.
但是,正如其他人已经提到的,这不是很好的设计。 Upcalls 对在内核中实现的服务器很有用。
如果您的 exer_read()
只为您自己的代码调用(在您为其实现驱动程序的文件上),那么 inotify 可能是更好的设计。
如果您的 exer_read()
可以为任何文件调用(例如,您希望在机器上写入任何文件以更改 LED 状态),那么您希望包含 eval_keycode()
的用户空间进程轮询一些字符设备,并且您希望模块将代码写入此字符设备而不是调用 eval_keycode()
.
但是,如果 change_led_state()
是同步的,并且您实际上需要阻塞读取直到 returns,那么建议您重新考虑您的设计...但这是一个有效的用例电话。