리눅스 디바이스 모델(9)_디바이스 리소스 관리
1. 소개
모든 Linux 드라이버 엔지니어는 시간을 내어 이 문서를 읽어 보는 것이 좋습니다.
이 기사에서 소개된 주제는 매우 실용적이고 일부 혼란에 답할 수 있으며 코드를 간단하고 간결하게 만들 수도 있습니다. 먼저 예를 살펴보겠습니다.
으아아아Linux 드라이버를 작성한 모든 엔지니어는 프로브 기능에서 비슷한 문제에 직면했다고 생각합니다. 여러 리소스(IRQ, 시계, 메모리, 영역, ioremap, DMA 등)를 특정 순서로 적용해야 합니다. 그 중 하나 리소스 적용이 실패하면 이전에 적용한 모든 리소스를 롤백하고 해제해야 합니다. 따라서 일반적으로 함수 끝에는 많은 goto 레이블(예:exit_free_irq,exit_free_dma 등)이 있으므로 리소스 적용이 실패할 경우 올바른 레이블로 조심스럽게 점프하여 적용된 리소스를 해제할 수 있습니다.
그러나 이러한 코드는 반복되는 코드의 큰 섹션으로 가득 차 있으며 "if (조건) { err = xxx; goto xxx; }" 논리를 많이 포함하고 있습니다. 이는 시간 낭비일 뿐만 아니라 오류가 발생하기 쉬울 뿐만 아니라 보기 흉한. 그러나 혼란스럽다면 개선의 여지가 있을 것입니다. 궁극적으로 Linux 장치 모델은 장치 리소스 관리를 통해 이 문제를 해결합니다. 드라이버는 리소스를 신청하기만 하면 되며 리소스를 해제하는 방법에 대해 걱정할 필요가 없습니다. 궁극적으로 다음을 통해 드라이버 코드를 단순화할 수 있습니다.
으아아아어떻게 하나요? 위에서 "devm_"으로 시작하는 인터페이스에 주목하세요. 답이 거기에 있습니다. 기존 리소스 애플리케이션 인터페이스를 사용하지 말고 대신 devm_xxx 인터페이스를 사용하세요. 호환성을 유지하기 위해 이러한 새 인터페이스의 매개변수는 이름 앞에 "devm_"이 추가되고 추가적인 구조체 장치 포인터가 추가된다는 점을 제외하면 이전 인터페이스의 매개변수와 동일하게 유지됩니다.
2.devm_xxx
다음은 장치 리소스 관리를 기반으로 하는 다양한 프레임워크(예: 시계, 조정기, gpio 등)로 구현되는 일반적으로 사용되는 리소스 응용 프로그램 인터페이스 목록입니다. 사용할 때 "devm_" 접두사를 무시하면 나머지 부분은 드라이버 엔지니어에게 친숙할 것입니다. 한 가지만 기억하세요. 드라이버는 적용만 가능하고 릴리스할 수는 없으며 장치 모델이 드라이버를 릴리스하는 데 도움이 됩니다. 그러나 엄격하게 하고 싶다면 드라이버가 제거될 때 적극적으로 해제할 수 있습니다(여기에 나열되지 않은 해당 인터페이스도 있습니다).
으아아아3. "장치 리소스"란 무엇입니까
장치가 작동하려면 전원 공급 장치, 시계 등 다양한 외부 조건에 따라 달라집니다. 이러한 외부 조건을 장치 리소스라고 합니다. 최신 컴퓨터 아키텍처의 경우 가능한 리소스는 다음과 같습니다.
“
a) 전원, 전원 공급 장치.
b) 시계, 시계.
c) 메모리, 메모리는 일반적으로 커널에서 kzalloc을 사용하여 할당됩니다.
d) GPIO, 사용자 및 CPU는 간단한 제어, 상태 및 기타 정보를 교환합니다.
e) IRQ는 인터럽트를 트리거합니다.
f) DMA, CPU 참여 없이 데이터 전송.
g) 가상 주소 공간은 일반적으로 ioremap, request_region 등을 사용하여 할당됩니다.
h) 잠깐”
Linux 커널의 관점에서 "리소스"의 정의는 PWM, RTC, Reset과 같이 더 광범위하며, 이는 모두 드라이버에서 사용할 리소스로 추상화될 수 있습니다.
이전 커널에서는 시스템이 특별히 복잡하지 않았고 각 프레임워크가 아직 구체화되지 않았기 때문에 대부분의 리소스는 드라이버 자체에서 유지 관리되었습니다. 그러나 시스템의 복잡성이 증가함에 따라 드라이버 간에 점점 더 많은 리소스가 공유되고, 전력 관리의 필요성이 더욱 시급해집니다. 따라서 커널은 "장치 리소스 관리" 프레임워크를 기반으로 각 리소스의 관리 권한을 되찾아 할당 및 재활용을 포함하여 이를 균일하게 관리합니다.
4. 장치 리소스 관리를 위한 소프트웨어 프레임워크
장치 리소스 관리는 "drivers/base/devres.c"에 있으며 구현이 매우 간단합니다. 이유는 무엇입니까? 자원의 종류와 표현의 형태가 다양하고, 개발자가 이를 모두 알 수 없기 때문에 구체적인 할당과 재활용을 수행할 수 없습니다. 따라서 개발자가 할 수 있는 작업(및 유일한 기능)은 다음과 같습니다.
“
提供一种机制,将系统中某个设备的所有资源,以链表的形式,组织起来,以便在driver detach的时候,自动释放。
”
而更为具体的事情,如怎么抽象某一种设备,则由上层的framework负责。这些framework包括:regulator framework(管理power资源),clock framework(管理clock资源),interrupt framework(管理中断资源)、gpio framework(管理gpio资源),pwm framework(管理PWM),等等。
其它的driver,位于这些framework之上,使用它们提供的机制和接口,开发起来就非常方便了。
5. 代码分析
5.1 数据结构
先从struct device开始吧!该结构中有一个名称为“devres_head”的链表头,用于保存该设备申请的所有资源,如下:
1: struct device { 2: ... 3: spinlock_t devres_lock; 4: struct list_head devres_head; 5: ... 6: } 7:
那资源的数据结构呢?在“drivers/base/devres.c”中,名称为struct devres,如下:
1: struct devres { 2: struct devres_node node; 3: /* -- 3 pointers */ 4: unsigned long long data[]; /* guarantee ull alignment */ 5: };
咋一看非常简单,一个struct devres_node的变量node,一个零长度数组data,但其中有无穷奥妙,让我们继续分析。
node用于将devres组织起来,方便插入到device结构的devres_head链表中,因此一定也有一个list_head(见下面的entry)。另外,资源的存在形式到底是什么,device resource management并不知情,因此需要上层模块提供一个release的回调函数,用于release资源,如下:
1: struct devres_node { 2: struct list_head entry; 3: dr_release_t release; 4: #ifdef CONFIG_DEBUG_DEVRES 5: const char *name; 6: size_t size; 7: #endif 8: };
抛开用于debug的变量不说,也很简单,一个entry list_head,一个release回调函数。看不出怎么抽象资源啊!别急,奥妙都在data这个零长度数组上面呢。
注1:不知道您是否注意到,devres有关的数据结构,是在devres.c中定义的(是C文件哦!)。换句话说,是对其它模块透明的。这真是优雅的设计(尽量屏蔽细节)!
5.2 一个无关话题:零长度数组
零长度数组的英文原名为Arrays of Length Zero,是GNU C的规范,主要用途是用来作为结构体的最后一个成员,然后用它来访问此结构体对象之后的一段内存(通常是动态分配的内存)。什么意思呢?
以struct devres为例,node变量的长度为3个指针的长度,而struct devres的长度也是3个指针的长度。而data只是一个标记,当有人分配了大于3个指针长度的空间并把它转换为struct devres类型的变量后,我们就可以通过data来访问多出来的memory。也就是说,有了零长度数组data,struct devres结构的长度可以不定,完全依赖于你分配的空间的大小。有什么用呢?
以本文的应用场景为例,多出来的、可通过data访问的空间,正是具体的device resource所占的空间。资源的类型不同,占用的空间的多少也不同,但devres模块的主要功能又是释放资源所占的资源。这是就是零长度数组的功能之一,因为整个memory空间是连续的,因此可以通过释devres指针,释放所有的空间,包括data所指的那片不定长度的、具体资源所用的空间。
零长度数组(data[0]),在不同的C版本中,有不同的实现方案,包括1长度数组(data[1])和不定长度数组(data[],本文所描述就是这一种),具体可参考GCC的规范:
https://gcc.gnu.org/onlinedocs/gcc/Zero-Length.html
5.3 向上层framework提供的接口:devres_alloc/devres_free、devres_add/devres_remove
先看一个使用device resource management的例子(IRQ模块):
1: /* include/linux/interrupt.h */ 2: static inline int __must_check 3: devm_request_irq(struct device *dev, unsigned int irq, irq_handler_t handler, 4: unsigned long irqflags, const char *devname, void *dev_id) 5: { 6: return devm_request_threaded_irq(dev, irq, handler, NULL, irqflags, 7: devname, dev_id); 8: } 9: 10: 11: /* kernel/irq/devres.c */ 12: int devm_request_threaded_irq(struct device *dev, unsigned int irq, 13: irq_handler_t handler, irq_handler_t thread_fn, 14: unsigned long irqflags, const char *devname, 15: void *dev_id) 16: { 17: struct irq_devres *dr; 18: int rc; 19: 20: dr = devres_alloc(devm_irq_release, sizeof(struct irq_devres), 21: GFP_KERNEL); 22: if (!dr) 23: return -ENOMEM; 24: 25: rc = request_threaded_irq(irq, handler, thread_fn, irqflags, devname, 26: dev_id); 27: if (rc) { 28: devres_free(dr); 29: return rc; 30: } 31: 32: dr->irq = irq; 33: dr->dev_id = dev_id; 34: devres_add(dev, dr); 35: 36: return 0; 37: } 38: EXPORT_SYMBOL(devm_request_threaded_irq); 39: 40: void devm_free_irq(struct device *dev, unsigned int irq, void *dev_id) 41: { 42: struct irq_devres match_data = { irq, dev_id }; 43: 44: WARN_ON(devres_destroy(dev, devm_irq_release, devm_irq_match, 45: &match_data)); 46: free_irq(irq, dev_id); 47: } 48: EXPORT_SYMBOL(devm_free_irq);
前面我们提过,上层的IRQ framework,会提供两个和request_irq/free_irq基本兼容的接口,这两个接口的实现非常简单,就是在原有的实现之上,封装一层devres的操作,如要包括:
1)一个自定义的数据结构(struct irq_devres),用于保存和resource有关的信息(对中断来说,就是IRQ num),如下:
1: /* 2: * Device resource management aware IRQ request/free implementation. 3: */ 4: struct irq_devres { 5: unsigned int irq; 6: void *dev_id; 7: };
2)一个用于release resource的回调函数(这里的release,和memory无关,例如free IRQ),如下:
1: static void devm_irq_release(struct device *dev, void *res) 2: { 3: struct irq_devres *this = res; 4: 5: free_irq(this->irq, this->dev_id); 6: }
“
因为回调函数是由devres模块调用的,由它的参数可知,struct irq_devres变量就是实际的“资源”,但对devres而言,它并不知道该资源的实际形态,因而是void类型指针。也只有这样,devres模块才可以统一的处理所有类型的资源。
”
3)以回调函数、resource的size为参数,调用devres_alloc接口,为resource分配空间。该接口的定义如下:
1: void * devres_alloc(dr_release_t release, size_t size, gfp_t gfp) 2: { 3: struct devres *dr; 4: 5: dr = alloc_dr(release, size, gfp); 6: if (unlikely(!dr)) 7: return NULL; 8: return dr->data; 9: }
调用alloc_dr,分配一个struct devres类型的变量,并返回其中的data指针(5.2小节讲过了,data变量实际上是资源的代表)。alloc_dr的定义如下:
1: static __always_inline struct devres * alloc_dr(dr_release_t release, 2: size_t size, gfp_t gfp) 3: { 4: size_t tot_size = sizeof(struct devres) + size; 5: struct devres *dr; 6: 7: dr = kmalloc_track_caller(tot_size, gfp); 8: if (unlikely(!dr)) 9: return NULL; 10: 11: memset(dr, 0, tot_size); 12: INIT_LIST_HEAD(&dr->node.entry); 13: dr->node.release = release; 14: return dr; 15: }
看第一句就可以了,在资源size之前,加一个struct devres的size,就是total分配的空间。除去struct devres的,就是资源的(由data指针访问)。之后是初始化struct devres变量的node。
4)调用原来的中断注册接口(这里是request_threaded_irq),注册中断。该步骤和device resource management无关。
5)注册成功后,以设备指针(dev)和资源指针(dr)为参数,调用devres_add,将资源添加到设备的资源链表头(devres_head)中,该接口定义如下:
1: void devres_add(struct device *dev, void *res) 2: { 3: struct devres *dr = container_of(res, struct devres, data); 4: unsigned long flags; 5: 6: spin_lock_irqsave(&dev->devres_lock, flags); 7: add_dr(dev, &dr->node); 8: spin_unlock_irqrestore(&dev->devres_lock, flags); 9: }
从资源指针中,取出完整的struct devres指针,调用add_dr接口。add_dr也很简单,把struct devres指针挂到设备的devres_head中即可:
1: static void add_dr(struct device *dev, struct devres_node *node) 2: { 3: devres_log(dev, node, "ADD"); 4: BUG_ON(!list_empty(&node->entry)); 5: list_add_tail(&node->entry, &dev->devres_head); 6: }
6)如果失败,可以通过devres_free接口释放资源占用的空间,devm_free_irq接口中,会调用devres_destroy接口,将devres从devres_head中移除,并释放资源。这里就不详细描述了。
5.4 向设备模型提供的接口:devres_release_all
这里是重点,用于自动释放资源。
先回忆一下设备模型中probe的流程(可参考“Linux设备模型(5)_device和device driver”),devres_release_all接口被调用的时机有两个:
1)probe失败时,调用过程为(就不详细的贴代码了):
__driver_attach/__device_attach–>driver_probe_device—>really_probe,really_probe调用driver或者bus的probe接口,如果失败(返回值非零,可参考本文开头的例子),则会调用devres_release_all。
2)deriver dettach时(就是driver remove时)
driver_detach/bus_remove_device–>__device_release_driver–>devres_release_all
devres_release_all的实现如下:
1: int devres_release_all(struct device *dev) 2: { 3: unsigned long flags; 4: 5: /* Looks like an uninitialized device structure */ 6: if (WARN_ON(dev->devres_head.next == NULL)) 7: return -ENODEV; 8: spin_lock_irqsave(&dev->devres_lock, flags); 9: return release_nodes(dev, dev->devres_head.next, &dev->devres_head, 10: flags); 11: }
以设备指针为参数,直接调用release_nodes:
1: static int release_nodes(struct device *dev, struct list_head *first, 2: struct list_head *end, unsigned long flags) 3: __releases(&dev->devres_lock) 4: { 5: LIST_HEAD(todo); 6: int cnt; 7: struct devres *dr, *tmp; 8: 9: cnt = remove_nodes(dev, first, end, &todo); 10: 11: spin_unlock_irqrestore(&dev->devres_lock, flags); 12: 13: /* Release. Note that both devres and devres_group are 14: * handled as devres in the following loop. This is safe. 15: */ 16: list_for_each_entry_safe_reverse(dr, tmp, &todo, node.entry) { 17: devres_log(dev, &dr->node, "REL"); 18: dr->node.release(dev, dr->data); 19: kfree(dr); 20: } 21: 22: return cnt; 23: }
release_nodes会先调用remove_nodes,将设备所有的struct devres指针从设备的devres_head中移除。然后,调用所有资源的release回调函数(如5.3小节描述的devm_irq_release),回调函数会回收具体的资源(如free_irq)。最后,调用free,释放devres以及资源所占的空间。
위 내용은 리눅스 디바이스 모델(9)_디바이스 리소스 관리의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

