문자 특수 파일을 이해하려고합니다. 에서 위키 피 디아 , 나는이 파일이 장치에 대한 "인터페이스를 제공한다"고 이해를 한 번에 전송 데이터를 하나 개의 문자가. 내 이해는 시스템이 장치 드라이버를 직접 호출하는 대신 어떻게 든 문자 장치를 호출한다는 것입니다. 그러나 파일은 어떻게이 인터페이스를 제공합니까? 시스템 호출을 번역하는 실행 파일입니까? 누군가 무슨 일인지 설명 할 수 있습니까?
문자 특수 파일을 이해하려고합니다. 에서 위키 피 디아 , 나는이 파일이 장치에 대한 "인터페이스를 제공한다"고 이해를 한 번에 전송 데이터를 하나 개의 문자가. 내 이해는 시스템이 장치 드라이버를 직접 호출하는 대신 어떻게 든 문자 장치를 호출한다는 것입니다. 그러나 파일은 어떻게이 인터페이스를 제공합니까? 시스템 호출을 번역하는 실행 파일입니까? 누군가 무슨 일인지 설명 할 수 있습니까?
답변:
그들은 실제로 인터페이스입니다. "major"와 "minor"번호로 인코딩되어 커널에 연결됩니다.
문자 장치와 블록 장치라는 두 가지 맛이 있습니다 (물론 세 가지이지만 명명 된 파이프는이 설명의 범위를 벗어났습니다).
블록 장치는 출력을 버퍼링하고 나중에 검색하기 위해 데이터를 저장할 수있는 저장 장치 인 경향이 있습니다.
문자 장치는 오디오 또는 그래픽 카드 또는 키보드 및 마우스와 같은 입력 장치입니다.
각각의 경우 커널이 올바른 드라이버를로드 할 때 (부팅시 또는 udev 와 같은 프로그램을 통해 ) 다양한 버스를 스캔하여 해당 드라이버가 처리하는 장치가 실제로 시스템에 있는지 확인합니다. 그렇다면 적절한 주 / 부 번호를 '듣는'장치를 설정합니다.
(예를 들어, 시스템에서 찾은 첫 번째 오디오 카드의 디지털 신호 프로세서는 14/3의 주 / 부 숫자 쌍을 받고, 두 번째는 14,35 등을 얻습니다.)
major 14 minor 3으로 표시된 문자 장치로 /dev
이름이 지정된 항목을 작성하는 것은 udev에 달려 있습니다 dsp
.
Linux의 이전 또는 최소 설치 공간 버전에서는 /dev/
동적으로로드되지 않고 가능한 모든 장치 파일이 정적으로 포함되어있을 수 있습니다.
그런 다음 사용자 공간 프로그램이 적절한 주 / 부 번호를 가진 '특수 문자 파일'로 표시된 파일에 액세스하려고하면 (예 : 오디오 플레이어가 디지털 오디오를 보내려고하는 /dev/dsp
경우) 커널은이 데이터가 메이저 / 마이너 번호가 첨부 된 드라이버를 통해 전송되고; 아마도 운전자는 그와 함께 무엇을해야할지 알고있을 것입니다.
모든 파일, 장치 등은 VFS 내에서 6 가지 기본 작업을 지원합니다.
또한 장치 파일은 I / O 제어를 지원하여 처음 6에서 다루지 않은 기타 기타 작업을 허용합니다.
캐릭터 스페셜의 경우, 시크 앤텔은 스트리밍 인터페이스 를 지원하므로 구현되지 않습니다 . 즉, 쉘에서 리디렉션과 같이 직접 읽거나 쓰는 것은 다음과 같습니다.
echo 'foo' > /dev/some/char
sed ... < /dev/some/char
최소 실행 가능 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)
유저 랜드 테스트 프로그램 :
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 업스트림 :
더 복잡한 예 :
read
, write
, lseek
고정 된 크기로 내부 버퍼 대신 문자 장치의 debugfs의 경우 : https://github.com/cirosantilli/linux-kernel-module-cheat/blob/6788a577c394a2fc512d8f3df0806d84dc09f355/kernel_module/fops.cpoll
: https://github.com/cirosantilli/linux-kernel-module-cheat/blob/6788a577c394a2fc512d8f3df0806d84dc09f355/kernel_module/poll.cioctl
: https://github.com/cirosantilli/linux-kernel-module-cheat/blob/6788a577c394a2fc512d8f3df0806d84dc09f355/kernel_module/poll.canon_inode_getfd
file_operations
파일 시스템 파일이없는 파일 디스크립터에 a 를 연결 합니다. /programming/4508998/what-is-anonymous-inode/44388030#44388030*off = 1;
하며 왜 설정되어 1
있습니까?
read
호출에서 동일한 open(
파일 설명 자로 전달 됩니다. 운전자는 원하는대로 무엇이든 할 수 있습니다. 일반적인 의미는 읽은 바이트 수를 포함하는 것입니다. 그러나이 예제에서는 0
첫 번째 읽기 1
후 첫 번째 읽기에 대해 더 간단한 의미를 갖 습니다. 그것을 실행하고 printk 또는 GDB 단계를 디버깅하십시오.
"한 번에 캐릭터"는 잘못된 이름입니다 (캐릭터 장치가 찾기와 말하기를 반드시 지원하지 않는다는 생각과 마찬가지로). 실제로 "한 번에 차단"(즉, 테이프 드라이브와 같은 레코드 지향 *) 장치 는 문자 장치 여야 합니다. 캐릭터 장치는 반드시 보이지 않아야한다는 생각입니다. 캐릭터 장치 드라이버 file_operations
는 장치가 작업을 지원하는지 여부에 따라 무료로 정의 할 수 있는 전체 구조를 정의합니다. 대부분의 사람들이 예로 생각하는 문자 장치는 null, urandom, TTY 장치, 사운드 카드, 마우스 등입니다.이 장치의 특성으로 인해 모두 사용할 수는 없지만 / dev / vcs, / dev / fb0 및 / dev / kmem도 문자 장치이며 모두 검색 가능합니다.
앞에서 언급했듯이 문자 장치 드라이버는 파일에서 검색, 읽기, 쓰기, ioctl 등 모든 파일에 대해 호출하려는 모든 작업에 대한 함수 포인터가있는 file_operations 구조를 정의하며 해당 시스템 호출시 각각 한 번 호출됩니다. 이 장치 파일을 연 상태에서 실행됩니다. 따라서 읽기와 쓰기는 논증으로 원하는 모든 것을 할 수 있습니다. 너무 큰 쓰기를 받아들이기를 거부하거나 적합한 것을 쓰기 만 할 수 있습니다. 요청 된 전체 바이트 수가 아니라 하나의 레코드에 해당하는 데이터 만 읽을 수 있습니다.
그렇다면 블록 장치 란 무엇입니까? 기본적으로 블록 장치는 디스크 드라이브입니다. 다른 종류의 장치 ( 램 디스크 및 루프백과 같은 가상 디스크 드라이브 제외 )는 블록 장치가 아닙니다 . 사용자 프로세스에서 / dev / sda에 액세스하는 경우에도 문자 장치가없는 방식으로 I / O 요청 시스템, 파일 시스템 계층, 버퍼 / 캐시 시스템 및 가상 메모리 시스템에 통합 됩니다. . 페이지에서 예외 로 언급 한 "원시 장치"조차도 문자 장치 입니다.
* 일부 UNIX 시스템은 현재 "고정 블록 모드"를 구현했습니다.이를 통해 커널 그룹 및 분할 I / O 요청이 디스크 드라이브와 거의 동일한 방식으로 구성된 블록 경계에 맞게 블록으로 구성됩니다. 장치. "변수 블록 모드"에는 문자 장치가 필요합니다.이 변수 블록은 단일 write (2) 호출이 하나의 블록을 쓰고 단일 read (2) 호출이 하나의 블록을 반환하므로 사용자 프로그램의 블록 경계를 유지합니다. 모드 전환은 이제 별도의 장치 파일이 아닌 ioctl로 구현되므로 문자 장치가 사용됩니다. 가변 레코드 테이프 드라이브는 탐색에 여러 바이트가 아닌 여러 레코드의 수를 포함하고 기본 탐색 조작이 ioctl로 구현되기 때문에 대부분 "검색 할 수없는"것입니다.
문자 장치는 커널 모듈 (또는 커널 자체)로 만들 수 있습니다. 장치를 만들 때 작성자는 open, read 등과 같은 표준 처리를 구현하는 함수에 대한 포인터를 제공합니다. 그런 다음 Linux 커널은 이러한 기능을 문자 장치와 연결합니다. 예를 들어 사용자 모드 응용 프로그램이 read () 문자 장치 파일에서 함수를 호출하면 syscall이 발생하고 커널은이 호출을 드라이버 작성시 지정된 읽기 함수로 라우팅합니다. 문자 장치를 만드는 방법에 대한 단계별 튜토리얼있다 여기가 , 당신은 샘플 프로젝트와 핸들러를 호출하는 방법과 장치 개체가 생성되고 때 이해하기 디버거를 사용하여 그것을 통해 단계를 만들 수 있습니다.