Làm thế nào để thiết bị nhân vật hoặc tập tin đặc biệt nhân vật hoạt động?


22

Tôi đang cố gắng để hiểu các tập tin đặc biệt nhân vật. Từ wikipedia , tôi hiểu rằng các tệp này "cung cấp giao diện" cho các thiết bị truyền dữ liệu một ký tự một lần. Tôi hiểu rằng hệ thống bằng cách nào đó gọi thiết bị nhân vật thay vì gọi trực tiếp trình điều khiển thiết bị. Nhưng làm thế nào để tập tin cung cấp giao diện này? Đây có phải là một thực thi mà dịch cuộc gọi hệ thống? Ai đó có thể giải thích những gì lên.

Câu trả lời:


19

Họ thực sự chỉ là như vậy - giao diện. Được mã hóa bởi một số "chính" và "phụ", chúng cung cấp một hook cho kernel.

Chúng có hai loại (tốt, ba, nhưng các ống được đặt tên nằm ngoài phạm vi của lời giải thích này): Thiết bị nhân vật và Thiết bị khối.

Thiết bị khối có xu hướng là thiết bị lưu trữ, có khả năng đệm đầu ra và lưu trữ dữ liệu để truy xuất sau này.

Thiết bị nhân vật là những thứ như card âm thanh hoặc đồ họa hoặc thiết bị đầu vào như bàn phím và chuột.

Trong mỗi trường hợp, khi kernel tải trình điều khiển chính xác (vào lúc khởi động hoặc thông qua các chương trình như udev ), nó sẽ quét các xe buýt khác nhau để xem liệu có thiết bị nào được xử lý bởi trình điều khiển đó có thực sự có trên hệ thống không. Nếu vậy, nó sẽ thiết lập một thiết bị 'nghe' trên số chính / phụ phù hợp.

(Ví dụ: Bộ xử lý tín hiệu số của thẻ âm thanh đầu tiên được tìm thấy bởi hệ thống của bạn có cặp số chính / phụ là 14/3; số thứ hai là 14,35, v.v.)

Tùy thuộc vào udev để tạo một mục nhập /devcó tên dsplà một thiết bị ký tự được đánh dấu chính 14 phụ 3.

(Trong các phiên bản cũ hơn hoặc tối thiểu đáng kể của Linux, /dev/có thể không được tải động mà chỉ chứa tất cả các tệp thiết bị có thể tĩnh.)

Sau đó, khi chương trình không gian người dùng cố gắng truy cập một tệp được đánh dấu là 'tệp đặc biệt ký tự' với số chính / phụ thích hợp (ví dụ: trình phát âm thanh của bạn đang cố gửi âm thanh kỹ thuật số đến /dev/dsp), hạt nhân biết rằng dữ liệu này cần phải được truyền qua trình điều khiển mà số chính / phụ được gắn vào; có lẽ nói lái xe biết phải làm gì với nó lần lượt.


1
1. Vậy số chính / phụ có giống với cổng không?
bernie2436

2. Vì vậy, khi các chương trình truy cập vào bất kỳ tệp nào, kernel sẽ đọc các giao diện đặc biệt này để tìm hiểu xem chương trình có bị gián đoạn từ một thiết bị cụ thể không? Ví dụ: nếu một chương trình mở một tệp từ, nó sẽ đọc tệp đặc biệt của thiết bị ký tự để biết chương trình sẽ phản hồi với đầu vào bàn phím?
bernie2436

1) Một chút nào đó . Đó là sự tương tự của một người nghèo nhưng nó sẽ làm được.
Shadur

