<meter id="pryje"><nav id="pryje"><delect id="pryje"></delect></nav></meter>
          <label id="pryje"></label>

          新聞中心

          EEPW首頁(yè) > 嵌入式系統(tǒng) > 設(shè)計(jì)應(yīng)用 > arm驅(qū)動(dòng)linux設(shè)備地址映射到用戶(hù)空間

          arm驅(qū)動(dòng)linux設(shè)備地址映射到用戶(hù)空間

          作者: 時(shí)間:2016-11-19 來(lái)源:網(wǎng)絡(luò) 收藏
          [《[arm驅(qū)動(dòng)]linux設(shè)備地址映射用戶(hù)空間》涉及內(nèi)核驅(qū)動(dòng)函數(shù)二個(gè),內(nèi)核結(jié)構(gòu)體二個(gè),分析了內(nèi)核驅(qū)動(dòng)函數(shù)二個(gè);可參考的相關(guān)應(yīng)用程序模板或內(nèi)核驅(qū)動(dòng)模板二個(gè),可參考的相關(guān)應(yīng)用程序模板或內(nèi)核驅(qū)動(dòng)四個(gè)

          一、問(wèn)題描述:一般情況下,用戶(hù)空間是不可能也不應(yīng)該直接訪問(wèn)設(shè)備的,但是,設(shè)備驅(qū)動(dòng)程序中可實(shí)現(xiàn)mmap()函數(shù),這個(gè)函數(shù)可使用戶(hù)空間直接訪問(wèn)設(shè)備的物理地址。
          1、mmap()函數(shù)工作原理:mmap()實(shí)現(xiàn)了這樣的一個(gè)映射過(guò)程,它將用戶(hù)的內(nèi)存空間的一般內(nèi)存(準(zhǔn)確來(lái)說(shuō)是執(zhí)行mmap進(jìn)程的映射區(qū)域內(nèi)存)與設(shè)備內(nèi)存關(guān)聯(lián),當(dāng)用戶(hù)訪問(wèn)用戶(hù)空間的這段地址范圍時(shí),實(shí)際上會(huì)轉(zhuǎn)化為對(duì)設(shè)備的訪問(wèn)(linux上一切皆文件)。

          本文引用地址:http://www.ex-cimer.com/article/201611/318218.htm

          文件內(nèi)存映射原理圖示 a

          2、mmap優(yōu)點(diǎn):1、對(duì)于設(shè)備文件,最大的有點(diǎn)就是用戶(hù)空間可以直接訪問(wèn)設(shè)備內(nèi)存;2、普通文件被映射到進(jìn)程地址空間后,進(jìn)程進(jìn)程訪問(wèn)文件的速度也變塊,不必再調(diào)read(),write(),可以用memcpy,strcpy等操作寫(xiě)文件,寫(xiě)完后用msync()同步一下。(感覺(jué)還是很抽象,看了后面的實(shí)例一就明白了)
          3、應(yīng)用場(chǎng)景:mmap()的這種能力用于顯示適配器一類(lèi)的設(shè)備,屏幕幀的像素不再需要從一個(gè)用戶(hù)空間到內(nèi)核空間的復(fù)制過(guò)程。
          二、應(yīng)用程序相關(guān)函數(shù)
          1、建立映射:

          void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset);

          a) 參數(shù)含義:
          addr:指定映射的起始地址, 通常設(shè)為NULL, 由系統(tǒng)指定。
          length: 映射到內(nèi)存的文件長(zhǎng)度。
          prot: 映射區(qū)的保護(hù)方式, 可以是:
          PROT_EXEC: 映射區(qū)可被執(zhí)行
          PROT_READ: 映射區(qū)可被讀取
          PROT_WRITE: 映射區(qū)可被寫(xiě)入
          PROT_NONE 映射區(qū)不可訪問(wèn).

          flags: 映射區(qū)的特性, 可以是:
          MAP_SHARED:對(duì)此區(qū)域所做的修改內(nèi)容獎(jiǎng)寫(xiě)入文件內(nèi);允許其他映射該文件的進(jìn)程共享,意思是:n個(gè)mmap.out程序在運(yùn)行,這n個(gè)進(jìn)程的“虛擬內(nèi)存區(qū)域”的物理空間空間都相同。詳看《虛擬內(nèi)存共享原理圖b》

          虛擬內(nèi)存共享原理圖b

          MAP_PRIVATE:對(duì)此區(qū)域所做的修改不會(huì)更改原來(lái)的文件內(nèi)容,對(duì)映射區(qū)的寫(xiě)入操作會(huì)產(chǎn)生一個(gè)映射區(qū)的復(fù)制(copy-on-write);意思是:n個(gè)mmap.out程序在運(yùn)行,但是虛擬內(nèi)存區(qū)域的物理地址會(huì)被內(nèi)核另外分配。詳看《虛擬內(nèi)存共享原理圖c》

          虛擬內(nèi)存共享原理圖c

          fd: 由open返回的文件描述符, 代表要映射的文件。
          offset: 以文件開(kāi)始處的偏移量, 必須是分頁(yè)大小的整數(shù)倍, 通常為0, 表示從文件頭開(kāi)始映射。

          b)返回值:返回成功----函數(shù)的返回值為最后文件映射到進(jìn)程空間的地址(參照文件內(nèi)存映射原理圖示 a),進(jìn)程可直接操作起始地址為該值的有效地址。返回失敗返回MAP_FAILED(-1),錯(cuò)誤原因存于errno 中。

          2、解除映射:

          int munmap(void *addr, size_t length);

          3、 同步回寫(xiě)函數(shù):

          int msync(const void *start, size_t length, int flags);

          如果您希望立即將數(shù)據(jù)寫(xiě)入文件中,可使用msync。
          a)參數(shù)

          start為記憶體開(kāi)始位置(mmap函數(shù)返回的值---地址),length為長(zhǎng)度。
          flags則有三個(gè):
          MS_ASYNC : 請(qǐng)Kernel快將資料寫(xiě)入,發(fā)出回寫(xiě)請(qǐng)求后立即返回
          MS_SYNC : 在msync結(jié)束返回前,將資料寫(xiě)入。
          MS_INVALIDATE使用回寫(xiě)的內(nèi)容更新該文件的其它映射

          實(shí)例一)mmap普通文件被映射到進(jìn)程地址空間實(shí)例

          mmapfile.c

          #include
          #include
          #include
          #include
          #include
          #include
          #include
          #include
          void printfMapChar(char *nameChar, char *mapChar){//打印mapChar的內(nèi)容
          printf("%s = %snn", nameChar,mapChar);
          }
          void printfMmapAddr(char *nameChar, char *mmapChar){//打印mmapChar的地址
          printf("%saddress = %pn",nameChar, mmapChar);
          }
          void printfDivLine(char *desc){
          printf("********%s*******n", desc);
          }
          int main(){
          int fd;
          char *mapChar;//mapchar存放虛擬內(nèi)存地址
          char *checkChar;//驗(yàn)證是否mapChar是映射地址
          printf("mypid is %dn",getpid());//輸出本pid
          /*獲得映射區(qū)域地址,賦值mapChar*/
          fd = open("/tmp/test.txt",O_RDWR);
          mapChar = mmap(NULL,100,PROT_READ|PROT_WRITE,MAP_SHARED,fd,0);//獲得映射區(qū)域地址MAP_SHARED更改mapchar后改變fd文件內(nèi)容
          /*****************/

          Tip:此時(shí)mapchar就是虛擬內(nèi)存區(qū)域的物理地址部分的首地址;也就是《文件內(nèi)存映射原理圖示 a》中的fd文件存儲(chǔ)映射部分對(duì)應(yīng)的的首地址,當(dāng)進(jìn)車(chē)訪問(wèn)mapchar這段地址范圍時(shí),實(shí)際上會(huì)轉(zhuǎn)化為對(duì)文件fd的訪問(wèn)

          /********打印映射區(qū)域內(nèi)容;和mapChar*********/
          printfDivLine("打印映射區(qū)域內(nèi)容;和mapChar");
          printfMapChar("mapChar", mapChar);
          /**************/
          /*******通過(guò)mapChar將數(shù)據(jù)寫(xiě)入映射區(qū)域*******/
          strcpy(mapChar, "writeSrc,writeSrc,writeSrc,writeSrc,writeSrc,writeSrc,");//寫(xiě)入映射區(qū)域
          printfDivLine("通過(guò)mapChar將數(shù)據(jù)寫(xiě)入映射區(qū)域");
          printfMapChar("mapChar", mapChar);
          /**********checkChar驗(yàn)證*********/
          checkChar = mmap(NULL,100,PROT_READ|PROT_WRITE,MAP_SHARED,fd,0);//獲得映射區(qū)域地址
          close(fd);//不使用fd時(shí)就可以close
          printfDivLine("checkChar驗(yàn)證");
          printfMmapAddr("mapChar", mapChar);
          printfMmapAddr("checkChar", checkChar);
          printfMapChar("checkChar", checkChar);
          munmap(mapChar, 100);//釋放mapchar的映射,此時(shí)文件的映射在內(nèi)存內(nèi)然存在
          munmap(checkChar, 100);
          return 0;
          }

          運(yùn)行結(jié)果:

          mypid is 28529
          ********打印映射區(qū)域內(nèi)容;和mapChar*******
          mapChar = this is a just test temp file
          ********通過(guò)mapChar將數(shù)據(jù)寫(xiě)入映射區(qū)域*******
          mapChar = writeSrc,writeSrc,writeSrc,writeSrc,writeSrc,writeSrc,
          ********checkChar驗(yàn)證*******
          mapCharaddress = 0x7f356eaaa000
          checkCharaddress = 0x7f356eaa9000
          checkChar = writeSrc,writeSrc,writeSrc,writeSrc,writeSrc,writeSrc,

          Tip:一個(gè)進(jìn)程的內(nèi)存映象由下面幾部分組成:程序代碼、數(shù)據(jù)、BSS和棧區(qū)域,以及內(nèi)存映射的區(qū)域。一個(gè)進(jìn)程的內(nèi)存區(qū)域可以通過(guò)查看/proc/pid/maps

          三、給驅(qū)動(dòng)設(shè)備添加mmap虛擬內(nèi)存映射
          內(nèi)核函數(shù)一)1、 驅(qū)動(dòng)中的mmap(內(nèi)核空間):

          int(*mmap)(struct file *,struct vm_area_struct *);

          2、在struct file_operations中與mmap接口函數(shù)關(guān)聯(lián)

          static struct file_operations module_drv_fops = {
          //............
          .mmap = memdev_mmap,
          //...............
          }

          結(jié)構(gòu)體一)3、struct vm_area_struct(VMA)結(jié)構(gòu)體如下

          struct vm_area_struct {
          struct mm_struct * vm_mm; /* The address space we belong to. */
          unsigned long vm_start; /* Our start address within vm_mm. */
          unsigned long vm_end; /* The first byte after our end address within vm_mm. */
          /* linked list of VM areas per task, sorted by address */
          struct vm_area_struct *vm_next;
          pgprot_t vm_page_prot; /* Access permissions of this VMA. */
          unsigned long vm_flags; /* Flags, listed below. */
          struct rb_node vm_rb;
          struct vm_operations_struct * vm_ops;
          /* Information about our backing store: */
          unsigned long vm_pgoff; /* Offset (within vm_file) in PAGE_SIZE units, *not* PAGE_CACHE_SIZE */
          struct file * vm_file; /* File we map to (can be NULL). */
          void * vm_private_data; /* was vm_pte (shared mem) */
          unsigned long vm_truncate_count;/* truncate_count or restart_addr */
          //..................
          };

          4、struct vm_area_struct(VMA)結(jié)構(gòu)體flag參數(shù)
          VM_IO將該VMA標(biāo)記為內(nèi)存映射的IO區(qū)域,VM_IO會(huì)阻止系統(tǒng)將該區(qū)域包含在進(jìn)程的存放轉(zhuǎn)存(core dump )中
          VM_RESERVED標(biāo)志內(nèi)存區(qū)域不能被換出

          內(nèi)核函數(shù)二)5、內(nèi)核mmap中創(chuàng)建頁(yè)表函數(shù):remap_pfn_range();

          作用用“addr ~ addr + size之間的虛擬地址”構(gòu)造頁(yè)表,參考《虛擬內(nèi)存共享原理圖b》和《虛擬內(nèi)存共享原理圖c》

          int remap_pfn_range(struct vm_area_struct *vma, unsigned long addr,
          unsigned long pfn, unsigned long size, pgprot_t prot)

          a)參數(shù)
          1) vma: 虛擬內(nèi)存區(qū)域指針(默認(rèn)使用vma)
          2)addr: 虛擬地址的起始值(默認(rèn)使用vma->vm_start)
          3)pfn:總的來(lái)說(shuō)(pfn = virt_to_phys(void *mem))>>PAGE_SHIFT(常用);或使用默認(rèn)方式vma->vm_pgoff)

          推理:pfn是虛擬地址應(yīng)該映射到的物理地址所在的物理頁(yè)幀號(hào)就是物理地址右移PAGE_SHIFT位,若PAGE_SIZE為4k則PAGE_SHIFT為12(2的12次方為4k),因此PAGE_SIZE為1<>PAGE_SHIFT"得到(虛擬地址 = 物理地址>>PAGE_SHIFT)。如何得到物理地址:將驅(qū)動(dòng)設(shè)備中某個(gè)內(nèi)存變量用函數(shù)virt_to_phys(void *mem)轉(zhuǎn)換成物理地址;(物理地址 = virt_to_phys(void *mem));
          4)size: 要映射的區(qū)域的大小。(默認(rèn)使用vma->vm_end - vma->vm_start)
          5)prot: VMA的保護(hù)屬性。(默認(rèn)使用vma->vm_page_prot)

          模板一)6、mmap驅(qū)動(dòng)模板

          static int memdev_mmap(struct file*file, struct vm_area_struct *vma){
          struct VirtualDisk *devp = file->private_data; /*鑾峰緱璁懼緇撴瀯浣撴寚閽?/
          vma->vm_flags |= VM_IO;
          vma->vm_flags |= VM_RESERVED;
          if (remap_pfn_range(vma,vma->vm_start,virt_to_phys(devp->mem)>>PAGE_SHIFT, vma->vm_end - vma->vm_start, vma->vm_page_prot))
          return -EAGAIN;
          return 0;
          }

          file_oprations中添加或修改.mmap

          static struct file_operations module_drv_fops = {
          //............
          .mmap = memdev_mmap,
          //...............
          }

          實(shí)例二)7、完整實(shí)例

          a)驅(qū)動(dòng)部分

          //“module_drv”,"module_","module"
          #include //模塊所需的大量符號(hào)和函數(shù)定義
          #include
          #include //文件系統(tǒng)相關(guān)的函數(shù)和頭文件
          #include //指定初始化和清除函數(shù)
          #include
          #include //cdev結(jié)構(gòu)的頭文件包含
          #include
          #include
          //#include //包含驅(qū)動(dòng)程序使用的大部分內(nèi)核API的定義,包括睡眠函數(shù)以及各種變量聲明
          #include //在內(nèi)核和用戶(hù)空間中移動(dòng)數(shù)據(jù)的函數(shù)
          #include
          #include
          #include
          #include
          #define VIRTUALDISK_SIZE 0x1000//4k
          #define MEM_CLEAR 0x1
          #define VIRTUALDISK_MAJOR 250
          int VirtualDisk_major = VIRTUALDISK_MAJOR;
          struct VirtualDisk{
          struct cdev cdev;//詳細(xì)看cdev機(jī)制
          unsigned char mem[VIRTUALDISK_SIZE ];
          long count; /*記錄設(shè)備目前被多少設(shè)備打開(kāi)*/
          };
          static struct class *module_class;
          static struct class_device *module_class_dev;
          struct VirtualDisk *VirtualDiskp;
          static int module_drv_open(struct inode *inode, struct file *file)
          {
          printk("module_dev readn");
          file->private_data = VirtualDiskp;
          VirtualDiskp->count++; /*增加設(shè)備打開(kāi)次數(shù)*/
          return 0;
          }
          static int module_drv_release(struct inode *inode, struct file *file)
          {
          printk("module_dev releasen");
          VirtualDiskp->count--; /*減少設(shè)備打開(kāi)次數(shù)*/
          return 0;
          }
          /*seek文件定位函數(shù):seek()函數(shù)對(duì)文件定位的起始地址可以是文件開(kāi)頭(SEEK_SET,0)、當(dāng)前位置(SEEK_CUR,1)、文件尾(SEEK_END,2)*/
          static loff_t module_drv_llseek(struct file *file, loff_t offset, int origin){
          loff_t ret = 0;/*返回的位置偏移*/

          switch (origin)
          {
          case SEEK_SET: /*相對(duì)文件開(kāi)始位置偏移*/
          if (offset < 0)/*offset不合法*/
          {
          ret = - EINVAL; /*無(wú)效的指針*/
          break;
          }
          if ((unsigned int)offset > VIRTUALDISK_SIZE)/*偏移大于設(shè)備內(nèi)存*/
          {
          ret = - EINVAL; /*無(wú)效的指針*/
          break;
          }
          file->f_pos = (unsigned int)offset; /*更新文件指針位置*/
          ret = file->f_pos;/*返回的位置偏移*/
          break;
          case SEEK_CUR: /*相對(duì)文件當(dāng)前位置偏移*/
          if ((file->f_pos + offset) > VIRTUALDISK_SIZE)/*偏移大于設(shè)備內(nèi)存*/
          {
          ret = - EINVAL;/*無(wú)效的指針*/
          break;
          }
          if ((file->f_pos + offset) < 0)/*指針不合法*/
          {
          ret = - EINVAL;/*無(wú)效的指針*/
          break;
          }
          file->f_pos += offset;/*更新文件指針位置*/
          ret = file->f_pos;/*返回的位置偏移*/
          break;
          default:
          ret = - EINVAL;/*無(wú)效的指針*/
          break;
          }
          return ret;
          }
          /*設(shè)備控制函數(shù):ioctl()函數(shù)接受的MEM_CLEAR命令,這個(gè)命令將全局內(nèi)存的有效數(shù)據(jù)長(zhǎng)度清零,對(duì)于設(shè)備不支持的命令,ioctl()函數(shù)應(yīng)該返回-EINVAL*/
          static int module_drv_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg){
          struct VirtualDisk *devp = file->private_data;/*獲得設(shè)備結(jié)構(gòu)體指針*/

          switch (cmd)
          {
          case MEM_CLEAR:/*設(shè)備內(nèi)存清零*/
          memset(devp->mem, 0, VIRTUALDISK_SIZE);
          printk(KERN_INFO "VirtualDisk is set to zeron");
          break;
          default:
          return - EINVAL;
          }
          return 0;
          }
          /*讀函數(shù):讀寫(xiě)函數(shù)主要是讓設(shè)備結(jié)構(gòu)體的mem[]數(shù)組與用戶(hù)空間交互數(shù)據(jù),并隨著訪問(wèn)字節(jié)數(shù)變更返回用戶(hù)的文件讀寫(xiě)偏移位置*/
          static ssize_t module_drv_read(struct file *file, const char __user *buf, size_t count, loff_t * ppos)
          {

          unsigned long p = *ppos; /*記錄文件指針偏移位置*/
          unsigned int countt = count;/*記錄需要讀取的字節(jié)數(shù)*/
          int ret = 0; /*返回值*/
          struct VirtualDisk *devp = file->private_data; /*獲得設(shè)備結(jié)構(gòu)體指針*/
          printk("module_dev readn");
          /*分析和獲取有效的讀長(zhǎng)度*/
          if (p >= VIRTUALDISK_SIZE ) /*要讀取的偏移大于設(shè)備的內(nèi)存空間*/
          return countt ? - ENXIO: 0;/*讀取地址錯(cuò)誤*/
          if (countt > VIRTUALDISK_SIZE - p)/*要讀取的字節(jié)大于設(shè)備的內(nèi)存空間*/
          countt = VIRTUALDISK_SIZE - p;/*將要讀取的字節(jié)數(shù)設(shè)為剩余的字節(jié)數(shù)*/
          /*內(nèi)核空間->用戶(hù)空間交換數(shù)據(jù)*/
          if (copy_to_user(buf, (void*)(devp->mem + p), countt))
          {
          ret = - EFAULT;
          }
          else
          {
          *ppos += countt;
          ret = countt;
          printk("read %d bytes(s) is %ldn", countt, p);
          }
          printk("bytes(s) is %sn", buf);
          return ret;
          }
          /*
          file 是文件指針,count 是請(qǐng)求的傳輸數(shù)據(jù)長(zhǎng)度,buff 參數(shù)是指向用戶(hù)空間的緩沖區(qū),這個(gè)緩沖區(qū)或者保存要寫(xiě)入的數(shù)據(jù),或者是一個(gè)存放新讀入數(shù)據(jù)的空緩沖區(qū),該地址在內(nèi)核空間不能直接讀寫(xiě),ppos 是一個(gè)指針指向一個(gè)"long offset type"對(duì)象, 它指出用戶(hù)正在存取的文件位置. 返回值是一個(gè)"signed size type。寫(xiě)的位置相對(duì)于文件開(kāi)頭的偏移。
          */
          static ssize_t module_drv_write(struct file *file, const char __user *buf, size_t count, loff_t * ppos)
          {

          unsigned long p = *ppos; /*記錄文件指針偏移位置*/
          int ret = 0; /*返回值*/
          unsigned int countt = count;/*記錄需要寫(xiě)入的字節(jié)數(shù)*/
          struct VirtualDisk *devp = file->private_data; /*獲得設(shè)備結(jié)構(gòu)體指針*/
          printk("module_dev writen");
          /*分析和獲取有效的寫(xiě)長(zhǎng)度*/
          if (p >= VIRTUALDISK_SIZE )/*要寫(xiě)入的偏移大于設(shè)備的內(nèi)存空間*/
          return countt ? - ENXIO: 0;/*寫(xiě)入地址錯(cuò)誤*/
          if (countt > VIRTUALDISK_SIZE - p)/*要寫(xiě)入的字節(jié)大于設(shè)備的內(nèi)存空間*/
          countt = VIRTUALDISK_SIZE - p;/*將要寫(xiě)入的字節(jié)數(shù)設(shè)為剩余的字節(jié)數(shù)*/
          /*用戶(hù)空間->內(nèi)核空間*/
          if (copy_from_user(devp->mem + p, buf, countt))
          ret = - EFAULT;
          else
          {
          *ppos += countt;/*增加偏移位置*/
          ret = countt;/*返回實(shí)際的寫(xiě)入字節(jié)數(shù)*/
          printk("written %d bytes(s) from%ldn", countt, p);
          }
          return ret;
          return 0;
          }
          static int memdev_mmap(struct file*file, struct vm_area_struct *vma){
          struct VirtualDisk *devp = file->private_data; /*獲得設(shè)備結(jié)構(gòu)體指針*/
          vma->vm_flags |= VM_IO;
          vma->vm_flags |= VM_RESERVED;
          if (remap_pfn_range(vma,vma->vm_start,virt_to_phys(devp->mem)>>PAGE_SHIFT, vma->vm_end - vma->vm_start, vma->vm_page_prot))
          return -EAGAIN;

          return 0;
          }
          static struct file_operations module_drv_fops = {
          .owner = THIS_MODULE, /* 這是一個(gè)宏,推向編譯模塊時(shí)自動(dòng)創(chuàng)建的__this_module變量 */
          .open = module_drv_open,
          .read = module_drv_read,
          .write = module_drv_write,
          .release = module_drv_release,
          .llseek = module_drv_llseek,
          .ioctl = module_drv_ioctl,
          .mmap = memdev_mmap,
          };
          /*將 cdev 結(jié)構(gòu)嵌入一個(gè)你自己的設(shè)備特定的結(jié)構(gòu),你應(yīng)當(dāng)初始化你已經(jīng)分配的結(jié)構(gòu)使用以上函數(shù),有一個(gè)其他的 struct cdev 成員你需要初始化. 象 file_operations 結(jié)構(gòu),struct cdev 有一個(gè)擁有者成員,應(yīng)當(dāng)設(shè)置為 THIS_MODULE,一旦 cdev 結(jié)構(gòu)建立, 最后的步驟是把它告訴內(nèi)核, 調(diào)用:
          cdev_add(&dev->cdev, devno, 1);*/
          static void VirtualDisk_setup_cdev(struct VirtualDisk *dev, int minorIndex){
          int err;
          int devno = MKDEV(VirtualDisk_major, minorIndex);
          cdev_init(&dev->cdev, &module_drv_fops);
          dev->cdev.owner = THIS_MODULE;
          err = cdev_add(&dev->cdev, devno, 1);
          if(err){
          printk("error %d cdev file addedn", err);
          }
          }
          static int module_drv_init(void)
          {
          int result;
          dev_t devno = MKDEV(VirtualDisk_major, 0);
          if(VirtualDisk_major){
          result = register_chrdev_region(devno, 1, "module");
          }else{
          result = alloc_chrdev_region(&devno, 0, 1, "module");
          VirtualDisk_major = MAJOR(devno);
          }
          if(result < 0 ){
          return result;
          }
          VirtualDiskp = kmalloc(sizeof(struct VirtualDisk), GFP_KERNEL);
          if(!VirtualDiskp){
          result = -ENOMEM;
          goto fail_malloc;
          }
          memset(VirtualDiskp, 0, sizeof(struct VirtualDisk));
          VirtualDisk_setup_cdev(VirtualDiskp, 0);
          module_class = class_create(THIS_MODULE, "module_drv");
          if (IS_ERR(module_class))
          return PTR_ERR(module_class);
          module_class_dev = class_device_create(module_class, NULL, MKDEV(VirtualDisk_major, 0), NULL, "module"); /* /dev/xyz */
          if (IS_ERR(module_class_dev))
          return PTR_ERR(module_class_dev);
          return 0;
          fail_malloc:
          unregister_chrdev_region(devno, 1);
          return result;

          }
          static void module_drv_exit(void)
          {
          cdev_del(&VirtualDiskp->cdev);
          kfree(VirtualDiskp);
          unregister_chrdev_region(MKDEV(VirtualDisk_major, 0), 1);
          class_device_unregister(module_class_dev);
          class_destroy(module_class);
          }
          module_init(module_drv_init);
          module_exit(module_drv_exit);
          MODULE_LICENSE("GPL");

          實(shí)例三)b)與驅(qū)動(dòng)對(duì)應(yīng)的應(yīng)用程序部分

          #include
          #include
          #include
          #include
          #include
          #include
          int main(){
          int fd;
          char *start;
          //char buf[100];
          char *buf;
          char *end;
          char key_vals[20];
          /*打開(kāi)文件*/
          printf("mypid is %d",getpid());
          fd = open("/dev/module",O_RDWR);

          buf = (char *)malloc(100);
          memset(buf, 0, 100);
          start=mmap(NULL,10,PROT_READ|PROT_WRITE,MAP_SHARED,fd,0);
          end = mmap(NULL, 20,PROT_READ|PROT_WRITE,MAP_SHARED,fd,0);//addr為NULL,由系統(tǒng)分配
          /* 讀出數(shù)據(jù) */
          strcpy(buf,start);
          sleep (1);
          printf("buf 1 = %sn",buf);
          /* 寫(xiě)入數(shù)據(jù) */
          strcpy(start,"Buf Is Not Null!rgrgrfgfgfdg");
          memset(buf, 0, 100);
          strcpy(buf,start);
          sleep (1);
          printf("buf 2 = %sn",buf);
          /* 寫(xiě)入數(shù)據(jù) */
          strcpy(end,"is it reality! is not sure,are you ok, make sure ,you");
          memset(buf, 0, 100);
          strcpy(buf,start);
          sleep (1);
          printf("buf 3 = %sn",buf);
          printf("buf 3 = %sn",end);

          read(fd, key_vals, sizeof(key_vals));
          printf("key_vals 3 = %sn",key_vals);

          // while(1);
          munmap(start,10); /*解除映射*/
          munmap(end,20); /*解除映射*/

          free(buf);
          close(fd);
          return 0;
          }

          第三部分:struct stat 作用
          1、stat,lstat,fstat1 函數(shù)都是獲取文件(普通文件,目錄,管道,socket,字符,塊()的屬性。函數(shù)原型#include
          2、向stat,fstat1、lstat傳入文件名字path、fd、path,獲取文件對(duì)應(yīng)屬性buf。

          int stat(const char *path, struct stat *buf); //文件路徑或文件名
          int fstat(int fd, struct stat *buf);//文件描述符
          int lstat(const char *path, struct stat *buf);//連接文件

          結(jié)構(gòu)體二)struct stat結(jié)構(gòu)

          struct stat {
          mode_t st_mode; //文件對(duì)應(yīng)的模式,文件,目錄等
          ino_t st_ino; //inode節(jié)點(diǎn)號(hào)
          dev_t st_dev; //設(shè)備號(hào)碼
          dev_t st_rdev; //特殊設(shè)備號(hào)碼
          nlink_t st_nlink; //文件的連接數(shù)
          uid_t st_uid; //文件所有者
          gid_t st_gid; //文件所有者對(duì)應(yīng)的組
          off_t st_size; //普通文件,對(duì)應(yīng)的文件字節(jié)數(shù)(常用)
          time_t st_atime; //文件最后被訪問(wèn)的時(shí)間
          time_t st_mtime; //文件內(nèi)容最后被修改的時(shí)間
          time_t st_ctime; //文件狀態(tài)改變時(shí)間
          blksize_t st_blksize; //文件內(nèi)容對(duì)應(yīng)的塊大小
          blkcnt_t st_blocks; //文件內(nèi)容對(duì)應(yīng)的塊數(shù)量
          };

          四、與mmap應(yīng)用程序中“普通文件虛擬內(nèi)存映射模板和實(shí)例

          模板二)1、mmap()應(yīng)用程序模板

          int fd;
          /*獲得映射區(qū)域地址,賦值mapChar*/
          fd = open("/tmp/test.txt",O_RDWR);
          struct stat fileStat;
          /* 獲取文件的屬性 */
          if ((fstat(fd, &fileStat)) == -1) {
          perror("fstat");
          }
          unsigned int fileBufferSize;
          fileBufferSize = fileStat.st_size;/*mmap回寫(xiě)時(shí),字節(jié)最大大小
          為fileStat.st_size,所以定義字節(jié)大fileStat.st_size*/
          mmap(NULL,fileBufferSize,PROT_READ|PROT_WRITE,MAP_SHARED,fd,0);//獲得映射區(qū)域地址
          munmap(checkChar, fileBufferSize);

          實(shí)例四)2、完整實(shí)例

          #include
          #include
          #include
          #include
          #include
          #include
          #include
          #include
          #include
          void printfMapChar(char *nameChar, char *mapChar){
          printf("%s = %snn", nameChar,mapChar);
          }
          void printfDivLine(char *desc){
          printf("********%s*******n", desc);
          }
          int main(){
          int fd;
          char *mapChar;
          char *checkChar;//驗(yàn)證是否mapChar是映射地址
          struct stat fileStat;
          printf("mypid is %dn",getpid());//輸出本pid
          /*獲得映射區(qū)域地址,賦值mapChar*/
          fd = open("/tmp/test.txt",O_RDWR);
          /* 獲取文件的屬性 */
          if ((fstat(fd, &fileStat)) == -1) {
          perror("fstat");
          }

          unsigned int fileBufferSize;
          fileBufferSize = fileStat.st_size;/*mmap回寫(xiě)時(shí),字節(jié)最大大小
          為fileStat.st_size,所以定義字節(jié)大fileStat.st_size*/

          Tip:mmap回寫(xiě)時(shí),回寫(xiě)字節(jié)最大大小為fileStat.st_size,所以定義字節(jié)大fileStat.st_size。(這個(gè)我沒(méi)有根據(jù),只是實(shí)驗(yàn)結(jié)果是這樣)

          mapChar = mmap(NULL,fileBufferSize,PROT_READ|PROT_WRITE,MAP_SHARED,fd,0);//獲得映射區(qū)域地址MAP_SHARED更改mapchar后改變fd文件內(nèi)容
          /*****************/
          /********打印映射區(qū)域內(nèi)容;和mapChar*********/
          printfDivLine("打印映射區(qū)域內(nèi)容;和mapChar");
          printfMapChar("mapChar", mapChar);
          /**************/
          /*******通過(guò)mapChar將數(shù)據(jù)寫(xiě)入映射區(qū)域*******/
          strcpy(mapChar, "writeSrc,writeSrc,writeSrc,writeSrc,writeSrc,writeSrc,");//寫(xiě)入映射區(qū)域
          printfDivLine("通過(guò)mapChar將數(shù)據(jù)寫(xiě)入映射區(qū)域");
          printfMapChar("mapChar", mapChar);
          /**********checkChar驗(yàn)證*********/
          checkChar = mmap(NULL,fileBufferSize,PROT_READ|PROT_WRITE,MAP_SHARED,fd,0);//獲得映射區(qū)域地址
          close(fd);//不使用fd時(shí)就可以close
          printfDivLine("checkChar驗(yàn)證");
          printfMapChar("checkChar", checkChar);
          munmap(mapChar, fileBufferSize);//釋放mapchar的映射,此時(shí)文件的映射在內(nèi)存內(nèi)然存在
          munmap(checkChar, fileBufferSize);
          return 0;
          }



          評(píng)論


          技術(shù)專(zhuān)區(qū)

          關(guān)閉
          看屁屁www成人影院,亚洲人妻成人图片,亚洲精品成人午夜在线,日韩在线 欧美成人 (function(){ var bp = document.createElement('script'); var curProtocol = window.location.protocol.split(':')[0]; if (curProtocol === 'https') { bp.src = 'https://zz.bdstatic.com/linksubmit/push.js'; } else { bp.src = 'http://push.zhanzhang.baidu.com/push.js'; } var s = document.getElementsByTagName("script")[0]; s.parentNode.insertBefore(bp, s); })();