인식 제고
드라이버는 기본 하드웨어 장치의 작동을 캡슐화하고 상위 계층에 기능 인터페이스를 제공합니다.
장치 분류: Linux 시스템은 장치를 문자 장치, 블록 장치 및 네트워크 장치의 3가지 범주로 나눕니다.
전체 통화 과정을 설명하기 위해 예를 들어보겠습니다
open("/dev/pin4",O_RDWR);
를 호출하고 /dev 아래의 pin4를 호출하여 읽기 및 쓰기 가능한 방식으로 엽니다. **== 상위 계층 열기가 커널에 호출되면 소프트 인터럽트가 발생합니다. 인터럽트 번호는 0X80으로, 커널 공간==**system_call
(커널 기능)을 호출하고 system_call은 /dev/pin4 장치 이름을 기반으로 원하는 장치 번호를 찾습니다. sys_open
를 호출하면 sys_open이 드라이버 목록에서 발견되고 를 기반으로 드라이버를 찾습니다. 메이저 장치 번호와 마이너 장치 번호 4번 핀의 오픈 기능은 4번 핀의 오픈 기능은 레지스터를 동작시키는 것입니다
여기에 이미지 설명을 삽입하세요
“
드라이버를 작성할 때 우리는 드라이버 추가: 드라이버 추가는 무엇을 합니까?
- 기기 이름
- 기기번호
- 장치 드라이버 기능(IO 포트를 구동하기 위한 작동 레지스터)
”
==요약== dev
아래pin4
핀, 프로세스는 다음과 같습니다: dev
下面的pin4
引脚,过程是:用户态调用open(“/de/pin4”,O_RDWR
),对于内核来说,上层调用open函数会触发一个软中断(系统调用专用,中断号是0x80,0x80代表发生了一个系统调用),系统进入内核态,并走到system_call
,可以认为这个就是此软中断的中断服务程序入口,然后通过传递过来的系统调用号来决定调用相应的系统调用服务程序(在这里是调用VFS
中的sys_open
)。sys_open
会在内核的驱动链表里面根据设备名和设备号查找到相关的驱动函数(每一个驱动函数是一个节点
사용자 모드 호출 열기
"/de/pin4",O_RDWR
), 커널용 예를 들어, 상위 계층에서 open 함수를 호출하면 소프트 인터럽트가 발생합니다(시스템 호출의 경우 특별하며 인터럽트 번호는 0x80, 0x80은 시스템 호출이 발생했음을 의미함). system_call
, 이렇게 생각하시면 됩니다. 소프트 인터럽트의 인터럽트 서비스 프로그램에 진입한 후, 전달된 시스템 콜 번호(여기서는 <code style="font-size: 14px;padding: VFS에서 2px 4px
;border-radius: 4px;margin: 0 2px;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;color: #10f5d6c">sys_open ). sys_open는 장치 이름과 장치 번호(<code style="font-size: 14px;padding: 2px 4px; border-radius: 4px; margin: 0 2px ; Font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;color: #10f5d6c">각 드라이버 함수는 노드입니다
), **==드라이버 함수에는 레지스터를 통해 IO 포트를 제어하기 위한 코드가 포함되어 있습니다. 그런 다음 IO 포트를 제어하여 관련 기능 ==**을 구현할 수 있습니다.
2. 각 구성요소에 대한 자세한 설명”
“
사용자 프로필:
open,read,write,fork,pthread,socket
. C 라이브러리(Linux 표준 라이브러리에 포함되어야 함): 커널 작업을 제어하기 위한 프로그램용 인터페이스를 제공하는 Clibary입니다. 호출 은 여기에 캡슐화되어 구현되며 작성된 애플리케이션에 의해 호출됩니다.
“
커널 상태:
🎜”🎜🎜
사용자가 특정 하드웨어 장치를 사용하려면 커널 모드 장치 드라이버가 필요하고,하드웨어가 작동하도록 구동합니다. 이전 기사wiringPi库
에서 언급했듯이 사용자가 하드웨어 장치를 제어할 수 있는 인터페이스를 제공합니다 , WiringPi 라이브러리가 없으면 WiringPi 라이브러리의 기능을 직접 구현해야 합니다. 즉, 장치 드라이버를 직접 작성해야 합니다. 이런 식으로 다른 유형의 보드를 얻으면 개발을 완료할 수도 있습니다.
리눅스의 모든 것은 파일입니다 모든 종류의 파일과 장치(예: 아래 그림과 같이 마우스, 키보드, 화면, 플래시, 메모리, 네트워크 카드)는 모두 파일이므로파일입니다. 파일 작업 기능을 사용하여 이러한 장치를 작동할 수 있습니다 .
질문이 있습니다. 열기, 읽기 등의 파일 작업 기능은 열린 파일이 어떤 하드웨어 장치인지 어떻게 알 수 있나요? ① open 함수에 해당 파일명을 입력하면 해당 기기를 제어할 수 있습니다. ②패스 ==장치번호(주장치번호, 부장치번호)==. 또한 이러한 드라이버의 위치와 구현 방법도 이해해야 합니다. 각 하드웨어 장치는 서로 다른 드라이버에 해당합니다(이러한 드라이버는 자체적으로 구현됩니다).
Linux 장치 관리는 파일 시스템과 긴밀하게 통합되어 있습니다. 다양한 장치는 ==device files==*라는 파일 형식으로 /dev 디렉터리에 저장됩니다. 애플리케이션은 이러한 장치 파일을 열고, 닫고, 읽고 쓸 수 있으며 일반 데이터 파일을 작동하는 것처럼 장치에서 작업을 완료할 수 있습니다. **이러한 장치를 관리하기 위해 시스템은 장치에 번호를 매깁니다**,*각 장치 번호는 ==주 장치 번호== 및 ==부 장치 번호==*로 구분됩니다(아래 그림 참조). ). *****주 장치 번호**는 서로 다른 유형의 장치를 구분하는 데 사용되며, **부 장치 번호는 동일한 유형의 여러 장치를 구분하는 데 사용됩니다. 일반적으로 사용되는 장치의 경우 Linux에는 일반적인 숫자가 있습니다. 예를 들어 하드 디스크의 주요 장치 번호는 3입니다. ****문자 디바이스 또는 블록 디바이스에는 메이저 디바이스 번호와 마이너 디바이스 번호가 있습니다. ==주 장치 번호와 부 장치 번호를 합쳐서 장치 번호라고 합니다==**.
“
주요 장치 번호는 특정 드라이버를 나타내는 데 사용됩니다.
부 장치 번호는 이 드라이버를 사용하는 각 장치를 나타내는 데 사용됩니다.”
예를 들어 임베디드 시스템에는 두 개의 LED 표시기가 있으며 LED 조명은 독립적으로 켜거나 꺼야 합니다. 그런 다음 LED 조명용 문자 장치 드라이버를 작성하고 주 장치 번호를 장치 번호 5로 등록하고 부 장치 번호를 각각 1과 2로 등록하면 됩니다. 여기서 보조 장치 번호는 각각 두 개의 LED 조명을 나타냅니다.
==운전자 연결 목록==
“
모든 장치의 드라이버 관리, 추가 또는 찾기
는 드라이버를 호출하고 있으며 애플리케이션 계층 사용자 공간은 이를 찾기 위해 open 함수를 사용합니다추가
添加
是发生在我们编写完驱动程序,加载到内核。查找
는 드라이버 작성을 마치고 커널에 로드한 후에 발생합니다.찾기
. 링크된 목록에 드라이버가 삽입되는 순서는 장치 번호로 검색됩니다. 즉, 주 장치 번호와 부 장치 번호는 다른 유형의 장치와 다른 유형의 장치를 구분할 수 있을 뿐만 아니라 아래에 소개된 드라이버 코드의 개발은 드라이버 추가(장치 번호, 장치 이름 및 장치 드라이버 기능 추가)와
호출에 지나지 않습니다. 운전사.
”
추가됨:
문자 장치 드라이버 작동 원리 Linux 세계에서는 모든 것이 파일이며 모든 하드웨어 장치 작업은 애플리케이션 계층에서 파일 작업으로 추상화됩니다. 애플리케이션 계층이 하드웨어 장치에 액세스하려면 하드웨어에 해당하는 드라이버를 호출해야 한다는 것을 알고 있습니다. Linux 커널에는 드라이버가 너무 많습니다. 애플리케이션이 기본 드라이버를 어떻게 정확하게 호출할 수 있습니까?
==꼭 알아야 할 지식:==🎜🎜struct inode
구조로 설명됩니다. 이 구조는 파일 형식, 액세스 권한 등과 같은 파일의 모든 정보를 기록합니다. /dev
目录或者其他如/sys
디렉터리에 해당 파일을 갖습니다. struct file
구조를 할당합니다. (1) open 함수가 디바이스 파일을 열면 해당 디바이스 파일에 해당하는 struct inode 구조에 기술된 정보를 기반으로 다음에 동작할 디바이스의 타입(캐릭터 디바이스 또는 블록 디바이스)을 알 수 있으며, 파일 구조도 할당됩니다.
(2) 구조체 inode 구조에 기록된 장치 번호에 따라 해당 드라이버를 찾을 수 있습니다. 여기서는 문자 장치를 예로 들어보겠습니다. Linux 운영 체제에서 각 문자 장치에는 struct cdev 구조가 있습니다. 이 구조는 캐릭터 디바이스의 모든 정보를 기술하며, 그 중 가장 중요한 것은 캐릭터 디바이스의 동작 기능 인터페이스이다.
(3) struct cdev 구조를 찾은 후 Linux 커널은 struct cdev 구조가 위치한 메모리 공간의 첫 번째 주소를 struct inode 구조의 i_cdev 멤버에 기록하고, struct cdev 구조. struct 파일 구조의 f_ops 멤버에 있습니다.
(4) 작업이 완료되면 VFS 계층은 파일 설명자(fd)를 애플리케이션에 반환합니다. 이 fd는 구조체 파일 구조에 해당합니다. 다음으로 상위 계층 응용 프로그램은 fd를 통해 구조체 파일을 찾은 다음 구조체 파일에서 문자 장치를 작동하기 위한 함수 인터페이스 file_Operation을 찾을 수 있습니다.
그 중 cdev_init와 cdev_add는 드라이버의 진입 함수에서 호출되어 각각 문자 장치와 file_Operation 함수 작업 인터페이스의 바인딩을 완료하고 문자 드라이버를 커널에 등록했습니다.
-커널 드라이버 **==가장 간단한 문자 장치 드라이버 프레임워크==**:
수동으로 장치 이름 만들기
sudo mknod +设备名字 +设备类型(c表示字符设备驱动) +主设备号+次设备号
b: 블록(버퍼) 특수 파일을 만듭니다. c, u: 버퍼링되지 않은 문자 특수 파일을 만듭니다. p: FIFO를 생성합니다. 수동으로 생성된 장치 이름을 삭제하려면 rm만 하면 됩니다. 아래 그림과 같이: 상위 계층 프로그램을 통해 장치를 엽니다. 드라이버가 없으면 실행 시 오류가 보고됩니다. 커널 드라이버에서는 상위 계층 시스템 호출 , sys_open 및 sys_write가 open,wirte
函数会触发sys_call
、sys_call会调用sys_open,
和sys_write
주요 장치 번호를 전달합니다. 그리고 커널의 드라이버 목록에 장치를 넣고 드라이버를 찾아 열기를 실행한 뒤 내부에 쓰기를 하게 되면 먼저 드라이버(장치 드라이버 파일)를 준비해야 합니다.
장치 드라이버 파일에는 고정된 프레임이 있습니다:
module_init(pin4_drv_init);
//入口 去调用 pin4_drv_init
기능int __init pin4_drv_init(void)
//실제 운전자 입장devno = MKDEV(major,minor);
// 장치 번호 생성register_chrdev(major, module_name,&pin4_fops);
//드라이버를 등록하고 위에서 준비한 구조를 커널 드라이버의 연결 목록에 추가하도록 커널에 지시합니다pin4_class=class_create(THIS_MODULE,"myfirstdemo");
//dev에서 코드로 장치를 자동으로 생성하고 클래스를 생성합니다pin4_class_dev =device_create(pin4_class,NULL,devno,NULL,module_name);
//디바이스 파일을 생성합니다. /dev
상위 레이어를 열 수 있는 추가 파일sudo mknod +设备名字 +设备类型(c表示字符设备驱动) +主设备号+次设备号
드라이브 모듈 코드 컴파일(모듈 컴파일에는 구성된 커널 소스 코드가 필요합니다. 컴파일 및 연결 후 생성된 커널 모듈의 접미사는 **.ko
입니다. 컴파일 프로세스는 먼저 커널 소스 코드 디렉터리로 이동하여 최상위 레벨을 읽습니다. Makefile 파일을 만든 다음 모듈 소스 코드가 있는 디렉터리로 돌아갑니다. **
#include //file_operations声明 #include //module_init module_exit声明 #include //__init __exit 宏定义声明 #include //class devise声明 #include //copy_from_user 的头文件 #include //设备号 dev_t 类型声明 #include //ioremap iounmap的头文件 static struct class *pin4_class; static struct device *pin4_class_dev; static dev_t devno; //设备号 static int major =231; //主设备号 static int minor =0; //次设备号 static char *module_name="pin4"; //模块名 //led_open函数 static int pin4_open(struct inode *inode,struct file *file) { printk("pin4_open\n"); //内核的打印函数和printf类似 return 0; } //read函数 static int pin4_read(struct file *file,char __user *buf,size_t count,loff_t *ppos) { printk("pin4_read\n"); //内核的打印函数和printf类似 return 0; } //led_write函数 static ssize_t pin4_write(struct file *file,const char __user *buf,size_t count, loff_t *ppos) { printk("pin4_write\n"); //内核的打印函数和printf类似 return 0; } static struct file_operations pin4_fops = { .owner = THIS_MODULE, .open = pin4_open, .write = pin4_write, .read = pin4_read, }; //static限定这个结构体的作用,仅仅只在这个文件。 int __init pin4_drv_init(void) //真实的驱动入口 { int ret; devno = MKDEV(major,minor); //创建设备号 ret = register_chrdev(major, module_name,&pin4_fops); //注册驱动 告诉内核,把这个驱动加入到内核驱动的链表中 pin4_class=class_create(THIS_MODULE,"myfirstdemo");//让代码在dev下自动>生成设备 pin4_class_dev =device_create(pin4_class,NULL,devno,NULL,module_name); //创建设备文件 return 0; } void __exit pin4_drv_exit(void) { device_destroy(pin4_class,devno); class_destroy(pin4_class); unregister_chrdev(major, module_name); //卸载驱动 } module_init(pin4_drv_init); //入口,内核加载驱动的时候,这个宏会被调用,去调用pin4_drv_init这个函数 module_exit(pin4_drv_exit); MODULE_LICENSE("GPL v2");
/SYSTEM/linux-rpi-4.19.y/drivers/char
将以上代码复制到一个文件中,然后下一步要做的是就是:将上面的驱动代码编译生成模块,再修改Makefile。(你放那个文件下,就改哪个文件下的Makefile)obj-m += pin4drive.o
添加到Makefile中即可。下图:Makefile文件图
.ko
文件发送给树莓派**然后回/SYSTEM/linux-rpi-4.19.y
下使用指令:ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- KERNEL=kernel7 make modules
进行编译生成驱动模块。然后将生成的.ko
文件发送给树莓派:scp drivers/char/pin4driver.ko pi@192.168.0.104:/home/pi
编译生成驱动模块会生成以下几个文件:
.o
的文件是object文件,.ko
是kernel object,与.o的区别在于其多了一些sections,比如.modinfo
。.modinfo section
是由kernel source里的modpost工具生成的, 包括MODULE_AUTHOR, MODULE_DESCRIPTION, MODULE_LICENSE, device ID table以及模块依赖关系等等。depmod 工具根据.modinfo section生成modules.dep, modules.*map等文件,以便modprobe更方便的加载模块。“
- 컴파일 과정에서 우리는 다음 단계를 거쳤습니다:
- 먼저 Linux 커널이 있는 디렉터리로 들어가서 pin4drive.o 파일을 컴파일하세요
- MODPOST를 실행하면 임시 pin4drive.mod.c 파일이 생성된 다음 이 파일을 기반으로 pin4drive.mod.o를 컴파일합니다.
- 그런 다음 pin4drive.o 및 pin4drive.mod.o 파일을 연결하여 모듈 대상 파일 pin4drive.ko를 가져옵니다.
- 마지막으로 Linux 커널이 있는 디렉터리를 그대로 둡니다.
”
크로스 컴파일 pin4test.c(상위 계층 호출 코드)를 실행하여 Raspberry Pi로 전송하면 아래 그림과 같이 pi 디렉터리 아래에 두 개의 파일이 전송되는 것을 볼 수 있습니다. .ko文件
和pin4test
sudo insmod pin4drive.ko
加载内核驱动(相当于通过insmod调用了module_init这个宏,然后将整个结构体加载到驱动链表中) 加载完成后就可以在dev
下面看到名字为pin4
lsmod
sudo chmod 666 /dev/pin4
pin4test
표면적으로는 정보 출력이 없습니다. 실제로 커널에 인쇄 정보가 있지만 상위 계층에서는 볼 수 없습니다. 커널에서 인쇄된 정보를 보려면 , 다음 명령을 사용할 수 있습니다. dmesg |grep pin4
. 아래 그림과 같이 드라이버 호출이 성공했음을 의미합니다pin4test
表面上看没有任何信息输出,其实内核里面有打印信息只是上层看不到如果想要查看内核打印的信息可以使用指令:dmesg |grep pin4
드라이버를 설치한 후 다음 명령을 사용하여
생성된 드라이버 모듈을 가상머신에서 생성해야 하는 이유는 무엇인가요? 라즈베리파이는 작동하지 않나요?
드라이버 모듈을 생성하려면 컴파일 환경이 필요합니다(Linux 소스 코드 및 컴파일, 시스템 버전과 동일한 Linux 커널 소스 코드를 다운로드해야 함). Raspberry Pi에서도 컴파일할 수 있지만 컴파일은 Raspberry Pi는 매우 비효율적이며 매우 오랜 시간이 걸립니다 . 이 문서에서는 Raspberry Pi 드라이버의 로컬 컴파일에 대해 설명합니다.
위 내용은 Linux 중급 - '드라이버' 하드웨어 제어를 위해 배워야 하는 낮은 수준의 지식의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!