linux内存管理系统相关(国嵌教材整理)

内存地址相关概念:

内存地址类型可以分为物理地址、线性地址(虚拟地址)、逻辑地址。

物理地址:物理地址是指出现在CPU地址总线上的寻址物理内存的地址信号,是地址变换的最终结果。

虚拟(线性)地址:在32位CPU架构下,4G的地址空间为0-0xFFFFFFFF。

逻辑地址:程序代码经过编译后在汇编程序中使用的地址(如属于.CSEG下ROM存储的地址或者属于.DSEG下的RAM存储地址,)。


逻辑地址转换成线性地址需要段式内存管理单元。

线性地址转换成物理地址需要页式内存管理单元。

段式管理(16位CPU):

16位CPU有20位的地址线,即1M的寻址空间。所以需要另外的寄存器即段寄存器,所以把1M的空间分为若干的逻辑段,很显然一段不能超过64K,因为2^16=65536=64K。

16位CPU有四个段寄存器,程序可以同时访问四个不同含义的段:

CS+IP: 用于代码段的访问,CS 指向存放程序的段基址,IP指向下条要执行的指令在CS段的偏移量,用这两个寄存器就可以得到一个内存物理地址,该地址存放着一条要执行的指令。

SS+SP:用于堆栈段的访问,SS指向堆栈段的基地址,SP指向栈顶,可以通过SS和SP两个寄存器直接访问栈顶单元的内存物理位置。

DS+BX:用于数据段的访问。 DS中的值左移四位得到数据段起始地址,再加上BX中的偏移量,得到一个存储单元的物理地址。

ES+BX:用于附加段的访问。 ES中的值左移四位得到附加段起始地址,再加上BX中的偏移量,得到一个存储单元的物理地址。

段式管理即将分配到的虚拟内存分成很多段。

32位CPU工作模式有:实模式和保护模式。其中实模式32位和16位CPU内存管理是一致的。

在保护模式下,每个段的最大容量可以达到4G,段寄存器的值是段地址的“选择器”(selector),用该选择器中内存中得到一个32位的段地址,存储单元的物理地址就是该段地址加上段内偏移量。(只是增加了选择器的概念,原理相同)。

物理页,理论上有4G但是实际上只装了2G,这样对物理地址进行划分。

LINUX内核的涉及并没有全部采用Intel所提供的段机制,仅仅是有限度地使用了分段机制,这不仅简化了Linux内核的设计,而且为把Linux移植到其他平台创造了条件,因为很多RISC处理器并不支持段机制。

这是因为所有段的基地址都是为0,由此可以得出,每个段的逻辑地址空间范围为0-4GB。因为每个段的基地址为0(这就相当于根本没分段= =),因此,逻辑地址与线性地址保持一致(即逻辑地址的偏移量字段的值与线性地址的值总是相同的),在Linux中所提到的逻辑地址和线性地址(虚拟地址),可以认为是一致的。看来,Linux巧妙地把段机制给绕过去了,而完全利用了分页机制。

例,LINUX2.6中物理地址转换成页地址如 0x56000000>> PAGE_SHIFT,右移13位

关于逻辑、线性、物理地址详细

Linux进程地址空间:

