อุปกรณ์อักขระหรือไฟล์พิเศษของอักขระทำงานอย่างไร


22

ฉันพยายามทำความเข้าใจกับไฟล์พิเศษของตัวละคร จากวิกิพีเดียฉันเข้าใจว่าไฟล์เหล่านี้ "ให้ส่วนต่อประสาน" สำหรับอุปกรณ์ที่ส่งข้อมูลทีละตัวอักษร ความเข้าใจของฉันคือระบบจะเรียกอุปกรณ์ตัวอักษรแทนการเรียกไดรเวอร์อุปกรณ์โดยตรง แต่ไฟล์มีส่วนต่อประสานนี้อย่างไร มันเป็นปฏิบัติการที่แปลสายระบบหรือไม่ ใครสามารถอธิบายสิ่งที่เกิดขึ้น

คำตอบ:


19

จริงๆแล้วมันเป็นเพียงแค่อินเตอร์เฟส เข้ารหัสโดยหมายเลข "หลัก" และ "รอง" ซึ่งจะให้ตะขอของเคอร์เนล

พวกเขามาในสองรสชาติ (ทั้งสามท่อ แต่ชื่อมีอยู่นอกขอบเขตของคำอธิบายนี้ในตอนนี้): อุปกรณ์ตัวละครและอุปกรณ์บล็อก

อุปกรณ์บล็อคมักจะเป็นอุปกรณ์เก็บข้อมูลที่สามารถบัฟเฟอร์เอาต์พุตและจัดเก็บข้อมูลเพื่อเรียกคืนได้ในภายหลัง

อุปกรณ์ตัวละครคืออุปกรณ์ต่าง ๆ เช่นการ์ดเสียงหรือกราฟิกหรืออุปกรณ์อินพุตเช่นแป้นพิมพ์และเมาส์

ในแต่ละกรณีเมื่อเคอร์เนลโหลดไดรเวอร์ที่ถูกต้อง (ไม่ว่าในเวลาบูตหรือผ่านโปรแกรมเช่นudev ) มันจะสแกนบัสต่างๆเพื่อดูว่าอุปกรณ์ใด ๆ ที่จัดการโดยไดรเวอร์นั้นมีอยู่จริงในระบบ หากเป็นเช่นนั้นจะตั้งค่าอุปกรณ์ที่ 'ฟัง' ในหมายเลขหลัก / รองที่เหมาะสม

(ตัวอย่างเช่นตัวประมวลผลสัญญาณดิจิตอลของการ์ดเสียงตัวแรกที่ระบบของคุณพบจะได้รับคู่หลัก / รองหมายเลข 14/3 ส่วนที่สองได้รับ 14,35 เป็นต้น)

มันขึ้นอยู่กับ udev ที่จะสร้างรายการใน/devชื่อdspเป็นอุปกรณ์ตัวละครที่มีเครื่องหมายสำคัญ 14 รายย่อย 3

(ใน Linux เวอร์ชันเก่าหรือต่ำสุดอย่างมีนัยสำคัญ/dev/อาจไม่โหลดแบบไดนามิก แต่มีไฟล์อุปกรณ์ที่เป็นไปได้ทั้งหมดแบบคงที่)

จากนั้นเมื่อโปรแกรม userspace พยายามเข้าถึงไฟล์ที่มีการทำเครื่องหมายเป็น 'ไฟล์พิเศษของตัวละคร' ด้วยหมายเลขหลัก / รองที่เหมาะสม (เช่นเครื่องเล่นเสียงของคุณพยายามส่งสัญญาณเสียงดิจิตอลไป/dev/dsp) เคอร์เนลรู้ว่าข้อมูลนี้จำเป็นต้อง ถูกส่งผ่านทางไดรเวอร์ที่มีหมายเลขหลัก / รองเชื่อมต่อ; สันนิษฐานว่าคนขับรู้ว่าจะทำอย่างไรกับมันในทางกลับกัน


1
1. ดังนั้นหมายเลขหลัก / รองจึงคล้ายกับพอร์ต
bernie2436

2. ดังนั้นเมื่อโปรแกรมเข้าถึงไฟล์ใด ๆ เคอร์เนลจะอ่านอินเตอร์เฟสพิเศษเหล่านี้เพื่อเรียนรู้ว่าโปรแกรมควรได้รับการขัดจังหวะจากอุปกรณ์ใดหรือไม่ เช่น: หากโปรแกรมเปิดไฟล์คำ, มันจะอ่านไฟล์พิเศษของอุปกรณ์ตัวอักษรเพื่อให้รู้ว่าโปรแกรมควรตอบสนองต่อคีย์บอร์ด?
bernie2436

1) ค่อนข้าง มันเป็นความคล้ายคลึงของคนจน แต่มันจะทำ
Shadur