인터넷은 단일 운영 체제에 의존하지 않지만 Linux는 이에 중요한 역할을합니다. Linux는 서버 및 네트워크 장치에서 널리 사용되며 안정성, 보안 및 확장 성으로 인기가 있습니다.

Linux 운영 체제의 핵심은 명령 줄 인터페이스이며 명령 줄을 통해 다양한 작업을 수행 할 수 있습니다. 1. 파일 및 디렉토리 작업 LS, CD, MKDIR, RM 및 기타 명령을 사용하여 파일 및 디렉토리를 관리합니다. 2. 사용자 및 권한 관리는 UserAdd, Passwd, CHMOD 및 기타 명령을 통해 시스템 보안 및 리소스 할당을 보장합니다. 3. 프로세스 관리는 PS, Kill 및 기타 명령을 사용하여 시스템 프로세스를 모니터링하고 제어합니다. 4. 네트워크 운영에는 Ping, Ifconfig, SSH 및 기타 명령이 포함되어 있으며 네트워크 연결을 구성하고 관리합니다. 5. 시스템 모니터링 및 유지 관리 Top, DF, Du와 같은 명령을 사용하여 시스템의 작동 상태 및 리소스 사용을 이해합니다.

소개 Linux는 유연성과 효율성으로 인해 개발자, 시스템 관리자 및 전원 사용자가 선호하는 강력한 운영 체제입니다. 그러나 길고 복잡한 명령을 자주 사용하는 것은 지루하고 응급실이 될 수 있습니다.