2
2) Bạn đang thiếu khoảng ba hoặc bốn lớp trừu tượng ở đó. Chương trình bạn mở một tệp văn bản mà không biết cũng không quan tâm thiết bị bàn phím là gì. Giao tiếp với phần cứng cơ bản xảy ra thông qua trình giả lập thiết bị đầu cuối (nếu bạn đang ở chế độ bảng điều khiển) hoặc qua lớp sự kiện X (nếu bạn đang ở chế độ đồ họa), một trong hai sẽ lắng nghe bàn phím và các ổ đĩa khác và quyết định xem , nếu có bất cứ điều gì, để truyền lại cho chương trình. Tôi đang tóm tắt một hệ thống đa lớp khá phức tạp ở đây; bạn có thể làm tốt để đọc lên Hệ thống X Window nói chung.
Shadur

1
Cũng lưu ý rằng, trên một số hương vị của UN * X, có các tệp đặc biệt cho các thiết bị lưu trữ; một đọc hoặc ghi vào tập tin đặc biệt biến thành đọc hoặc ghi vào một chuỗi các khối trên thiết bị. (Trong các phiên bản gần đây của FreeBSD, đó là những chỉ tập tin đặc biệt cho các thiết bị lưu trữ, có không có khối tập tin đặc biệt.)

10

Mỗi tệp, thiết bị hoặc cách khác, hỗ trợ 6 thao tác cơ bản trong VFS:

  1. Mở
  2. Gần
  3. Đọc
  4. Viết
  5. Tìm kiếm
  6. Nói

Ngoài ra, các tệp thiết bị hỗ trợ Điều khiển I / O, cho phép các hoạt động linh tinh khác không nằm trong 6 đầu tiên.

Trong trường hợp một nhân vật đặc biệt, tìm kiếm và cho biết không được thực hiện vì chúng hỗ trợ giao diện phát trực tuyến . Đó là, đọc hoặc viết trực tiếp như được thực hiện với chuyển hướng trong trình bao:

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

6

file_operationsVí dụ runnable tối thiểu

Một khi bạn thấy một ví dụ tối thiểu, tất cả trở nên rõ ràng.

Các ý tưởng chính là:

  • file_operations chứa các cuộc gọi lại cho mỗi tòa nhà liên quan đến tập tin
  • mknod <path> c <major> <minor> tạo ra một thiết bị nhân vật sử dụng chúng file_operations
  • đối với các thiết bị ký tự phân bổ động số thiết bị (định mức để tránh xung đột), hãy tìm số có cat /proc/devices

character_device.ko mô-đun hạt nhân:

#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)

Chương trình thử nghiệm người dùng:

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 ngược dòng với nồi hơi để thực sự chạy nó:

Ví dụ phức tạp hơn:


Điều này là siêu hữu ích cảm ơn! Chỉ cần một câu hỏi, chính xác điều này làm gì *off = 1;, và tại sao nó được đặt thành 1?
SilverSlash