Linux操作系统采用虚拟内存管理技术,使得每个进程都有独立的进程地址空间,该空间是大小为3G,用户看到和接触的都是虚拟地址,无法看到实际的物理地址。利用这种虚拟地址不但能起到保护操作系统的作用,而且更重要的是用户程序可使用比实际物理内存更大的地址空间。
Linux将4G的虚拟地址空间划分为两个部分——用户空间与内核空间。用户空间从0到0xbfffffff,内核空间从3G到4G。用户进程通常情况下只能访问用户空间的虚拟地址,不能访问内核空间。
例外情况是用户进程通过系统调用访问内核空间。
用户空间对应进程,所以每当进程切换,用户空间就会跟着变化。
每个进程的用户空间都是完全独立、互不相干的。把同一个程序同时运行10次(为了能同时运行,让它们在返回前睡眠100秒),会看到10个进程使用的线性地址一模一样。(cat/proc/<pid>/maps )
创建进程fork()、程序载入execve()、动态内存分配malloc()等进程相关操作都需要分配内存给进程。这时进程申请和获得的不是物理地址,仅仅是虚拟地址。
实际的物理内存只有当进程真的去访问新获取的虚拟地址时,才会由“请页机制”产生“缺页”异常,从而进入分配实际页框的程序。该异常是虚拟内存机制赖以存在的基本保证——它会告诉内核去为进程分配物理页,并建立对应的页表,这之后虚拟地址才实实在在地映射到了物理地址上。
在应用程序中,常使用malloc函数进行动态内存分配,而在Linux内核中,通常使用kmalloc来动态分配内存。
kmalloc 原型是:
#include <linux/slab.h>
void *kmalloc(size_t size,int flags)
参数:
size:要分配的内存大小。
flags:分配标志, 它控制 kmalloc 的行为。
分配标志
最常用的标志是GFP_KERNEL,它的意思是该内存分配是由运行在内核态的进程调用的。也就是说,调用它的函数属于某个进程的,当空闲内存太少时,kmalloc函数会使当前进程进入睡眠,等待空闲页的出现。
分配标志
如果kmalloc是在进程上下文之外调用,比如在中断处理,任务队列处理和内核定时器处理中。这些情况属于中断上下文,不能进入睡眠,这时应该使用优先权GFP_ATOMIC。
分配标志
GFP_ATOMIC
用来在进程上下文之外的代码(包括中断处理)中分配内存,从不睡眠。
GFP_KERNEL
进程上下文中的分配。可能睡眠。(16M-896M)
__GFP_DMA
这个标志要求分配能够 DMA 的内存区(物理地址在16M以下的页帧 )
__GFP_HIGHMEM
这个标志表示分配的内存位于高端内存。(896M以上)
按页分配
如果模块需要分配大块的内存,那使用面向页的分配技术会更好
get_zeroed_page(unsigned int flags)返回指向新页面的指针,并将页面清零。
__get_free_page(unsigned int flags)和get_free_page类似,但不清零页面。
__get_free_pages(unsigned int flags,unsigned int order)分配若干个连续的页面,返回指向该内存区域的指针,但也不清零这段内存区域。

释放
当程序用完这些页, 可以使用下列函数之一来释放它们:
void free_page(unsigned long addr)
void free_pages(unsignedlongaddr, unsigned long order)
**如果释放的和先前分配数目不等的页面,会导致系统错误**


Linux内核地址空间:

进程地址空间是在改变的,内核空间是有内核负责映射,他并不会由进程而改变,是固定的。
高端内存:物理内存896MB以上的部分称之为高端内存。

内核空间分布:直接映射区、动态映射区(Vmalloc Rgion)、KMAP区(永久内存映射区 PKMap Region)、固定映射区


直接内存映射区(Direct Memory Region):

从3G开始,最大869M的线性地址区间,我们称作直接映射区,这是因为该区域的线性地址和物理地址之间存在线性转换关系:线性地址=3G + 物理地址 

例:
物理地址区间0x100000-0x200000映射到线性空间就是3G+0x100000-3G+0x200000。引用小于0xC0000000即3G之前的地址会报错。

动态内存映射区(Vmalloc Region):
该区域的地址由内核函数vmalloc来进行分配,其特点是线性空间连续,但对应的物理空间不一定连续。vmalloc分配的线性地址所对应的物理页可能处于低端内存,也可能处于高端内存。

永久内存映射区(PKMap Region ):
对于896MB以上的高端内存,可使用该区域来访问,访问方法:

1、使用alloc_page(__GFP_HIGHMEM)分配高端内存页;

2、使用kmap函数将分配到的高端内存映射到该区域。

固定映射区(fixing mapping region):

PKMap区上面,有4M的线性空间,被称作固定映射区,它和4G顶端只有4K的隔离带。固定映射区中每个地指向都服务于特定的用途,如ACPI_BASE等。

Linux内核链表:

在LINUX内核中使用了大量的链表结构来组织数据。这些链表大多采用了 include/linux/list.h 中实现的一套精彩的链表数据结构。

链表数据结构的定义:
struct list_head 

struct list_head *next, *prev; 
}; 
list_head结构包含两个指向list_head结构的指针prev和next,由此可见,内核的链表具备双链表功能,实际上,通常它都组织成双向循环链表。

链表操作
Linux内核中提供的链表操作主要有:

初始化链表头
INIT_LIST_HEAD(list_head *head)
插入节点

list_add(struct list_head *new, struct list_head *head)
list_add_tail(struct list_head *new, struct list_head *head)

删除节点

list_del(struct list_head *entry)
提取数据结构
list_entry(ptr, type, member)
已知数据结构中的节点指针ptr,找出数据结构,例:list_entry(aup, struct autofs,list)

