原创:kylin_zeng
http://blog.sina.com.cn/u/2312748742
len:映射到内存的文件长度。
prot:映射区的保护。PROT_EXEC:可执行,PROT_READ:可读,PROT_WRITE:可写
MAP_PRIVATE:不会对文件更改。
start :区域的起始虚拟地址;
end:结束虚拟地址:
perm:表示对这个区域运行进程做什么,有读写和执行等权限。最后一个字符p表示私有的,s表示共享
offset:被映射部分在文件中的起始地址。
major,minor:主次设备号。
映射一个设备是指:将用户空间的一段地址关联到设备内存中。单程序读这个用户空间的地址时
mmap设备方法要完成这个功能实际要做就是建立虚拟地址到物理地址的页表。
mmap如何完成页表的建立:
1)remap_pfn_range(struct vm_area_struct* vma,unsigened long addr,
unsigned long pfn,unsigned long size,pgprot_t prot)一次建立所有的页表。
vma:虚拟内存区域指针,
virt_addr:虚拟地址的起始值
pfn:要映射的物理地址所在物理页的帧号。物理地址>>PAGE_SHIFT(页大小)得到
size:要映射的区域大小
prot:vma保护属性。
例如:
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;
}
2)nopage vma每次建立一个页表。
struct resource *request_mem_region(unsigned long start,unsigned long len,char *name);
在访问io内存前必须先进行物理地址到虚拟地址的映射,:
读io 内存:
unsigned ioread8(void *addr);//8位 老版本 ioreadb ioreadw ioreadl
unsigned ioread16(void *addr);//16位
unsigned ioread32(void *addr);//32位
写io内存:
unsigned ioread8(u8 value,void *addr);//8位
unsigned ioread16(u8 value,void *addr);//16位
unsigned ioread32(u8 value,void *addr);//32位
释放io内存:
void iounmap(void *addr);
void release_mem_region(unsigned long start,unsigned long len);
一、
mmap系统调用:
1)void *mmap(void *addr,size_t len, int prot, int flags, int fd, off_t offset);
2)功能:内存映射函数mmap将文件内容映射到进程的虚拟内存空间中,可以通过读取和更改这段内存从而间接改文件。
3)参数:addr: 指定映射的起始地址,可设null由系统指定
flags:映射区的特性:MAP_SHARED:映射区更改文件也会更改,且允许其他映射该文件的进程共享。
4)返回值:返回虚拟的内存空间:void *mmap。
munmap解除映射:
1)int munmap(void *start,size_t length);
2)功能:取消start所指的length长度的映射内存。
3)参数:start,有void *mmap返回的值。length长度。
4)返回值:解除成功0,否则-1,错误原因在errno中。
注意:最终文件的内容大小不会改变。
二、虚拟内存区域:
1)虚拟内存区域是进程的虚拟地址空间中的一个同质区间,即具有同样特性的连续地址范围。
内存映像由:程序代码,数据,BSS,栈区域,内存映射区域组成。
也就是比如栈为一个虚拟内存区域,堆为一个,初始化数据为一个等等。
一个进程内存区域可以通过 cat /proc/pid/maps查看。
2)如:08048000-0804f000 r-xp 00000000 08:01 573748 /sbin/rpc.statd #test
inode:索引节点。
3)在linux内核使用vm_area_struct()来描述虚拟内存区
有几个成员:unsigned long vm_start:虚拟内存区的起始地址;
unsigned long vm_end;结束。
unsigned long vm_flags:有VM_IO 将vma标记为内存映射的io区域。
VM_RESERVED标记内存区域不能被换出。
三、mmap设备的操作:
就是在访问设备。
int(*mmap)(struct file *,struct vm_area_struct*)
四、访问物理地址:
操作io内存:
申请,映射,访问,释放。
申请io内存:
申请一个从start开始,长度为len的字节内存区。成功返回非0,失败返回0.
所以的io内存在/proc/inmem中列出。
void *ioremap(unsigned long phys_addr,unsigned long size);
**********************************************************************************
五、混杂设备 miscdevice:一类字符设备它们共享一个主设备(10),但次设备号不同。
所以的混杂设备形成一个链表,内核根据次设备号查找相应的miscdevice设备。
struct miscdevice{
int minor;//次设备号
const char *name; //设备名
const struct file_operations *fops;//文件操作
struct list_head list;
sturct device *parent;
struct device *this_device;
};
注册用:int misc_register(struct miscdevice *misc);
例如:注册led:
1)ret = misc_register(&misc);
struct miscdevice{
};
注册用:int misc_register(struct miscdevice *misc);
例如:注册led:
1)ret = misc_register(&misc);
2)static struct miscdevice misc = {
.minor = MISC_DYNAMIC_MINOR, //这个是255 表示由系统动态分配一个次设备号
.name = "led",
.fops = &dev_fops,
//字符设备都要有fops.
};
3)static struct file_operations dev_fops = {
.owner
=
THIS_MODULE,
.ioctl
=
sbc2440_leds_ioctl,
//支持ioctl.内核还自动设置open,和relese.
};
};
3)static struct file_operations dev_fops = {
};
4)
static int sbc2440_leds_ioctl(
struct inode *inode,
struct file *file,
unsigned int cmd,
unsigned long arg)
{
switch(cmd) {
case 0:
case 1:
if (arg > 4) {
return -EINVAL;
}
s3c2410_gpio_setpin(led_table[arg], !cmd);
return 0;
default:
return -EINVAL;
}
}
static int sbc2440_leds_ioctl(
{
}
ioctl一般可以两步:定义命令和实现命令。这里比较简单没这样做。
说明:sscanf(argv[1], "%d", &led_no) != 1 这个表示 将argv[1]收到的字符串转换成%d整形然后赋给led_no.
说明:sscanf(argv[1], "%d", &led_no) != 1 这个表示 将argv[1]收到的字符串转换成%d整形然后赋给led_no.