2
2) คุณหายไปประมาณสามหรือสี่ชั้นของสิ่งที่เป็นนามธรรม โปรแกรมที่คุณเปิดไฟล์ข้อความโดยที่ไม่รู้หรือไม่สนใจว่าอุปกรณ์แป้นพิมพ์คืออะไร การสื่อสารกับฮาร์ดแวร์พื้นฐานนั้นเกิดขึ้นผ่านเทอร์มินัลอีมูเลเตอร์ (ถ้าคุณอยู่ในโหมดคอนโซล) หรือผ่านเลเยอร์เหตุการณ์ X (หากคุณอยู่ในโหมดกราฟิก) ซึ่งทั้งสองอย่างนี้จะรับฟังแป้นพิมพ์และไดรฟ์อื่น ๆ หากมีสิ่งใดที่จะส่งผ่านไปยังโปรแกรม ฉันสรุประบบหลายเลเยอร์ที่ค่อนข้างซับซ้อนที่นี่ คุณอาจจะอ่านบนระบบ X Window ได้ดี
Shadur

1
โปรดทราบว่าในบางรสชาติของ UN * X จะมีไฟล์พิเศษสำหรับอุปกรณ์เก็บข้อมูล การอ่านหรือเขียนไปยังไฟล์พิเศษจะเปลี่ยนเป็นการอ่านหรือเขียนไปยังลำดับของบล็อกบนอุปกรณ์ (ในรุ่นล่าสุดของ FreeBSD, ผู้ที่มีเพียงไฟล์พิเศษสำหรับอุปกรณ์จัดเก็บข้อมูลนั้นมีอยู่ไม่มีบล็อกไฟล์พิเศษ.)

10

ทุกไฟล์อุปกรณ์หรืออย่างอื่นรองรับการทำงานพื้นฐาน 6 อย่างใน VFS:

  1. เปิด
  2. ปิด
  3. อ่าน
  4. เขียน
  5. แสวงหา
  6. บอก

นอกจากนี้ไฟล์อุปกรณ์ยังรองรับการควบคุม I / O ซึ่งช่วยให้การดำเนินการอื่น ๆ ที่ไม่ครอบคลุมใน 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 upstream พร้อมกับ boilerplate เพื่อใช้งานจริง:

ตัวอย่างที่ซับซ้อนมากขึ้น:


นี่เป็นประโยชน์อย่างมากขอบคุณ! เพียงแค่หนึ่งคำถามว่าสิ่งที่จะทำนี้*off = 1;และทำไมมันตั้งค่าให้1?
SilverSlash