遍历
list_for_each(struc list_head *pos, struc list_head *head)
例:
struct list_head *entry;
struct list_head cs46xx_devs;//链表头
list_for_each(entry, &cs46xx_devs)
{
card =list_entry(entry, struct cs_card, list);
if (card->dev_midi == minor)
break;
}

Linux内核定时器:

时钟中断由系统的定时硬件以周期性的时间间隔产生,这个间隔(即频率)由内核根据HZ来确定,HZ是一个与体系结
构无关的常数,可配置(50-1200),在X86平台,默认值为1000。
每当时钟中断发生时,全局变量jiffies(unsigned long)就加1,因此jiffies记录了自linux启动后时钟中断发生的次数。驱动程序常利用jiffies来计算不同事件间的时间间隔。
延迟执行
如果对延迟的精度要求不高,最简单的实现方法如下--忙等待:
unsignedlongj=jiffies + jit_delay*HZ;
while (jiffies<j)

/* do nothing */
}
内核定时器
定时器用于控制某个函数(定时器处理函数)在未来的某个特定时间执行。内核定时器注册的处理函数只执行一次--不是循环执行的。
内核定时器被组织成双向链表,并使用struct timer_list结构描述。
struct timer_list{
	struct list_head entry/*内核使用*/;
	unsigned long expires; /*超时的jiffies值*/
	void (*function)(unsigned long); /*超时处理函数*/
	unsigned long data; /*超时处理函数参数*/
	struct tvec_base *base;/*内核使用*/
};
操作定时器的有如下函数:
void init_timer(struct timer_list *timer);初始化定时器队列结构。
void add_timer(struct timer_list * timer);启动定时器。
int del_timer(struct timer_list *timer);在定时器超时前将它删除。当定时器超时后,系统会自动地将它删除。

Linux系统调用:

定义
Linux内核中设置了一组用于实现各种系统功能的子程序,称为系统调用。用户可以通过系统调用命令在自己的应用程序中调用它们。

区别
系统调用和普通的函数调用非常相似,区别仅仅在于,系统调用由操作系统内核实现,运行于内核态;而普通的函数调用由函数库或用户自己提供,运行于用户态。

库函数
Linux系统还提供了一些C语言函数库,这些库对系统调用进行了一些包装和扩展,这些库函数与系统调用的关系非常紧密。

系统调用数
在2.6.29 内核中,共有系统调用332个,可在arch/arm/include/asm/unistd.h中找到它们。

系统调用手册
各系统调用的功能参考

使用系统调用

#include<time.h>
main()
{
	time_t the_time;
	the_time=time((time_t *)0);/*调用time系统调用*/
	printf("The time is %ld\n",the_time);
}


/* 从格林尼治时间1970年1月1日0:00开始到现在的秒数。 */

工作原理
一般情况下,用户进程是不能访问内核的。它既不能访问内核所在的内存空间,也不能调用内核中的函数。系统调用是一个例外。其原理是进程先用适当的值填充寄存器,然后调用一个特殊的指令,这个指令会让用户程序跳转到一个事先定义好的内核中的一个位置:
在Intel CPU中,这个指令由中断0x80实现。
在ARM中,这个指令是SWI。

工作原理
进程可以跳转到的内核位置是ENTRY(vector_swi) <entry-common.S>。这个过程检查系统调用号,这个号码告诉内核进程请求哪种服务。然后,它查看系统调用表(sys_call_table)找到所调用的内核函数入口地址。接着,就调用函数,等返回后,做一些系统检查,最后返回到进程。

工作原理(应用)
#define __syscall(name)   "swi\t" __NR_##name "\n\t“
int open( const char * pathname, int flags)
{
。。。。。。
__syscall(open);
。。。。。。
}
转化为
int open( const char * pathname, int flags)
{
。。。。。。
swi\t  __NR_open 
。。。。。。
}

