Як працюють пристрої символів або спеціальні файли символів?


22

Я намагаюся зрозуміти спеціальні файли символів. З вікіпедії я розумію, що ці файли "забезпечують інтерфейс" для пристроїв, які передають дані по одному символу одночасно. Я розумію, що система якось викликає символьний пристрій замість того, щоб безпосередньо викликати драйвер пристрою. Але як файл надає цей інтерфейс? Це виконавчий файл, який переводить системний виклик? Може хтось пояснить, що там.

Відповіді:


19

Вони насправді є саме цим - інтерфейсами. Зашифровані "основним" та "другорядним" числом, вони забезпечують гачок ядра.

Вони випускаються у двох варіантах (ну, три, але названі труби поки що не входять до цього пояснення): Характеристичні пристрої та Блокові пристрої.

Блокові пристрої, як правило, є пристроями зберігання даних, здатними буферизувати вихід і зберігати дані для подальшого пошуку.

Пристрої символів - це речі, такі як аудіо- чи графічні карти, або пристрої введення, такі як клавіатура та миша.

У кожному випадку, коли ядро ​​завантажує правильний драйвер (або під час завантаження, або через такі програми, як udev ), воно сканує різні шини, щоб побачити, чи які пристрої, якими обробляється цей драйвер, насправді є у системі. Якщо так, він налаштовує пристрій, який "слухає" відповідну основну / другорядну кількість.

(Наприклад, цифровий процесор сигналу першої аудіокарти, знайденої вашою системою, отримує основну / другорядну числову пару 14/3; друга отримує 14,35 тощо).

Це зробити udev, щоб створити запис у /devназваному dspяк символьному пристрої, позначеному основним 14 другорядним 3.

(У значно старіших версіях Linux або мінімальної площі, /dev/можливо , вони не завантажуються динамічно, а містять статично всі можливі файли пристроїв.)

Потім, коли програма простору користувача намагається отримати доступ до файлу, який позначений як "спеціальний спеціальний файл" з відповідним основним / другорядним номером (наприклад, ваш аудіоплеєр, який намагається надіслати цифрове аудіо /dev/dsp), ядро ​​знає, що ці дані потребують передавати через драйвер, до якого додано основний / другорядний номер; імовірно, сказаний водій знає, що з цим робити по черзі.


1
1. Тож основні / другорядні числа є аналогами портів?
bernie2436

2. Отже, коли програми отримують доступ до будь-якого файлу, ядро ​​зчитує ці спеціальні інтерфейси, щоб дізнатися, чи повинна програма отримувати переривання від певного пристрою? Наприклад: якщо програма відкриває файл слова, він читає спеціальний файл пристрою символів, щоб знати, що програма повинна відповідати на введення з клавіатури?
bernie2436

1) Дещо . Це аналогія бідної людини, але це зробить.
Шадур

2
2) Ви пропускаєте там приблизно три-чотири шари абстракції. Програма, за допомогою якої ви відкриваєте текстовий файл, не знає і не піклується про те, що таке пристрій клавіатури. Зв'язок із базовим обладнанням відбувається або через емулятор терміналу (якщо ви перебуваєте в консольному режимі), або через шар події X (якщо ви перебуваєте в графічному режимі), кожен з яких прослухає клавіатуру та інші приводи та вирішить, що , якщо що, щоб перейти до програми. Я підсумовую тут досить складну багатошарову систему; ви можете взагалі прочитати на X Window System.
Шадур

1
Зауважте також, що на деяких ароматах UN * X є спеціальні файли символів для пристроїв зберігання даних; читання чи запис у спеціальний файл перетворюється на читання чи запис у послідовність блоків на пристрої. (В останніх версіях FreeBSD, ті тільки спеціальні файли для пристроїв зберігання даних, там немає жодного блоку спеціальних файлів.)

10

Кожен файл, пристрій чи інше підтримує 6 основних операцій у VFS:

  1. відчинено
  2. Закрити
  3. Прочитайте
  4. Пишіть
  5. Шукати
  6. Розкажи

Крім того, файли пристроїв підтримують управління введення-виводу, що дозволяє виконувати інші операції, які не охоплені першими 6.

У випадку особливого персонажа пошук і скажіть не реалізовано, оскільки вони підтримують потоковий інтерфейс . Тобто читання або запис безпосередньо, як це робиться з перенаправленням в оболонці:

echo 'foo' > /dev/some/char
sed ... < /dev/some/char

6

file_operationsПриклад мінімальної експлуатації

Як тільки ви бачите мінімальний приклад, все стає очевидним.

Ключові ідеї:

  • file_operations містить зворотні виклики для кожного файлового syscall
  • mknod <path> c <major> <minor> створює символьний пристрій, який використовує ці file_operations
  • для символьних пристроїв, які динамічно розподіляють номери пристроїв (норма, щоб уникнути конфліктів), знайдіть номер за допомогою cat /proc/devices

character_device.ko модуль ядра:

#include <asm/uaccess.h> /* copy_from_user, copy_to_user */
#include <linux/errno.h> /* EFAULT */
#include <linux/fs.h> /* register_chrdev, unregister_chrdev */
#include <linux/jiffies.h>
#include <linux/module.h>
#include <linux/printk.h> /* printk */
#include <uapi/linux/stat.h> /* S_IRUSR */

#define NAME "lkmc_character_device"

MODULE_LICENSE("GPL");

