13. 5.12. Exchanging Data Between Kernel and User Spaces
13.1. Введение
Одна из важнейших задач при написании драйверов Linux — это организация безопасного обмена данными между пространствами ядра (kernel space) и пользователя (user space). Такой обмен необходим, например, для чтения состояния кнопки или установки уровня на GPIO.
В ядре предоставляется несколько стандартных интерфейсов:
read() и write() — классический POSIX-способ взаимодействия
ioctl() — для управления и передачи структур
poll() и select() — для реактивного ожидания событий
mmap() — отображение памяти между user и kernel
sysfs / procfs — файловые интерфейсы без open()
—
13.2. Классический пример: read()
Рассмотрим простую реализацию read() в нашем драйвере hellokeys_rpi3_misc_dt:
static ssize_t hellokeys_read(struct file *file, char __user *buf, size_t count, loff_t *ppos) {
char msg[64];
int len;
// Пример: опрос двух GPIO
int val1 = gpiod_get_value(key1);
int val2 = gpiod_get_value(key2);
len = snprintf(msg, sizeof(msg), "key1: %d\\nkey2: %d\\n", val1, val2);
return simple_read_from_buffer(buf, count, ppos, msg, len);
}
Пояснение:
char __user *buf — указатель на буфер в user space
мы создаём строку в kernel space, затем копируем её через simple_read_from_buffer() или copy_to_user()
—
13.3. Передача данных из userspace: write()
Если вы хотите передать данные в ядро (например, установить цвет RGB), реализуется write():
static ssize_t rgb_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos) {
char kbuf[32];
int r, g, b;
if (count > sizeof(kbuf) - 1) return -EINVAL;
if (copy_from_user(kbuf, buf, count)) return -EFAULT;
kbuf[count] = 0;
// Пример: "255 0 128"
if (sscanf(kbuf, "%d %d %d", &r, &g, &b) == 3) {
gpiod_set_value(led_r, r > 0);
gpiod_set_value(led_g, g > 0);
gpiod_set_value(led_b, b > 0);
return count;
}
return -EINVAL;
}
—
13.4. Что важно помнить при обмене:
User Space → Kernel Space: используем copy_from_user()
Kernel Space → User Space: используем copy_to_user() или simple_read_from_buffer()
Нельзя напрямую разыменовывать указатели из user space
Следите за безопасностью буфера: проверяйте count, добавляйте 0 в строки
—
13.5. Когда использовать ioctl()
ioctl() подходит для:
передачи флагов, параметров, структур
конфигурирования устройств
static long my_ioctl(struct file *file, unsigned int cmd, unsigned long arg) {
int kval;
if (copy_from_user(&kval, (int __user *)arg, sizeof(int)))
return -EFAULT;
if (kval == 42) do_something_special();
return 0;
}
—
13.6. Вывод
Обмен данными между ядром и пользователем — основа интерактивных драйверов. Для простых задач (GPIO, флаги, сообщения) достаточно read() / write(). Для более сложных — используем ioctl() или mmap().
Реализация этих функций позволяет из user space обращаться к устройству через обычные системные вызовы: cat, echo, open(), read(), write().