工作原理(内核入口)
/* arch/arm/kernel/entry-common.S */
ENTRY(vector_swi)
…… …… …… ……
adr tbl, sys_call_table @ load syscall table pointer
…… …… …… ……
ldrcc  pc, [tbl, scno, lsl #2] @ call sys_* routine
…… …… …… ……
ENTRY(sys_call_table)
#include "calls.S"

工作原理(内核入口)
/* arch/arm/kernel/calls.S */

/* 0 */ 

CALL(sys_restart_syscall)

CALL(sys_exit)
CALL(sys_fork_wrapper)
CALL(sys_read)
CALL(sys_write)
/* 5 */ CALL(sys_open)
…… …… …… …… …… …… …… …… …… …… …… ……
CALL(sys_dup3)
CALL(sys_pipe2)
/* 360 */ CALL(sys_inotify_init1)

实现系统调用
向内核中添加新的系统调用,需要执行 3 个步骤:
1. 添加新的内核函数
2. 更新头文件 unistd.h
3. 针对这个新函数更新系统调用表calls.S

实现系统调用
1. 在kernel/sys.c中添加函数:
asmlinkage int sysMul(int a, int b)
{
intc;
c =a*b;
returnc;
}
/* asmlinkage:使用栈传递参数 */

2. 在arch/arm/include/asm/unistd.h中添加如下代码:
#define __NR_sysMul 361

3.在arch/arm/kernel/calls.S中添加代码,指向新实现的系统调用函数:
CALL(sysMul)

使用系统调用
#include <stdio.h>
#include <linux/unistd.h>
main()
{
int result;
result = syscall(361,1, 2);
printf("result = ", result);
}

实验
系统调用实现
1.修改内核,实现一个用于加法运算的系统调用
2.实现应用程序,使用该系统调用

Proc文件系统:

什么是proc文件系统?

实例:通过 /proc/meminfo,查询当前内存使用情况。

proc文件系统是内核和用户进行交互的一种机制。用户查看内核的一些状态。http://man.chinaunix.net/linux/mandrake/101/zh_cn/Command-Line.html/index.html

子目录/文件名内容描述
apm高级电源管理信息
bus总线及总线上的设备
devices可用的设备信息
driver已经启动的驱动程序
interrupts中断信息
ioports端口使用信息
version内核版本
特点
每个文件都规定了严格的权限,可读?可写?哪个用户可读?哪个用户可写?
可以用文本编辑程序读取(more命令,cat命令,vi 程序等等)
不仅可以有文件,还可以有子目录。
可以自己编写程序添加一个/proc目录下的文件。
文件的内容都是动态创建的,并不存在于磁盘上。

内核描述
struct proc_dir_entry{
{
。。 。。。。。。。。。。。。。。。。。
read_proc_t  *read_proc;
write_proc_t  *write_proc;
。。。。。。。。。。。。。。。。。。。
}

创建文件
struct proc_dir_entry* create_proc_entry (const char *name,mode_t mode,struct proc_dir_entry *parent)
功能:
创建proc文件
参数:
name:要创建的文件名
mode:要创建的文件的属性 默认0755
parent:这个文件的父目录

创建目录
struct proc_dir_entry * proc_mkdir (const char *name,struct proc_dir_entry *parent)
功能:
创建proc目录
参数:
name:要创建的目录名
parent:这个目录的父目录

删除目录/文件
void remove_proc_entry (const char *name,struct proc_dir_entry *parent)
功能:
删除proc目录或文件
参数:
name:要删除的文件或目录名
parent:所在的父目录

读写
为了能让用户读写添加的proc文件,需要
挂接上读写回调函数:
read_proc 
write_proc

读操作
int read_func (char *buffer,char**stat,off_t off,int count,int *peof,void *data)
参数:
buffer:把要返回给用户的信息写在buffer里,最大不超过PAGE_SIZE 

stat:一般不使用

off:偏移量
count:用户要取的字节数
peof:读到文件尾时,需要把*peof置1
data:一般不使用

写操作
int write_func (struct file*file,const char *buffer,unsigned long count,void*data)
参数:
file :该proc文件对应的file结构,一般忽略。
buffer :待写的数据所在的位置
count :待写数据的大小
data :一般不使用

实现流程
实现一个proc文件的流程:
(1)调用create_proc_entry创建一个struct proc_dir_entry。
(2)对创建的struct proc_dir_entry进行赋值:read_proc,mode,owner,size,write_proc 等等。

Linux内核异常:

定义
常在河边走,哪能不湿鞋。内核级的程序,总有死机的时候,如果运气好,会看到一些所谓“Oops”信息(在屏幕上或系统日志中),
比如:
Unable to handle kernel paging request at virtual address f899b670
printing eip:
c01de48c
*pde = 00737067
Oops: 0002 [#1]
Moduleslinked in: bluesmoke_e752x bluesmoke_mc md5ipv6 parport_pc 
lp parport nls_cp936 vfat fat dm_mod button battery asus_acpi ac joydev 
CPU:0
EIP: 0060:[] Not tainted VLI
EFLAGS: 00210286 (2.6.9-11.21AXKProbes)
EIP is at kobject_add+0x83/0xd7
。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。

定义
Oops 可以看成是内核级的Segmentation Fault。应用程序如果进行了非法内存访问或执行了非法指令,会得到Segfault信号,一般的行为是coredump,应用程序也可以自己截获Segfault信号,自行处理。如果内核自己犯了这样的错误,则会打出Oops信息。

分析步骤
1. 错误原因提示
2. 调用栈(对照反汇编代码)
3. 寄存器

实验
内核异常分析
编写内核模块,产生内核异常,根据OOPS分析
异常原因

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
国嵌嵌入式Linux视频教程全套2011新版,强烈推荐!!! 国嵌视频1.iso -学习方法与课程体系介绍(学前必看) -学习方法介绍.avi -国嵌嵌入式课程体系.pdf -嵌入式Linux学习方法.pdf -国嵌课程1-嵌入式入门体验班(上) -第1天(嵌入式系统概述) -国嵌体验入门班-1-1(嵌入式系统概述).avi -国嵌体验入门班-1-2(ARM概述).avi -国嵌体验入门班-1-3(嵌入式Linux概述).avi -国嵌体验入门班-1-4(2440开发板介绍).avi -国嵌体验入门班-1-5(软硬件环境搭建).avi -第2天(开发板快乐体验) -国嵌体验入门班-2-1(开发板系统安装).avi -国嵌体验入门班-2-1(开发板系统安装-Jlink方式).avi -国嵌体验入门班-2-1(开发板系统安装-并口方式).avi -国嵌体验入门班-2-2(裸机程序体验).avi -国嵌体验入门班-2-3(QT系统体验).avi -国嵌体验入门班-2-4(Android系统体验).avi 国嵌视频2.iso -国嵌课程1-嵌入式入门体验班(下) -第3天(Linux系统体验) -国嵌体验入门班-3-1(Linux定制安装).avi -国嵌体验入门班-3-2(Linux命令).avi -国嵌体验入门班-3-3(VI使用).avi -国嵌体验入门班-3-4(Linux系统管理).avi -国嵌体验入门班-3-5(Shell编程).avi -国嵌体验入门班-3-6(Qcd功能演示).avi -国嵌体验入门班-3-7(必修实验).avi -国嵌课程2-嵌入式Linux应用开发班 -第1天(编程基础) -国嵌应用班-1-1(GCC程序编译).avi -国嵌应用班-1-2(GDB程序调试).avi -国嵌应用班-1-3(makefile工程管理).avi -国嵌应用班-1-4(必修实验).avi -第2天(文件时间编程) -国嵌应用班-2-1(系统调用方式访问文件).avi -国嵌应用班-2-2(库函数访问文件).avi -国嵌应用班-2-3(时间编程).avi -国嵌应用班-2-4(必修实验).avi -第3天(多进程程序设计) -国嵌应用班-3-1(进程控制原理).avi -国嵌应用班-3-2(进程控制程序设计).avi -国嵌应用班-3-3(必修实验).avi -第4天(进程间通讯) -国嵌应用班-4-1(进程间通讯概述).avi -国嵌应用班-4-2(管道通讯).avi -国嵌应用班-4-3(信号通讯).avi -国嵌应用班-4-4(共享内存通讯).avi -国嵌应用班-4-5(必修实验).avi -第5天(进程间通讯) -国嵌应用班-5-1(消息队列).avi -国嵌应用班-5-2(信号量).avi -国嵌应用班-5-3(必修实验).avi -第6天(进程间通讯) -国嵌应用班-6-1(线程基础).avi -国嵌应用班-6-2(多线程程序设计).avi -国嵌应用班-6-3(必修实验).avi -第7天(网络编程) -国嵌应用班-7-1(Linux网络概述).avi -国嵌应用班-7-2(Linux网络编程基础).avi -国嵌应用班-7-3(TCP程序设计).avi -国嵌应用班-7-4(UDP网络程序设计).avi -国嵌应用班-7-5(并发服务器).avi -国嵌应用班-7-6(必修实验).avi 国嵌视频3.iso -国嵌课程3-ARM系统进阶班 -第1天(ARM系统开发基础) -ARM系统精讲班-1-1(ADS集成开发环境).avi -ARM系统精讲班-1-2(ARM工作模式).avi -ARM系统精讲班-1-3(ARM寄存器).avi -ARM系统精讲班-1-4(ARM寻址方式).avi -ARM系统精讲班-1-5(ARM汇编指令详解).avi -第2天(ARM系统开发基础) -ARM系统精讲班-2-1(ARM汇编指令详解).avi -ARM系统精讲班-2-2(ARM伪指令).avi -ARM系统精讲班-2-3((混合编程).avi -ARM系统精讲班-1-8(必修实验).avi -第3天(LED驱动程序设计) -ARM系统精讲班-2-1(GPIO).avi -ARM系统精讲班-2-2(LED控制程序设计).avi -ARM系统精讲班-2-3((LED闪烁).avi -第4天(LED与按键驱动程序设计) -ARM系统精讲班-4-1(ARM中断控制系统).avi -ARM系统精讲班-4-2(按键控制程序设计).avi -ARM系统精讲班-2-6(必修实验).avi -第5天(定时器与时钟驱动程序设计) -ARM系统精讲班-5-1(ARM时钟体系).avi -ARM系统精讲班-5-2(定时器驱动程序设计).avi -国嵌课程4(嵌入式LINUX内核驱动进阶班-上) -第1天(内核开发基础) -国嵌内核驱动进阶班-1-1(Linux内核简介).avi -国嵌内核驱动进阶班-1-2(Linux内核源代码).avi -国嵌内核驱动进阶班-1-3(Linux内核配置与编译).avi -国嵌内核驱动进阶班-1-4(Linux内核模块开发).avi -国嵌内核驱动进阶班-1-5(必修实验).avi -第2天(U-Boot移植) -国嵌内核驱动进阶班-2-1(嵌入式linux系统架构).avi -国嵌内核驱动进阶班-2-2(交叉工具链).avi -国嵌内核驱动进阶班-2-3(Bootloader介绍).avi -国嵌内核驱动进阶班-2-4(U-Boot介绍).avi -国嵌内核驱动进阶班-2-5(U-Boot命令).avi -国嵌内核驱动进阶班-2-6(U-Boot启动流程).avi -国嵌内核驱动进阶班-2-7(Uboot移植).avi -国嵌内核驱动进阶班-2-8(必修实验).avi 国嵌视频4.iso -国嵌课程4(嵌入式LINUX内核驱动进阶班-下) -第3天(内核开发基础) -国嵌内核驱动进阶班-3-1(嵌入式linux内核制作).avi -国嵌内核驱动进阶班-3-2(根文件系统制作).avi -国嵌内核驱动进阶班-3-3(内核启动流程).avi -国嵌内核驱动进阶班-3-4(嵌入式文件系统).avi -国嵌内核驱动进阶班-3-5(必修实验).avi -第4天(内存管理子系统) -国嵌内核驱动进阶班-4-1(Linux内存管理).avi -国嵌内核驱动进阶班-4-2(进程地址空间).avi -国嵌内核驱动进阶班-4-3(内核地址空间).avi -国嵌内核驱动进阶班-4-4(Linux内核链表).avi -国嵌内核驱动进阶班-4-5(Linux内核定时器).avi -国嵌内核驱动进阶班-2-7(Uboot移植).avi -国嵌内核驱动进阶班-2-8(必修实验).avi -第5天(进程管理子系统) -国嵌内核驱动进阶班-5-1(LINUX进程控制).avi -国嵌内核驱动进阶班-5-2(LINUX进程调度).avi -国嵌内核驱动进阶班-5-3(Linux系统调用).avi -国嵌内核驱动进阶班-5-4(Proc文件系统).avi -国嵌内核驱动进阶班-5-5(内核异常分析).avi -国嵌内核驱动进阶班-5-6(必修实验).avi -第6天(简单字符设备驱动程序设计) -国嵌内核驱动进阶班-6-1(LINUX驱动程序介绍).avi -国嵌内核驱动进阶班-6-2(字符设备驱动程序设计).avi -国嵌内核驱动进阶班-6-3(字符设备驱动程序实例分析).avi -国嵌内核驱动进阶班-6-4(竞争与互斥).avi -国嵌内核驱动进阶班-6-5(必修实验).avi -第7天(高级字符设备驱动程序设计) -国嵌内核驱动进阶班-7-1(Ioctl设备控制).avi -国嵌内核驱动进阶班-7-2(内核等待队列).avi -国嵌内核驱动进阶班-7-3(阻塞型字符设备驱动).avi -国嵌内核驱动进阶班-7-4(Poll设备方法).avi -国嵌内核驱动进阶班-7-5(自动创建设备文件).avi -国嵌内核驱动进阶班-7-6(必修实验).avi
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值