arm驅(qū)動linux設備地址映射到用戶空間
一、問題描述:一般情況下,用戶空間是不可能也不應該直接訪問設備的,但是,設備驅(qū)動程序中可實現(xiàn)mmap()函數(shù),這個函數(shù)可使用戶空間直接訪問設備的物理地址。
1、mmap()函數(shù)工作原理:mmap()實現(xiàn)了這樣的一個映射過程,它將用戶的內(nèi)存空間的一般內(nèi)存(準確來說是執(zhí)行mmap進程的映射區(qū)域內(nèi)存)與設備內(nèi)存關(guān)聯(lián),當用戶訪問用戶空間的這段地址范圍時,實際上會轉(zhuǎn)化為對設備的訪問(linux上一切皆文件)。
文件內(nèi)存映射原理圖示 a
2、mmap優(yōu)點:1、對于設備文件,最大的有點就是用戶空間可以直接訪問設備內(nèi)存;2、普通文件被映射到進程地址空間后,進程進程訪問文件的速度也變塊,不必再調(diào)read(),write(),可以用memcpy,strcpy等操作寫文件,寫完后用msync()同步一下。(感覺還是很抽象,看了后面的實例一就明白了)
3、應用場景:mmap()的這種能力用于顯示適配器一類的設備,屏幕幀的像素不再需要從一個用戶空間到內(nèi)核空間的復制過程。
二、應用程序相關(guān)函數(shù)
1、建立映射:
void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset);
a) 參數(shù)含義:
addr:指定映射的起始地址, 通常設為NULL, 由系統(tǒng)指定。
length: 映射到內(nèi)存的文件長度。
prot: 映射區(qū)的保護方式, 可以是:
PROT_EXEC: 映射區(qū)可被執(zhí)行
PROT_READ: 映射區(qū)可被讀取
PROT_WRITE: 映射區(qū)可被寫入
PROT_NONE 映射區(qū)不可訪問.
flags: 映射區(qū)的特性, 可以是:
MAP_SHARED:對此區(qū)域所做的修改內(nèi)容獎寫入文件內(nèi);允許其他映射該文件的進程共享,意思是:n個mmap.out程序在運行,這n個進程的“虛擬內(nèi)存區(qū)域”的物理空間空間都相同。詳看《虛擬內(nèi)存共享原理圖b》
虛擬內(nèi)存共享原理圖b
MAP_PRIVATE:對此區(qū)域所做的修改不會更改原來的文件內(nèi)容,對映射區(qū)的寫入操作會產(chǎn)生一個映射區(qū)的復制(copy-on-write);意思是:n個mmap.out程序在運行,但是虛擬內(nèi)存區(qū)域的物理地址會被內(nèi)核另外分配。詳看《虛擬內(nèi)存共享原理圖c》
虛擬內(nèi)存共享原理圖c
fd: 由open返回的文件描述符, 代表要映射的文件。
offset: 以文件開始處的偏移量, 必須是分頁大小的整數(shù)倍, 通常為0, 表示從文件頭開始映射。
b)返回值:返回成功----函數(shù)的返回值為最后文件映射到進程空間的地址(參照文件內(nèi)存映射原理圖示 a),進程可直接操作起始地址為該值的有效地址。返回失敗返回MAP_FAILED(-1),錯誤原因存于errno 中。
2、解除映射:
int munmap(void *addr, size_t length);
3、 同步回寫函數(shù):
int msync(const void *start, size_t length, int flags);
如果您希望立即將數(shù)據(jù)寫入文件中,可使用msync。
a)參數(shù)
start為記憶體開始位置(mmap函數(shù)返回的值---地址),length為長度。
flags則有三個:
MS_ASYNC : 請Kernel快將資料寫入,發(fā)出回寫請求后立即返回
MS_SYNC : 在msync結(jié)束返回前,將資料寫入。
MS_INVALIDATE使用回寫的內(nèi)容更新該文件的其它映射
實例一)mmap普通文件被映射到進程地址空間實例
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;//驗證是否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:此時mapchar就是虛擬內(nèi)存區(qū)域的物理地址部分的首地址;也就是《文件內(nèi)存映射原理圖示 a》中的fd文件存儲映射部分對應的的首地址,當進車訪問mapchar這段地址范圍時,實際上會轉(zhuǎn)化為對文件fd的訪問
/********打印映射區(qū)域內(nèi)容;和mapChar*********/
printfDivLine("打印映射區(qū)域內(nèi)容;和mapChar");
printfMapChar("mapChar", mapChar);
/**************/
/*******通過mapChar將數(shù)據(jù)寫入映射區(qū)域*******/
strcpy(mapChar, "writeSrc,writeSrc,writeSrc,writeSrc,writeSrc,writeSrc,");//寫入映射區(qū)域
printfDivLine("通過mapChar將數(shù)據(jù)寫入映射區(qū)域");
printfMapChar("mapChar", mapChar);
/**********checkChar驗證*********/
checkChar = mmap(NULL,100,PROT_READ|PROT_WRITE,MAP_SHARED,fd,0);//獲得映射區(qū)域地址
close(fd);//不使用fd時就可以close
printfDivLine("checkChar驗證");
printfMmapAddr("mapChar", mapChar);
printfMmapAddr("checkChar", checkChar);
printfMapChar("checkChar", checkChar);
munmap(mapChar, 100);//釋放mapchar的映射,此時文件的映射在內(nèi)存內(nèi)然存在
munmap(checkChar, 100);
return 0;
}
運行結(jié)果:
mypid is 28529
********打印映射區(qū)域內(nèi)容;和mapChar*******
mapChar = this is a just test temp file
********通過mapChar將數(shù)據(jù)寫入映射區(qū)域*******
mapChar = writeSrc,writeSrc,writeSrc,writeSrc,writeSrc,writeSrc,
********checkChar驗證*******
mapCharaddress = 0x7f356eaaa000
checkCharaddress = 0x7f356eaa9000
checkChar = writeSrc,writeSrc,writeSrc,writeSrc,writeSrc,writeSrc,
Tip:一個進程的內(nèi)存映象由下面幾部分組成:程序代碼、數(shù)據(jù)、BSS和棧區(qū)域,以及內(nèi)存映射的區(qū)域。一個進程的內(nèi)存區(qū)域可以通過查看/proc/pid/maps
三、給驅(qū)動設備添加mmap虛擬內(nèi)存映射
內(nèi)核函數(shù)一)1、 驅(qū)動中的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標記為內(nèi)存映射的IO區(qū)域,VM_IO會阻止系統(tǒng)將該區(qū)域包含在進程的存放轉(zhuǎn)存(core dump )中
VM_RESERVED標志內(nèi)存區(qū)域不能被換出
內(nèi)核函數(shù)二)5、內(nèi)核mmap中創(chuàng)建頁表函數(shù):remap_pfn_range();
作用用“addr ~ addr + size之間的虛擬地址”構(gòu)造頁表,參考《虛擬內(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ū)域指針(默認使用vma)
2)addr: 虛擬地址的起始值(默認使用vma->vm_start)
3)pfn:總的來說(pfn = virt_to_phys(void *mem))>>PAGE_SHIFT(常用);或使用默認方式vma->vm_pgoff)
推理:pfn是虛擬地址應該映射到的物理地址所在的物理頁幀號就是物理地址右移PAGE_SHIFT位,若PAGE_SIZE為4k則PAGE_SHIFT為12(2的12次方為4k),因此PAGE_SIZE為1<
4)size: 要映射的區(qū)域的大小。(默認使用vma->vm_end - vma->vm_start)
5)prot: VMA的保護屬性。(默認使用vma->vm_page_prot)
模板一)6、mmap驅(qū)動模板
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,
//...............
}
實例二)7、完整實例
a)驅(qū)動部分
//“module_drv”,"module_","module"
#include
#include
#include
#include
#include
#include
#include
#include
//#include
#include //在內(nèi)核和用戶空間中移動數(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;//詳細看cdev機制
unsigned char mem[VIRTUALDISK_SIZE ];
long count; /*記錄設備目前被多少設備打開*/
};
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ù)*/
return 0;
}
static int module_drv_release(struct inode *inode, struct file *file)
{
printk("module_dev releasen");
VirtualDiskp->count--; /*減少設備打開次數(shù)*/
return 0;
}
/*seek文件定位函數(shù):seek()函數(shù)對文件定位的起始地址可以是文件開頭(SEEK_SET,0)、當前位置(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: /*相對文件開始位置偏移*/
if (offset < 0)/*offset不合法*/
{
ret = - EINVAL; /*無效的指針*/
break;
}
if ((unsigned int)offset > VIRTUALDISK_SIZE)/*偏移大于設備內(nèi)存*/
{
ret = - EINVAL; /*無效的指針*/
break;
}
file->f_pos = (unsigned int)offset; /*更新文件指針位置*/
ret = file->f_pos;/*返回的位置偏移*/
break;
case SEEK_CUR: /*相對文件當前位置偏移*/
if ((file->f_pos + offset) > VIRTUALDISK_SIZE)/*偏移大于設備內(nèi)存*/
{
ret = - EINVAL;/*無效的指針*/
break;
}
if ((file->f_pos + offset) < 0)/*指針不合法*/
{
ret = - EINVAL;/*無效的指針*/
break;
}
file->f_pos += offset;/*更新文件指針位置*/
ret = file->f_pos;/*返回的位置偏移*/
break;
default:
ret = - EINVAL;/*無效的指針*/
break;
}
return ret;
}
/*設備控制函數(shù):ioctl()函數(shù)接受的MEM_CLEAR命令,這個命令將全局內(nèi)存的有效數(shù)據(jù)長度清零,對于設備不支持的命令,ioctl()函數(shù)應該返回-EINVAL*/
static int module_drv_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg){
struct VirtualDisk *devp = file->private_data;/*獲得設備結(jié)構(gòu)體指針*/
switch (cmd)
{
case MEM_CLEAR:/*設備內(nèi)存清零*/
memset(devp->mem, 0, VIRTUALDISK_SIZE);
printk(KERN_INFO "VirtualDisk is set to zeron");
break;
default:
return - EINVAL;
}
return 0;
}
/*讀函數(shù):讀寫函數(shù)主要是讓設備結(jié)構(gòu)體的mem[]數(shù)組與用戶空間交互數(shù)據(jù),并隨著訪問字節(jié)數(shù)變更返回用戶的文件讀寫偏移位置*/
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; /*獲得設備結(jié)構(gòu)體指針*/
printk("module_dev readn");
/*分析和獲取有效的讀長度*/
if (p >= VIRTUALDISK_SIZE ) /*要讀取的偏移大于設備的內(nèi)存空間*/
return countt ? - ENXIO: 0;/*讀取地址錯誤*/
if (countt > VIRTUALDISK_SIZE - p)/*要讀取的字節(jié)大于設備的內(nèi)存空間*/
countt = VIRTUALDISK_SIZE - p;/*將要讀取的字節(jié)數(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 是請求的傳輸數(shù)據(jù)長度,buff 參數(shù)是指向用戶空間的緩沖區(qū),這個緩沖區(qū)或者保存要寫入的數(shù)據(jù),或者是一個存放新讀入數(shù)據(jù)的空緩沖區(qū),該地址在內(nèi)核空間不能直接讀寫,ppos 是一個指針指向一個"long offset type"對象, 它指出用戶正在存取的文件位置. 返回值是一個"signed size type。寫的位置相對于文件開頭的偏移。
*/
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;/*記錄需要寫入的字節(jié)數(shù)*/
struct VirtualDisk *devp = file->private_data; /*獲得設備結(jié)構(gòu)體指針*/
printk("module_dev writen");
/*分析和獲取有效的寫長度*/
if (p >= VIRTUALDISK_SIZE )/*要寫入的偏移大于設備的內(nèi)存空間*/
return countt ? - ENXIO: 0;/*寫入地址錯誤*/
if (countt > VIRTUALDISK_SIZE - p)/*要寫入的字節(jié)大于設備的內(nèi)存空間*/
countt = VIRTUALDISK_SIZE - p;/*將要寫入的字節(jié)數(shù)設為剩余的字節(jié)數(shù)*/
/*用戶空間->內(nèi)核空間*/
if (copy_from_user(devp->mem + p, buf, countt))
ret = - EFAULT;
else
{
*ppos += countt;/*增加偏移位置*/
ret = countt;/*返回實際的寫入字節(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; /*獲得設備結(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, /* 這是一個宏,推向編譯模塊時自動創(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)嵌入一個你自己的設備特定的結(jié)構(gòu),你應當初始化你已經(jīng)分配的結(jié)構(gòu)使用以上函數(shù),有一個其他的 struct cdev 成員你需要初始化. 象 file_operations 結(jié)構(gòu),struct cdev 有一個擁有者成員,應當設置為 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");
實例三)b)與驅(qū)動對應的應用程序部分
#include
#include
#include
#include
#include
#include
int main(){
int fd;
char *start;
//char buf[100];
char *buf;
char *end;
char key_vals[20];
/*打開文件*/
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);
/* 寫入數(shù)據(jù) */
strcpy(start,"Buf Is Not Null!rgrgrfgfgfdg");
memset(buf, 0, 100);
strcpy(buf,start);
sleep (1);
printf("buf 2 = %sn",buf);
/* 寫入數(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,獲取文件對應屬性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; //文件對應的模式,文件,目錄等
ino_t st_ino; //inode節(jié)點號
dev_t st_dev; //設備號碼
dev_t st_rdev; //特殊設備號碼
nlink_t st_nlink; //文件的連接數(shù)
uid_t st_uid; //文件所有者
gid_t st_gid; //文件所有者對應的組
off_t st_size; //普通文件,對應的文件字節(jié)數(shù)(常用)
time_t st_atime; //文件最后被訪問的時間
time_t st_mtime; //文件內(nèi)容最后被修改的時間
time_t st_ctime; //文件狀態(tài)改變時間
blksize_t st_blksize; //文件內(nèi)容對應的塊大小
blkcnt_t st_blocks; //文件內(nèi)容對應的塊數(shù)量
};
四、與mmap應用程序中“普通文件虛擬內(nèi)存映射模板和實例
模板二)1、mmap()應用程序模板
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回寫時,字節(jié)最大大小
為fileStat.st_size,所以定義字節(jié)大fileStat.st_size*/
mmap(NULL,fileBufferSize,PROT_READ|PROT_WRITE,MAP_SHARED,fd,0);//獲得映射區(qū)域地址
munmap(checkChar, fileBufferSize);
實例四)2、完整實例
#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;//驗證是否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回寫時,字節(jié)最大大小
為fileStat.st_size,所以定義字節(jié)大fileStat.st_size*/
Tip:mmap回寫時,回寫字節(jié)最大大小為fileStat.st_size,所以定義字節(jié)大fileStat.st_size。(這個我沒有根據(jù),只是實驗結(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);
/**************/
/*******通過mapChar將數(shù)據(jù)寫入映射區(qū)域*******/
strcpy(mapChar, "writeSrc,writeSrc,writeSrc,writeSrc,writeSrc,writeSrc,");//寫入映射區(qū)域
printfDivLine("通過mapChar將數(shù)據(jù)寫入映射區(qū)域");
printfMapChar("mapChar", mapChar);
/**********checkChar驗證*********/
checkChar = mmap(NULL,fileBufferSize,PROT_READ|PROT_WRITE,MAP_SHARED,fd,0);//獲得映射區(qū)域地址
close(fd);//不使用fd時就可以close
printfDivLine("checkChar驗證");
printfMapChar("checkChar", checkChar);
munmap(mapChar, fileBufferSize);//釋放mapchar的映射,此時文件的映射在內(nèi)存內(nèi)然存在
munmap(checkChar, fileBufferSize);
return 0;
}
評論