Linux는 서버, 개발 환경 및 임베디드 시스템에 적합합니다. 1. 서버 운영 체제로서 Linux는 안정적이고 효율적이며 종종 고 대전성 애플리케이션을 배포하는 데 사용됩니다. 2. 개발 환경으로서 Linux는 효율적인 명령 줄 도구 및 패키지 관리 시스템을 제공하여 개발 효율성을 향상시킵니다. 3. 임베디드 시스템에서 Linux는 가볍고 사용자 정의 가능하며 자원이 제한된 환경에 적합합니다.

소개 : Linux 기반의 윤리적 해킹으로 디지털 프론티어 보안 점점 더 상호 연결된 세상에서 사이버 보안이 가장 중요합니다. 윤리적 해킹 및 침투 테스트는 취약점을 적극적으로 식별하고 완화하는 데 필수적입니다.

기본 Linux 학습 방법은 다음과 같습니다. 1. 파일 시스템 및 명령 줄 인터페이스 이해, 2. LS, CD, MKDIR, 3. 파일 생성 및 편집과 같은 파일 작업 배우기, 4. 파이프 라인 및 GREP 명령과 같은 고급 사용법, 5. 연습 및 탐색을 통해 지속적으로 기술을 향상시킵니다.

Linux는 서버, 임베디드 시스템 및 데스크탑 환경에서 널리 사용됩니다. 1) 서버 필드에서 Linux는 안정성 및 보안으로 인해 웹 사이트, 데이터베이스 및 응용 프로그램을 호스팅하기에 이상적인 선택이되었습니다. 2) 임베디드 시스템에서 Linux는 높은 사용자 정의 및 효율성으로 인기가 있습니다. 3) 데스크탑 환경에서 Linux는 다양한 사용자의 요구를 충족시키기 위해 다양한 데스크탑 환경을 제공합니다.