1
@SilverSlash giá trị đó được chuyển qua nhiều readcuộc gọi đến cùng một bộ open(mô tả tệp. Người lái xe có thể làm bất cứ điều gì nó muốn với nó. Ngữ nghĩa thông thường, là để chứa số byte được đọc. Tuy nhiên, trong ví dụ này, chúng ta chỉ có một ngữ nghĩa đơn giản hơn: 0cho lần đọc đầu tiên, 1sau lần đọc đầu tiên. Cố gắng chạy nó và đặt một bước printk hoặc GDB gỡ lỗi nó.
Ciro Santilli 心 心

4

"Nhân vật tại một thời điểm" là một cách gọi sai (như ý tưởng rằng các thiết bị nhân vật nhất thiết không hỗ trợ tìm kiếm và cho biết). Trong thực tế, "chặn tại một thời điểm" (nghĩa là các thiết bị được định hướng theo bản ghi nghiêm ngặt, chẳng hạn như các thiết bị ổ đĩa băng *) phải là thiết bị ký tự. Vì vậy, ý tưởng rằng một thiết bị nhân vật nhất thiết phải là không thể tách rời - trình điều khiển thiết bị nhân vật xác định file_operationscấu trúc đầy đủ có thể tự do xác định llseek hay không tùy theo thiết bị có hỗ trợ hoạt động hay không. Các thiết bị nhân vật mà hầu hết mọi người nghĩ là ví dụ là null, urandom, thiết bị TTY, card âm thanh, chuột, v.v ... tất cả đều không thể tìm thấy vì các chi tiết cụ thể của những thiết bị đó là gì, nhưng / dev / vcs, / dev / fb0 và / dev / kmem cũng là các thiết bị ký tự và chúng đều có thể tìm kiếm được.

Như tôi đã đề cập, trình điều khiển thiết bị ký tự xác định cấu trúc file_operations có con trỏ hàm cho tất cả các hoạt động mà ai đó có thể muốn gọi trên tệp - tìm kiếm, đọc, viết, ioctl, v.v. - và mỗi lệnh này được gọi một lần khi hệ thống tương ứng gọi được thực hiện với tập tin thiết bị này mở. Và đọc và viết do đó có thể làm bất cứ điều gì nó muốn với các đối số của nó - nó có thể từ chối chấp nhận một văn bản quá lớn hoặc chỉ viết những gì phù hợp; nó chỉ có thể đọc dữ liệu tương ứng với một bản ghi chứ không phải toàn bộ số byte được yêu cầu.

Vì vậy, một thiết bị khối là gì? Về cơ bản, các thiết bị khối là ổ đĩa. Không có loại thiết bị nào khác (ngoại trừ các ổ đĩa ảo , như ramdisk và loopback) là một thiết bị khối. Chúng được tích hợp vào hệ thống yêu cầu I / O, lớp hệ thống tập tin, hệ thống bộ đệm / bộ đệm và hệ thống bộ nhớ ảo theo cách mà các thiết bị ký tự không có, ngay cả khi bạn đang truy cập eg / dev / sda từ quá trình người dùng . Ngay cả "thiết bị thô" mà trang đề cập đến là một ngoại lệ là thiết bị ký tự .

* Một số hệ thống UNIX đã triển khai cái gọi là "chế độ khối cố định" - cho phép nhóm kernel và phân tách các yêu cầu I / O để phù hợp với các ranh giới khối được cấu hình theo cách tương tự như được thực hiện cho các ổ đĩa - như một khối thiết bị. Một thiết bị ký tự là cần thiết cho "chế độ khối biến", duy trì ranh giới khối từ chương trình người dùng dưới dạng một cuộc gọi viết (2) viết một khối và một cuộc gọi đọc (2) trả về một khối. Vì chuyển đổi chế độ được triển khai dưới dạng ioctl chứ không phải là tệp thiết bị riêng biệt, nên một thiết bị ký tự được sử dụng. Các ổ đĩa băng ghi âm biến đổi chủ yếu là "không thể tìm kiếm" bởi vì tìm kiếm liên quan đến việc đếm một số bản ghi chứ không phải là một số byte và hoạt động tìm kiếm riêng được thực hiện như một ioctl.


1

Các thiết bị ký tự có thể được tạo bởi các mô-đun hạt nhân (hoặc chính hạt nhân). Khi một thiết bị được tạo, trình tạo cung cấp các con trỏ tới các hàm thực hiện các lệnh gọi tiêu chuẩn như mở, đọc, v.v. Hạt nhân Linux sau đó liên kết các chức năng đó với thiết bị ký tự, ví dụ như khi ứng dụng ở chế độ người dùng gọi hàm read () Chức năng trên một tệp thiết bị ký tự, nó sẽ dẫn đến một tòa nhà và sau đó kernel sẽ định tuyến cuộc gọi này đến một chức năng đọc được chỉ định khi tạo trình điều khiển. Có một hướng dẫn từng bước về cách tạo một thiết bị nhân vật ở đây , bạn có thể tạo một dự án mẫu và từng bước sử dụng trình gỡ lỗi để hiểu cách tạo đối tượng thiết bị và khi trình xử lý được gọi.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.