mmap设备操作:
void *mmap(void *addr, size_t len, int port, int flags, int fd, off_t offset);
内存映射函数,负责把文件内容映射到进程的虚拟内存空间,通过对这段内存的修改来实现对文件的修改。
addr:指定映射的起始地址,通常为NULL,由系统指定
len :文件中映射到内存的长度
port:
PORT_EXEC:映射区可被执行;
PORT_READ:映射区可被读取;
PORT_WRITE:映射区可被写入;
flags:
MAP_SHARED:写入映射区的数据会复制回文件,且允许其他映射该文件的进程共享
MAP_PRIVATE:对映射区的写入操作会产生一个映射区的复制(copy-on-write),
对此区域的修改不会写回原文件。
offset:
映射文件中的偏移量。
解除映射:
int munmap(void *start, size_t length);
注意:内存映射不会增加文件的原来大小。
虚拟内存区域:
一个进程的内存区域可以通过查看/proc/pid/maps文件
例如:cat /proc/1/maps
start - end perm offset major:minor inode
00008000-000f1000 r-xp 00000000 1f:03 730 /bin/busybox
000f8000-000fb000 rwxp 000e8000 1f:03 730 /bin/busybox
000fb000-000fd000 rwxp 00000000 00:00 0
0048d000-004ae000 rwxp 00000000 00:00 0 [heap]
4001a000-40037000 r-xp 00000000 1f:03 263 /lib/ld-2.8.so
4003e000-4003f000 r-xp 0001c000 1f:03 263 /lib/ld-2.8.so
4003f000-40040000 rwxp 0001d000 1f:03 263 /lib/ld-2.8.so
40063000-40064000 rwxp 00000000 00:00 0
400c6000-400c7000 rwxp 00000000 00:00 0
40157000-401fc000 r-xp 00000000 1f:03 301 /lib/libm-2.8.so
401fc000-40203000 ---p 000a5000 1f:03 301 /lib/libm-2.8.so
40203000-40204000 r-xp 000a4000 1f:03 301 /lib/libm-2.8.so
40204000-40205000 rwxp 000a5000 1f:03 301 /lib/libm-2.8.so
40205000-40323000 r-xp 00000000 1f:03 295 /lib/libc-2.8.so
40323000-4032b000 ---p 0011e000 1f:03 295 /lib/libc-2.8.so
4032b000-4032d000 r-xp 0011e000 1f:03 295 /lib/libc-2.8.so
4032d000-4032e000 rwxp 00120000 1f:03 295 /lib/libc-2.8.so
4032e000-40331000 rwxp 00000000 00:00 0
bec95000-becb6000 rw-p 00000000 00:00 0 [stack]
ffff0000-ffff1000 r-xp 00000000 00:00 0 [vectors]
Linux内核使用结构vm_area_struct(<linux/mm_types.h>)来描述虚拟内存区域
unsigned long vm_start; //虚拟内存区域起始地址
unsigned long vm_end; //虚拟内存区域结束地址
unsigned long vm_flags;
/*
该区域的标记。
VM_IO:内存映射的IO区域,该标记阻止系统将该区域包含在进程的存放转存(core dump)中。
VM_RESERVED:内存区域不能被换出。
*/
mmap设备操作:
映射一个设备是指把用户空间的一段地址关联到设备内存上。
当程序读写这段用户空间地址时,它实际上是在访问设备。
mmap设备方法需要完成什么功能?
mmap方法是file_operations结构的成员,在mmap系统调用发出时被调用。
在此之前,内核已经完成了很多工作。mmap设备方法所需要做的就是建立
虚拟地址到物理地址的页表。
int (*mmap)(struct file* ,struct vm_area_struct *);
如何建立页表:
1.使用remap_pfn_range一次建立所以页表;
2.使用nopage VMA方法每次建立一个页表。
int remap_pfn_range(struct vm_area_struct *vma, unsigned long addr, unsigned long pfn, unsigned long size, pgprot_t prot)
vma:虚拟内存区域指针
addr:虚拟地址的起始值
pfn :要映射的物理地址所在的物理页帧号,可将物理地址>>PAGE_SHIFT得到
size:要映射的区域的大小
prot:VMA的保护属性
例子:mmap设备操作:
int memdev_mmap(struct file *filp, struct vm_area_struct *vma)
{
vma->vm_flags |= VM_IO;
vma->vm_flags |= VM_RESERVED;
if(remap_pfn_range(vma, vma->vm_start, virt_to_phys(dev->data)>>PAGE_SHIFT, size, vma->vm_page_prot))
return -EAGAIN;
return 0;
}
寄存器与内存的区别:
寄存器和RAM的主要不同在于寄存器操作有副作用(side effect 或 边际效果)
读取某个地址时可能导致该地址内容发生变化,比如很多设备的中断状态寄存器
只要一读取,便自动清零。
内存与I/O
在X86处理器中存在I/O空间的概念,I/O空间是相对内存空间而言的,他们是彼此独立
的地址空间,在32位的X86系统中,I/O空间大小为64K, 内存空间大小为4G。
————————
| |
| CPU | ——————————————————————————————————>
| | | |
———————— | |
| |
内存地V址空间 I/O地V址空间
|——————————————| ___________
| 网卡 | | |
|——————————————| | |
| 内存 | |———————————|
|——————————————| | |
| Nand | | ...... |
|——————————————| | |
| Nor | |———————————|
|——————————————|
| ...... |
|——————————————|
X86: 支持内存空间、IO空间
ARM: 只支持内存空间
MIPS:只支持内存空间
PPC: 只支持内存空间
IO端口与IO内存
IO端口:当一个寄存器或内存位于IO空间时,称为IO端口。
IO内存:当一个寄存器或内存位于内存空间时,称IO内存。
操作IO端口:
1.申请
#include <linux/ioport.h>
struct resource * request_region(resource_size_t start, resource_size_t n, const char *name);
系统端口分配记录于/proc/ioports中。
2.访问
#include <asm/io.h>
unsigned in[bwl](unsigned port);
void out[bwl](unsigned char byte, unsigned port)
3.释放
void release_region(resource_size_t start, resource_size_t n);
操作I/O内存
1.申请
request_mem_region(start,n,name)
系统IO内存分配记录于/proc/iomem
2.映射
void *ioremap(phys_addr_t offset, unsigned long size);
3.访问
unsigned ioread[8 16 32](void *addr);
void iowrite[8 16 32](u8 value, void *addr);
旧版:
unsigned read[bwl](address);
unsigned write[bwl](unsigned value, address);
4.释放
void iounmap(void *addr);
void release_mem_region(unsigned long start, unsigned long len);
Linux内核使用struct miscdevice来描述混杂设备
混杂设备是字符设备,相同的主设备号(10),所有的混杂设备形成一个链表
对设备访问时内核根据次设备号查找到相应的混杂设备。
struct miscdevice
{
int minor;/*次设备号*/
const char *name;/*设备名*/
const struct file_operations *fops;/*文件操作*/
struct list_head list;
struct device *parent;
struct device *this_device;
}
注册混杂设备:
int misc_register(struct miscdevice *misc);
注销混杂设备:
int misc_deregister(struct miscdevice *misc);
上拉电阻/下拉电阻:
上拉电阻:将不确定的信号通过一个电阻与电源相连,固定在高电平。
下拉电阻:将不确定的信号通过一个电阻于地相连,固定在低电平。
上拉是对器件注入电流。
下拉式输出电流。
当一个接有上拉电阻的I/O端口设为输入状态时,它常态为高电平,
可用于检测低电平的输入。
S3C2440有GPA~GPJ九组I/O端口。
GPxCON:00-输入
01-输出
10-表示特殊功能
11-reserved
GPxDAT:用于读写
GPxUP:用于决定是否使用内部上拉电阻
0-此引脚无内部上拉电阻
1-此引脚使用内部上拉电阻
void *mmap(void *addr, size_t len, int port, int flags, int fd, off_t offset);
内存映射函数,负责把文件内容映射到进程的虚拟内存空间,通过对这段内存的修改来实现对文件的修改。
addr:指定映射的起始地址,通常为NULL,由系统指定
len :文件中映射到内存的长度
port:
PORT_EXEC:映射区可被执行;
PORT_READ:映射区可被读取;
PORT_WRITE:映射区可被写入;
flags:
MAP_SHARED:写入映射区的数据会复制回文件,且允许其他映射该文件的进程共享
MAP_PRIVATE:对映射区的写入操作会产生一个映射区的复制(copy-on-write),
对此区域的修改不会写回原文件。
offset:
映射文件中的偏移量。
解除映射:
int munmap(void *start, size_t length);
注意:内存映射不会增加文件的原来大小。
虚拟内存区域:
一个进程的内存区域可以通过查看/proc/pid/maps文件
例如:cat /proc/1/maps
start - end perm offset major:minor inode
00008000-000f1000 r-xp 00000000 1f:03 730 /bin/busybox
000f8000-000fb000 rwxp 000e8000 1f:03 730 /bin/busybox
000fb000-000fd000 rwxp 00000000 00:00 0
0048d000-004ae000 rwxp 00000000 00:00 0 [heap]
4001a000-40037000 r-xp 00000000 1f:03 263 /lib/ld-2.8.so
4003e000-4003f000 r-xp 0001c000 1f:03 263 /lib/ld-2.8.so
4003f000-40040000 rwxp 0001d000 1f:03 263 /lib/ld-2.8.so
40063000-40064000 rwxp 00000000 00:00 0
400c6000-400c7000 rwxp 00000000 00:00 0
40157000-401fc000 r-xp 00000000 1f:03 301 /lib/libm-2.8.so
401fc000-40203000 ---p 000a5000 1f:03 301 /lib/libm-2.8.so
40203000-40204000 r-xp 000a4000 1f:03 301 /lib/libm-2.8.so
40204000-40205000 rwxp 000a5000 1f:03 301 /lib/libm-2.8.so
40205000-40323000 r-xp 00000000 1f:03 295 /lib/libc-2.8.so
40323000-4032b000 ---p 0011e000 1f:03 295 /lib/libc-2.8.so
4032b000-4032d000 r-xp 0011e000 1f:03 295 /lib/libc-2.8.so
4032d000-4032e000 rwxp 00120000 1f:03 295 /lib/libc-2.8.so
4032e000-40331000 rwxp 00000000 00:00 0
bec95000-becb6000 rw-p 00000000 00:00 0 [stack]
ffff0000-ffff1000 r-xp 00000000 00:00 0 [vectors]
Linux内核使用结构vm_area_struct(<linux/mm_types.h>)来描述虚拟内存区域
unsigned long vm_start; //虚拟内存区域起始地址
unsigned long vm_end; //虚拟内存区域结束地址
unsigned long vm_flags;
/*
该区域的标记。
VM_IO:内存映射的IO区域,该标记阻止系统将该区域包含在进程的存放转存(core dump)中。
VM_RESERVED:内存区域不能被换出。
*/
mmap设备操作:
映射一个设备是指把用户空间的一段地址关联到设备内存上。
当程序读写这段用户空间地址时,它实际上是在访问设备。
mmap设备方法需要完成什么功能?
mmap方法是file_operations结构的成员,在mmap系统调用发出时被调用。
在此之前,内核已经完成了很多工作。mmap设备方法所需要做的就是建立
虚拟地址到物理地址的页表。
int (*mmap)(struct file* ,struct vm_area_struct *);
如何建立页表:
1.使用remap_pfn_range一次建立所以页表;
2.使用nopage VMA方法每次建立一个页表。
int remap_pfn_range(struct vm_area_struct *vma, unsigned long addr, unsigned long pfn, unsigned long size, pgprot_t prot)
vma:虚拟内存区域指针
addr:虚拟地址的起始值
pfn :要映射的物理地址所在的物理页帧号,可将物理地址>>PAGE_SHIFT得到
size:要映射的区域的大小
prot:VMA的保护属性
例子:mmap设备操作:
int memdev_mmap(struct file *filp, struct vm_area_struct *vma)
{
vma->vm_flags |= VM_IO;
vma->vm_flags |= VM_RESERVED;
if(remap_pfn_range(vma, vma->vm_start, virt_to_phys(dev->data)>>PAGE_SHIFT, size, vma->vm_page_prot))
return -EAGAIN;
return 0;
}
寄存器与内存的区别:
寄存器和RAM的主要不同在于寄存器操作有副作用(side effect 或 边际效果)
读取某个地址时可能导致该地址内容发生变化,比如很多设备的中断状态寄存器
只要一读取,便自动清零。
内存与I/O
在X86处理器中存在I/O空间的概念,I/O空间是相对内存空间而言的,他们是彼此独立
的地址空间,在32位的X86系统中,I/O空间大小为64K, 内存空间大小为4G。
————————
| |
| CPU | ——————————————————————————————————>
| | | |
———————— | |
| |
内存地V址空间 I/O地V址空间
|——————————————| ___________
| 网卡 | | |
|——————————————| | |
| 内存 | |———————————|
|——————————————| | |
| Nand | | ...... |
|——————————————| | |
| Nor | |———————————|
|——————————————|
| ...... |
|——————————————|
X86: 支持内存空间、IO空间
ARM: 只支持内存空间
MIPS:只支持内存空间
PPC: 只支持内存空间
IO端口与IO内存
IO端口:当一个寄存器或内存位于IO空间时,称为IO端口。
IO内存:当一个寄存器或内存位于内存空间时,称IO内存。
操作IO端口:
1.申请
#include <linux/ioport.h>
struct resource * request_region(resource_size_t start, resource_size_t n, const char *name);
系统端口分配记录于/proc/ioports中。
2.访问
#include <asm/io.h>
unsigned in[bwl](unsigned port);
void out[bwl](unsigned char byte, unsigned port)
3.释放
void release_region(resource_size_t start, resource_size_t n);
操作I/O内存
1.申请
request_mem_region(start,n,name)
系统IO内存分配记录于/proc/iomem
2.映射
void *ioremap(phys_addr_t offset, unsigned long size);
3.访问
unsigned ioread[8 16 32](void *addr);
void iowrite[8 16 32](u8 value, void *addr);
旧版:
unsigned read[bwl](address);
unsigned write[bwl](unsigned value, address);
4.释放
void iounmap(void *addr);
void release_mem_region(unsigned long start, unsigned long len);
Linux内核使用struct miscdevice来描述混杂设备
混杂设备是字符设备,相同的主设备号(10),所有的混杂设备形成一个链表
对设备访问时内核根据次设备号查找到相应的混杂设备。
struct miscdevice
{
int minor;/*次设备号*/
const char *name;/*设备名*/
const struct file_operations *fops;/*文件操作*/
struct list_head list;
struct device *parent;
struct device *this_device;
}
注册混杂设备:
int misc_register(struct miscdevice *misc);
注销混杂设备:
int misc_deregister(struct miscdevice *misc);
上拉电阻/下拉电阻:
上拉电阻:将不确定的信号通过一个电阻与电源相连,固定在高电平。
下拉电阻:将不确定的信号通过一个电阻于地相连,固定在低电平。
上拉是对器件注入电流。
下拉式输出电流。
当一个接有上拉电阻的I/O端口设为输入状态时,它常态为高电平,
可用于检测低电平的输入。
S3C2440有GPA~GPJ九组I/O端口。
GPxCON:00-输入
01-输出
10-表示特殊功能
11-reserved
GPxDAT:用于读写
GPxUP:用于决定是否使用内部上拉电阻
0-此引脚无内部上拉电阻
1-此引脚使用内部上拉电阻