Chinaunix首页 | 论坛 | 博客
  • 博客访问: 19082
  • 博文数量: 15
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 45
  • 用 户 组: 普通用户
  • 注册时间: 2015-06-23 14:19
个人简介

我要成为多面手!

文章分类
文章存档

2015年(15)

我的朋友

分类: LINUX

2015-11-18 00:03:45

    这几日,一个项目要用到蓝牙模块,除用通过串口用AT指令控制蓝牙模块之外,还要另外控制两个GPIO口。对此,方案有二:
    1.编写一个文字字符设备驱动,也许可以用ioctl的方法来在用户空间调用给GPIO拉高拉低;
    2.用mmap的方法,似乎可以直接在用户空间直接控制GPIO。
    由于以前确实没有用过mmap,而在此,mmap的方法似乎更为合适,于是鄙人决定一探mmap的究竟。本文将以“mmap是谁,它从哪儿来,要到哪儿去”的哲学指导思想,来对mmap进行一番彻底的学习。鄙人才疏学浅,下文中必有不少疏漏之处,望大家指正,共勉之。

一、mmap是什么?
    1.mmap是一个系统调用
    mmap将一个文件或者其他对象映射进内存中。此处所说的内存,则为虚拟内存,即运行内存。也就是说,mmap是将文件或其他对象,从物理内存的地址映射到虚拟内存的地址当中的。而且,虚拟内存空间正是用户空间里进程的运行空间。换言之,mmap就是把物理内存里面的东西,传送过来虚拟空间内,可以被用户空间的进程直接读写。
     在用户空间中,mmap的函数原型:
     void * mmap(void *start, size_t length, int prot, int flags, int fd, off_t offset);
     参数:
     start : 映射到虚拟内存后的首地址,这个参数一般填为null,这样一来,内核便会自动去使用一段可用的地址。
     length : 映射区的长度,取决于要读取的数据的长度。
     prot : 内存保护标志位,根据取值来决定保存的形式,可以用||运算符组合。
                   PROT_EXEC     //页内容可以被执行
                   PROT_READ    //页内容可以被读取
                   PROT_WRITE  //页内容可以被写入
                   PROT_NONE   //页不可访问
     flags : 指定映射对象的类型,映射选项和映射页是否可以共享,它的值可以是一个或者多个以下宏的组合体。
                   MAP_SHARED     //与其它所有映射这个对象的进程共享映射空间。
                   MAP_PRIVATE    //建立一个写入时拷贝的私有映射。
    fd : 有效的文件描述符。
    offset : 被映射对象内容的起点。
   return : 返回所映射的虚拟内存首地址。
    以上函数成功执行时,mmap()返回被映射区的指针,munmap()返回0。失败时,mmap()返回MAP_FAILED[其值为(void *)-1],munmap返回-1。errno被设为以下的某个值   
    EACCES:访问出错
    EAGAIN:文件已被锁定,或者太多的内存已被锁定
    EBADF:fd不是有效的文件描述词
    EINVAL:一个或者多个参数无效
    ENFILE:已达到系统对打开文件的限制
    ENODEV:指定文件所在的文件系统不支持内存映射
    ENOMEM:内存不足,或者进程已超出最大内存映射数量
    EPERM:权能不足,操作不允许
    ETXTBSY:已写的方式打开文件,同时指定MAP_DENYWRITE标志
    SIGSEGV:试着向只读区写入
    SIGBUS:试着访问不属于进程的内存区

二、mmap内核源码解析
    上面从感性上大概了解了mmap,下面,我们从内核入手,理性地对mmap源码进行详细分析。
    首先,我们知道,通过系统调用mmap可以创建并且分配一个虚拟空间的地址,并对其进行操作,下面来看该调用的具体实现,文件目录是在linux-3.0.1/mm/mmap.c。
    在看这份代码的时候,发现有特别多的SYSCALL_DEFINEx这样的宏,于是便钻进去研究一番,发现Linus真的是宏大神,下面以SYSCALL_DEFINE6(mmap_pgoff, unsigned long, addr, unsigned long, len, unsigned long, prot, unsigned long, flags, unsigned long, fd, unsigned long, pgoff) 这个函数为例,说明一番。
     #define SYSCALL_DEFINE6(name, ...) SYSCALL_DEFINEx(6, _##name, __VA_ARGS__)
    根据以上定义,原函数可以展开得:
    SYSCALL_DEFINEx(6, _mmap_pgoff, __VA_ARGS__)
     又根据#define SYSCALL_DEFINEx(x, sname, ...)  __SYSCALL_DEFINEx(x, sname, __VA_ARGS__)
     继续展开得:
     __SYSCALL_DEFINE6(6, _mmap_pgoff, __VA_ARGS__)
     继续根据~ #define __SYSCALL_DEFINEx(x, name, ...)    asmlinkage long sys##name(__SC_DECL##x(__VA_ARGS__))
     再继续展开得:
     asmlinkage long sys_mmap_pgoff(__SC_DECL6(__VA_ARGS__))
     接着,重头戏来了,千万嫑眨眼睛:
     #define __SC_DECL1(t1, a1) t1 a1
    #define __SC_DECL2(t2, a2, ...) t2 a2, __SC_DECL1(__VA_ARGS__)
    #define __SC_DECL3(t3, a3, ...) t3 a3, __SC_DECL2(__VA_ARGS__)
    #define __SC_DECL4(t4, a4, ...) t4 a4, __SC_DECL3(__VA_ARGS__)
    #define __SC_DECL5(t5, a5, ...) t5 a5, __SC_DECL4(__VA_ARGS__)
    #define __SC_DECL6(t6, a6, ...) t6 a6, __SC_DECL5(__VA_ARGS__)
     以上这一部分,我觉得就是精华,到这里不得不深深地佩服Linus大神了,至于是为什么,大家自己探索一番,我负责继续展开:
     asmlinkage long sys_mmap_pgoff(unsigned long addr, unsigned long len, unsigned long prot, unsigned long flags, unsigned long fd, unsigned long pgoff)
     OK~大工将近告成,最后一步,让我们来看看asmlinkage到底是个什么东西
     #define asmlinkage CPP_ASMLINKAGE
     #define CPP_ASMLINKAGE
     asmlinkage是啥都没有的,至于到底有什么用,希望朋友们不吝赐教。
     今晚就到这里,该休息了。。。
 











          

阅读(553) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~