Linux의 단점에는 사용자 경험, 소프트웨어 호환성, 하드웨어 지원 및 학습 곡선이 포함됩니다. 1. 사용자 경험은 Windows 또는 MacOS만큼 친절하지 않으며 명령 줄 인터페이스에 의존합니다. 2. 소프트웨어 호환성은 다른 시스템만큼 좋지 않으며 많은 상용 소프트웨어의 기본 버전이 부족합니다. 3. 하드웨어 지원은 Windows만큼 포괄적이지 않으며 드라이버를 수동으로 컴파일 할 수 있습니다. 4. 학습 곡선은 가파르고 명령 줄 운영을 마스터하는 데 시간과 인내가 필요합니다.


핫 AI 도구

Undresser.AI Undress
사실적인 누드 사진을 만들기 위한 AI 기반 앱

AI Clothes Remover
사진에서 옷을 제거하는 온라인 AI 도구입니다.

Undress AI Tool
무료로 이미지를 벗다

Clothoff.io
AI 옷 제거제

AI Hentai Generator
AI Hentai를 무료로 생성하십시오.

인기 기사

뜨거운 도구

ZendStudio 13.5.1 맥
강력한 PHP 통합 개발 환경

SublimeText3 영어 버전
권장 사항: Win 버전, 코드 프롬프트 지원!

DVWA
DVWA(Damn Vulnerable Web App)는 매우 취약한 PHP/MySQL 웹 애플리케이션입니다. 주요 목표는 보안 전문가가 법적 환경에서 자신의 기술과 도구를 테스트하고, 웹 개발자가 웹 응용 프로그램 보안 프로세스를 더 잘 이해할 수 있도록 돕고, 교사/학생이 교실 환경 웹 응용 프로그램에서 가르치고 배울 수 있도록 돕는 것입니다. 보안. DVWA의 목표는 다양한 난이도의 간단하고 간단한 인터페이스를 통해 가장 일반적인 웹 취약점 중 일부를 연습하는 것입니다. 이 소프트웨어는

SublimeText3 중국어 버전
중국어 버전, 사용하기 매우 쉽습니다.

에디트플러스 중국어 크랙 버전
작은 크기, 구문 강조, 코드 프롬프트 기능을 지원하지 않음
