arm驅(qū)動(dòng)linux設(shè)備地址映射到用戶空間
一、問(wèn)題描述:一般情況下,用戶空間是不可能也不應(yīng)該直接訪問(wèn)設(shè)備的,但是,設(shè)備驅(qū)動(dòng)程序中可實(shí)現(xiàn)mmap()函數(shù),這個(gè)函數(shù)可使用戶空間直接訪問(wèn)設(shè)備的物理地址。
1、mmap()函數(shù)工作原理:mmap()實(shí)現(xiàn)了這樣的一個(gè)映射過(guò)程,它將用戶的內(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)用戶訪問(wèn)用戶空間的這段地址范圍時(shí),實(shí)際上會(huì)轉(zhuǎn)化為對(duì)設(shè)備的訪問(wèn)(linux上一切皆文件)。
文件內(nèi)存映射原理圖示 a
2、mmap優(yōu)點(diǎn):1、對(duì)于設(shè)備文件,最大的有點(diǎn)就是用戶空間可以直接訪問(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()的這種能力用于顯示適配器一類的設(shè)備,屏幕幀的像素不再需要從一個(gè)用戶空間到內(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)車訪問(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<
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
#include
#include
#include
#include
#include
#include
#include
//#include
#include //在內(nèi)核和用戶空間中移動(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ù)組與用戶空間交互數(shù)據(jù),并隨著訪問(wèn)字節(jié)數(shù)變更返回用戶的文件讀寫(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)核空間->用戶空間交換數(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ù)是指向用戶空間的緩沖區(qū),這個(gè)緩沖區(qū)或者保存要寫(xiě)入的數(shù)據(jù),或者是一個(gè)存放新讀入數(shù)據(jù)的空緩沖區(qū),該地址在內(nèi)核空間不能直接讀寫(xiě),ppos 是一個(gè)指針指向一個(gè)"long offset type"對(duì)象, 它指出用戶正在存取的文件位置. 返回值是一個(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ù)*/
/*用戶空間->內(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)論