1
@SilverSlash ค่านั้นถูกส่งผ่านข้ามการreadเรียกไปยังopen(ไฟล์ descriptor หลาย ๆ ตัว ผู้ขับขี่สามารถทำอะไรก็ได้ที่ต้องการด้วย ความหมายปกติคือมีจำนวนไบต์อ่าน อย่างไรก็ตามในตัวอย่างนี้เรามีความหมายที่ง่ายกว่า: 0สำหรับการอ่านครั้งแรก1หลังจากการอ่านครั้งแรก ลองเรียกใช้แล้ววางขั้นตอน printk หรือ GDB
Ciro Santilli 新疆改造中心法轮功六四事件

4

"ตัวละครในเวลา" เป็นชื่อเรียกที่ผิด (ตามความคิดที่ว่าอุปกรณ์ตัวละครไม่จำเป็นต้องรองรับการค้นหาและบอก) ในความเป็นจริงอุปกรณ์ "บล็อกในแต่ละครั้ง" (เช่นที่เน้นการบันทึกอย่างเคร่งครัดเช่นเทปไดรฟ์ *) ต้องเป็นอุปกรณ์อักขระ ดังนั้นจึงเป็นความคิดที่ว่าอุปกรณ์ตัวอักษรจะต้องไม่สามารถมองเห็นได้ - ไดรเวอร์อุปกรณ์ตัวอักษรกำหนดfile_operationsโครงสร้างแบบเต็มซึ่งมีอิสระในการกำหนด llseek หรือไม่เป็นไปตามที่อุปกรณ์สนับสนุนการดำเนินการ อุปกรณ์ตัวละครที่คนส่วนใหญ่คิดว่าเป็นตัวอย่างนั้นเป็นโมฆะ, urandom, อุปกรณ์ TTY, การ์ดเสียง, เมาส์, ฯลฯ ... ซึ่งไม่สามารถมองเห็นได้ทั้งหมดเนื่องจากลักษณะเฉพาะของอุปกรณ์เหล่านั้น แต่ / dev / vcs, / dev / fb0 และ / dev / kmem ยังเป็นอุปกรณ์ตัวละครและพวกมันก็หาได้

ดังที่ฉันได้กล่าวไว้ไดรเวอร์อุปกรณ์อักขระจะกำหนดโครงสร้าง file_operations ที่มีฟังก์ชันพอยน์เตอร์สำหรับการดำเนินการทั้งหมดที่บางคนอาจต้องการเรียกใช้ไฟล์ - ค้นหา, อ่าน, เขียน, ioctl และอื่น ๆ ซึ่งแต่ละครั้งจะถูกเรียกเมื่อการเรียกระบบที่สอดคล้องกัน ถูกดำเนินการโดยเปิดไฟล์อุปกรณ์นี้ ดังนั้นการอ่านและเขียนจึงสามารถทำอะไรก็ได้ตามที่ต้องการด้วยข้อโต้แย้ง - มันสามารถปฏิเสธที่จะยอมรับการเขียนที่ใหญ่เกินไปหรือเขียนเฉพาะสิ่งที่เหมาะสม มันสามารถอ่านข้อมูลที่สอดคล้องกับหนึ่งระเบียนมากกว่าจำนวนไบต์ที่ร้องขอทั้งหมด

ดังนั้นอุปกรณ์บล็อคคืออะไร? โดยทั่วไปอุปกรณ์บล็อกคือดิสก์ไดรฟ์ ไม่มีอุปกรณ์ประเภทอื่น (ยกเว้นดิสก์ไดรฟ์เสมือนเช่น ramdisk และลูปแบ็ค) เป็นอุปกรณ์บล็อก พวกมันถูกรวมเข้ากับระบบคำขอ I / O, ระบบไฟล์เลเยอร์, ​​ระบบบัฟเฟอร์ / แคชและระบบหน่วยความจำเสมือนในลักษณะที่อุปกรณ์ตัวละครไม่ได้อยู่แม้ในขณะที่คุณเข้าถึงเช่น / dev / sda จากกระบวนการผู้ใช้ . แม้แต่ "อุปกรณ์ดิบ" ที่หน้ากล่าวเป็นข้อยกเว้นเป็นอุปกรณ์ตัวอักษร

* ระบบ UNIX บางระบบใช้สิ่งที่เรียกว่า "โหมดบล็อกคงที่" ซึ่งช่วยให้กลุ่มเคอร์เนลและคำขอ I / O แบบแยกเพื่อให้พอดีกับขอบเขตบล็อกที่ตั้งค่าในลักษณะเดียวกับที่ทำกับดิสก์ไดรฟ์มากขึ้นหรือน้อยลง เครื่อง จำเป็นต้องใช้อุปกรณ์อักขระสำหรับ "โหมดตัวแปรบล็อก" ซึ่งเก็บรักษาขอบเขตบล็อกจากโปรแกรมผู้ใช้ในขณะที่การเรียก write (2) ครั้งเดียวเขียนหนึ่งบล็อกและการเรียก read (2) หนึ่งครั้งจะส่งคืนหนึ่งบล็อก เนื่องจากการสลับโหมดถูกนำมาใช้ในขณะนี้เป็น ioctl แทนที่จะเป็นไฟล์อุปกรณ์แยกต่างหากจึงใช้อุปกรณ์ตัวอักษร เทปไดร์ฟแบบแปรผันได้ส่วนใหญ่เป็น "ไม่สามารถค้นหาได้" เนื่องจากการค้นหาเกี่ยวข้องกับการนับจำนวนเร็กคอร์ดมากกว่าจำนวนไบต์และการดำเนินการค้นหาเนทีฟจะถูกนำไปใช้เป็น ioctl


1

อุปกรณ์ตัวละครสามารถสร้างได้โดยโมดูลเคอร์เนล (หรือเคอร์เนลเอง) เมื่ออุปกรณ์ถูกสร้างขึ้นผู้สร้างจะมีตัวชี้ไปยังฟังก์ชั่นที่ใช้จัดการการโทรมาตรฐานเช่นเปิดอ่าน ฯลฯ เคอร์เนล Linux จะเชื่อมโยงฟังก์ชั่นเหล่านั้นกับอุปกรณ์ตัวอักษรดังนั้นตัวอย่างเช่นเมื่อแอปพลิเคชันโหมดผู้ใช้เรียก read () ฟังก์ชั่นในไฟล์อุปกรณ์ตัวอักษรมันจะส่งผลให้ syscall แล้วเคอร์เนลจะกำหนดเส้นทางการโทรนี้ไปยังฟังก์ชั่นการอ่านที่ระบุไว้เมื่อสร้างไดรเวอร์ มีแบบฝึกหัดทีละขั้นตอนในการสร้างอุปกรณ์ตัวอักษรที่นี่คุณสามารถสร้างโครงการตัวอย่างและขั้นตอนโดยใช้ดีบักเกอร์เพื่อทำความเข้าใจวิธีการสร้างวัตถุอุปกรณ์และเมื่อตัวจัดการถูกเรียกใช้

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.