static int major;

static ssize_t read(struct file *filp, char __user *buf, size_t len, loff_t *off)
{
    size_t ret;
    char kbuf[] = {'a', 'b', 'c', 'd'};

    ret = 0;
    if (*off == 0) {
        if (copy_to_user(buf, kbuf, sizeof(kbuf))) {
            ret = -EFAULT;
        } else {
            ret = sizeof(kbuf);
            *off = 1;
        }
    }
    return ret;
}

static const struct file_operations fops = {
    .owner = THIS_MODULE,
    .read = read,
};

static int myinit(void)
{
    major = register_chrdev(0, NAME, &fops);
    return 0;
}

static void myexit(void)
{
    unregister_chrdev(major, NAME);
}

module_init(myinit)
module_exit(myexit)

Тестова програма Userland:

insmod /character_device.ko
dev="lkmc_character_device"
major="$(grep "$dev" /proc/devices | cut -d ' ' -f 1)"
mknod "/dev/$dev" c "$major" 0
cat /dev/lkmc_character_device
# => abcd
rm /dev/lkmc_character_device
rmmod character_device

GitHub QEMU + Buildroot вгору за течією з бойлерною панеллю, щоб реально запустити його:

Більш складні приклади:


Це було дуже корисно дякую! Лише одне запитання, що саме це робить *off = 1;, і чому це встановлено 1?
SilverSlash

1
@SilverSlash це значення передається через кілька readдзвінків до одного і того ж open(дескриптора файлу. Водій може робити з ним все, що завгодно. Звичайний семантичний - містити кількість прочитаних байтів. Однак у цьому прикладі у нас є просто простіша семантика: 0для першого читання, 1після першого читання. Спробуйте запустити його і поставте налагодження printk або GDB step.
Ciro Santilli 新疆 改造 中心 法轮功 六四 事件

4

"Персонаж за часом" - це неправильне значення (як це ідея, що пристрої символів обов'язково не підтримують пошук і передачу). Насправді пристрої "блокувати за один раз" (тобто суворо орієнтовані на запис, наприклад, стрічковий накопичувач *) повинні бути пристроями символів. Таким чином, ідея про те, що символьний пристрій обов'язково повинен бути невидимим - драйвери символьних пристроїв визначають повну file_operationsструктуру, яка вільна визначати llseek чи ні, залежно від того, підтримує пристрій операцію. Приклади символів, які більшість людей вважають прикладами, є нульовими, urandom, TTY-пристроями, звуковою картою, мишкою тощо ..., які неможливо побачити через специфіку того, що ці пристрої є, але / dev / vcs, / dev / fb0 , і / dev / kmem - також пристрої символів, і всі вони шукаються.

Як я вже згадував, драйвер пристрою символів визначає структуру file_operations, яка має покажчики функцій для всіх операцій, які, можливо, хтось захоче зателефонувати на файл - шукати, читати, писати, ioctl і т. Д. - і всі вони викликаються один раз, коли відповідний системний виклик виконується з відкритим файлом пристрою. І тому читати і писати може робити все, що завгодно, за допомогою своїх аргументів - він може відмовитись прийняти написання, яке є занадто великим, або написати лише те, що підходить; він може читати лише ті дані, що відповідають одному запису, а не всю запитувану кількість байт.

Отже, що таке блок-пристрій? В основному, блокові пристрої - це дисководи. Жоден інший тип пристроїв (крім віртуальних дискових накопичувачів, таких як ramdisk та loopback) не є блоковим пристроєм. Вони інтегровані в систему запитів вводу / виводу, рівень файлової системи, буфер / кеш-систему та систему віртуальної пам'яті таким чином, щоб пристрої символів не були, навіть коли ви звертаєтесь, наприклад, / dev / sda з користувальницького процесу . Навіть "необроблені пристрої", які сторінка згадує як виняток, є пристроями символів .

* Деякі системи UNIX реалізували те, що тепер називається "режим фіксованого блоку" - який дозволяє групі ядер і розділяти запити вводу / виводу для встановлення встановлених меж блоку більш-менш таким же чином, як це робиться для дисководів - як блок пристрій. Для "режиму змінного блоку" потрібен символьний пристрій, який зберігає межі блоку в користувацькій програмі як єдиний запис (2) виклик записує один блок, а один виклик читання (2) повертає один блок. Оскільки перемикання режимів зараз реалізоване як іоктол, а не як окремий файл пристрою, використовується пристрій символів. Пристрої змінних записів на стрічку здебільшого "не підлягають пошуку", тому що пошук передбачає підрахунок числа записів, а не кількість байтів, а нативної операції пошуку реалізовані як ioctl.


1

Пристрої символів можуть бути створені модулями ядра (або самим ядром). Коли створюється пристрій, творець надає покажчики на функції, які реалізують обробку стандартних дзвінків, таких як відкрити, читати тощо. Потім ядро ​​Linux пов'язує ці функції з символьним пристроєм, наприклад, коли програма в режимі користувача викликає read () Функція файлу символьного пристрою призведе до системного виклику, і ядро ​​перенаправить цей виклик до функції читання, визначеної під час створення драйвера. Там це крок за кроком підручник по створенню пристрою символів тут , ви можете створити зразок проект і крок через нього , використовуючи відладчик , щоб зрозуміти , як створюється об'єкт пристрою і коли обробники викликаються.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.