模型
#include
shm_open
//創(chuàng)建/獲取共享內(nèi)存的文件描述符,成功返回文件描述符,失敗返回-1//Link with -lrt.int shm_open(const char *name, int oflag, mode_t mode);
oflag
- Access Mode:
- O_RDONLY以只讀的方式打開(kāi)共享內(nèi)存對(duì)象
- O_RDWR以讀寫(xiě)的方式打開(kāi)共享內(nèi)存對(duì)象
- Opening-time flags(Bitwise Or):
- O_CREAT?表示創(chuàng)建共享內(nèi)存對(duì)象,剛被創(chuàng)建的對(duì)象會(huì)被初始化為0byte可以使用ftuncate()調(diào)整大小
- O_EXCL用來(lái)確保共享內(nèi)存對(duì)象被成功創(chuàng)建,如果對(duì)象已經(jīng)存在,那么返回錯(cuò)誤
- O_TRUNC表示如果共享內(nèi)存對(duì)象已經(jīng)存在那么把它清空
mode: eg,0664 etc
ftruncate()
//調(diào)整fd指向文件的大小,成功返回0,失敗返回-1設(shè)errno//VS truncate()int ftruncate(int fd, off_t length);
如果原文件大小>指定大小,原文件中多余的部分會(huì)被截除
int res=ftruncate(fd,3*sizeof(Emp));//要用sizeof,且是Emp(類(lèi)型)不是emp(對(duì)象)if(-1==res) perror("ftruncate"),exit(-1);
fstat()
//獲取文件狀態(tài),成功返回0,失敗返回-1設(shè)errno//VS stat()int lstat(const char *pathname, struct stat *buf);int fstat(int fd, struct stat *buf);
buf:stat類(lèi)型的指針
struct stat { dev_t st_dev; /* ID of device containing file */ ino_t st_ino; /* inode number */ mode_t st_mode; /* protection */ ?八進(jìn)制 usigned int ?o% nlink_t st_nlink; /* number of hard links */ uid_t st_uid; /* user ID of owner */ gid_t st_gid; /* group ID of owner */ dev_t st_rdev; /* device ID (if special file) */ off_t st_size; /* total size, in bytes */ ?ld% blksize_t st_blksize; /* blocksize for filesystem I/O */ blkcnt_t st_blocks; /* number of 512B blocks allocated */ struct timespec st_atim; /* time of last access */ struct timespec st_mtim; /* time of last modification */ ?ld%,秒 struct timespec st_ctim; /* time of last status change */};
//eg:st_mode=100664 //100是文件類(lèi)型 //664是權(quán)限, 通過(guò)100664和0777BitwiseAND得到st_mtime=1462787968 //秒
mmap()
//映射文件或設(shè)備到進(jìn)程的虛擬內(nèi)存空間,映射成功后對(duì)相應(yīng)的文件或設(shè)備操作就相當(dāng)于對(duì)內(nèi)存的操作//映射以頁(yè)為基本單位,文件大小, mmap的參數(shù) len 都不能決定進(jìn)程能訪問(wèn)的大小, 而是容納文件被映射部分的最小頁(yè)面數(shù)決定傳統(tǒng)文件訪問(wèn)//要求對(duì)文件進(jìn)行可讀可寫(xiě)的的打開(kāi)!!!//成功返回映射區(qū)的指針,失敗返回-1設(shè)errno void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset); //prot:protection, 權(quán)限
addr:映射的起始地址, 如果為NULL則由kernel自行選擇->最合適的方法
length:映射的區(qū)域長(zhǎng)度
prot:映射內(nèi)存的保護(hù)權(quán)限
- PROT_EXEC表示映射的內(nèi)存頁(yè)可執(zhí)行
- PROT_READ表示映射的內(nèi)存可被讀
- PROT_WRITE表示映射的內(nèi)存可被寫(xiě)
- PROT_NONE表示映射的內(nèi)存不可訪問(wèn)
flags
must include one of :
- MAP_SHARED表示共享這塊映射的內(nèi)存,讀寫(xiě)這塊內(nèi)存相當(dāng)于直接讀寫(xiě)文件,這些操作對(duì)其他進(jìn)程可見(jiàn),由于OS對(duì)文件的讀寫(xiě)都有緩存機(jī)制,所以實(shí)際上不會(huì)立即將更改寫(xiě)入文件,除非帶哦用msync()或mumap()
- MAP_PRIVATE表示創(chuàng)建一個(gè)私有的copy-on-write的映射, 更新映射區(qū)對(duì)其他映射到這個(gè)文件的進(jìn)程是不可見(jiàn)的
can be Bitwise ORed:
- MAP_32BIT把映射區(qū)的頭2GB個(gè)字節(jié)映射到進(jìn)程的地址空間,僅限域x86-64平臺(tái)的64位程序,在早期64位處理器平臺(tái)上,可以用來(lái)提高上下文切換的性能。當(dāng)設(shè)置了MAP_FIXED時(shí)此選項(xiàng)自動(dòng)被忽略
- MAP_ANONYMOUS映射不會(huì)備份到任何文件,fd和offset參數(shù)都被忽略,通常和MAP_SHARED連用
- MAP_DENYWRITEignored.
- MAP_EXECUTABLEignored
- MAP_FILE用來(lái)保持兼容性,ignored
- MAP_FIXED不要對(duì)addr參數(shù)進(jìn)行處理確確實(shí)實(shí)的放在addr指向的地址,此時(shí)addr一定時(shí)頁(yè)大小的整數(shù)倍,
- MAP_GROWSDOWN用在棧中,告訴VMM映射區(qū)應(yīng)該向低地址擴(kuò)展
- MAP_HUGETLB?(since Linux 2.6.32)用于分配"大頁(yè)"
fd: file decriptor
offset: 文件中的偏移量
void* pv=mmap(NULL,4,PROT_READ|PROT_WRITE,MAP_PRIVATE|MAP_ANONYMOUS,0,0);if(MAP_FAILED==pv) perror("mmap"),exit(-1);
映射機(jī)制小解
- mmap()就是建立一個(gè)指針,這個(gè)指針指向頁(yè)高速緩存的一頁(yè),并假設(shè)這個(gè)頁(yè)有我們想要訪問(wèn)的文件內(nèi)容(此時(shí)都在虛擬地址空間),當(dāng)然,這個(gè)頁(yè)描述符會(huì)自動(dòng)的加入的調(diào)用進(jìn)程的頁(yè)表中。當(dāng)我們第一次使用這個(gè)指針,去訪問(wèn)這個(gè)虛擬地址的頁(yè)時(shí),發(fā)現(xiàn)這個(gè)頁(yè)還沒(méi)有分配物理頁(yè)框,沒(méi)有想要的文件,引起缺頁(yè)中斷,系統(tǒng)會(huì)把相應(yīng)的(fd)文件內(nèi)容放到高速緩存的物理頁(yè)框(此時(shí)才會(huì)有對(duì)物理地址空間的讀寫(xiě))
- 映射過(guò)程中使用的文件相當(dāng)于藥引子,因?yàn)樗羞M(jìn)程都是可以通過(guò)VFS訪問(wèn)磁盤(pán)文件的,所以這個(gè)文件相當(dāng)于對(duì)映射內(nèi)存的一個(gè)標(biāo)識(shí),有了這個(gè)位于磁盤(pán)的藥引子,很多進(jìn)程都可以根據(jù)它找到同一個(gè)物理頁(yè)框,進(jìn)而實(shí)現(xiàn)內(nèi)存的共享,并不是說(shuō)就在磁盤(pán)上讀寫(xiě)
- 頁(yè)高速緩存的內(nèi)容不會(huì)立即寫(xiě)到磁盤(pán)中,會(huì)等幾秒,這種機(jī)制可以提高效率并保護(hù)磁盤(pán)
- 只要內(nèi)存夠大,頁(yè)高速緩存的內(nèi)容就會(huì)一直存在內(nèi)存中,以后再有進(jìn)程對(duì)該緩存頁(yè)的內(nèi)容訪問(wèn)的需求,就不需要從磁盤(pán)中搜索,直接訪問(wèn)緩存頁(yè)(把這個(gè)頁(yè)加入到進(jìn)程的頁(yè)表)就行
- mmap()是系統(tǒng)調(diào)用,使用一次的開(kāi)銷(xiāo)比較大,但比文件讀寫(xiě)的read()/write()進(jìn)行內(nèi)核空間到用戶空間的數(shù)據(jù)復(fù)制要快,通常只有需要分配的內(nèi)存>128KB(malloc一次分配33page就是128KB)的時(shí)候才會(huì)使用mmap()
- /shm是一個(gè)特殊的文件系統(tǒng),它不對(duì)應(yīng)磁盤(pán)中的區(qū)域,而是內(nèi)存中,所以使用mmap()在這個(gè)文件系統(tǒng)中創(chuàng)
- /proc 不占用任何磁盤(pán)空間
- linux采用的是頁(yè)式管理機(jī)制。對(duì)于用mmap()映射普通文件來(lái)說(shuō),進(jìn)程會(huì)在自己的地址空間新增一塊空間,空間大小由mmap()的len參數(shù)指定,注意,進(jìn)程并不一定能夠?qū)θ啃略隹臻g都能進(jìn)行有效訪問(wèn)。進(jìn)程能夠訪問(wèn)的有效地址大小取決于文件被映射部分的大小。
- 簡(jiǎn)單的說(shuō),能夠容納文件被映射部分大小的最少頁(yè)面?zhèn)€數(shù)決定了進(jìn)程從mmap()返回的地址開(kāi)始,能夠有效訪問(wèn)的地址空間大小。超過(guò)這個(gè)空間大小,內(nèi)核會(huì)根據(jù)超過(guò)的嚴(yán)重程度返回發(fā)送不同的信號(hào)給進(jìn)程。
- 經(jīng)過(guò)內(nèi)核!=在內(nèi)核空間和用戶空間來(lái)回切換!=在內(nèi)核空間和用戶空間傳遞復(fù)制的數(shù)據(jù)
- 頁(yè)是內(nèi)存映射的基本單位, 可以理解為實(shí)際分配給物理內(nèi)存的基本單位, 但不是數(shù)據(jù)操作的基本單位;
- 頁(yè)機(jī)制是操作系統(tǒng)和CPU約定好的一種方式,OS按照頁(yè)給CPU按頁(yè)發(fā)虛擬地址,CPU按頁(yè)解析并處理
- 操作系統(tǒng)(包括Linux)大量使用的緩存的兩個(gè)原理:
- CPU訪問(wèn)內(nèi)存的速度遠(yuǎn)遠(yuǎn)大于訪問(wèn)磁盤(pán)的速度(訪問(wèn)速度差距不是一般的大,差好幾個(gè)數(shù)量級(jí))
- 數(shù)據(jù)一旦被訪問(wèn),就有可能在短期內(nèi)再次被訪問(wèn)(臨時(shí)局部原理)
- 頁(yè)高速緩存(page cache)是個(gè)內(nèi)存區(qū)域,是Linux 內(nèi)核使用的主要磁盤(pán)高速緩存,在絕大多數(shù)情況下,內(nèi)核在讀寫(xiě)磁盤(pán)時(shí)都引用頁(yè)高速緩存,新頁(yè)被追加到頁(yè)高速緩存以滿足用戶態(tài)進(jìn)程的讀請(qǐng)求,如果頁(yè)不在高速緩存中,新頁(yè)就被加到高速緩存中,然后就從磁盤(pán)讀出的數(shù)據(jù)填充它,如果內(nèi)存有足夠的空閑空間,就讓該頁(yè)在高速緩存中長(zhǎng)期保留,使其他進(jìn)程再使用該頁(yè)時(shí)不再訪問(wèn)磁盤(pán), 即磁盤(pán)上的文件緩存到內(nèi)存后,它的虛擬內(nèi)存地址可以有多個(gè),但是物理內(nèi)存地址卻只能有一個(gè)
- 我們要讀寫(xiě)磁盤(pán)文件時(shí),實(shí)質(zhì)是對(duì)頁(yè)高速緩存進(jìn)行讀寫(xiě),所以無(wú)論讀寫(xiě),都會(huì)首先檢查頁(yè)高速緩存有沒(méi)有這個(gè)文件對(duì)應(yīng)的頁(yè),如果有,就直接訪問(wèn),如果沒(méi)有,就引起缺頁(yè)中斷,給OS發(fā)信號(hào),讓它把文件放到高速緩存再進(jìn)行讀寫(xiě),這個(gè)過(guò)程不經(jīng)過(guò)內(nèi)核空間到用戶空間復(fù)制數(shù)據(jù)
- OS中的頁(yè)機(jī)制,對(duì)應(yīng)到硬件中可不一定在主存中,也可以是高速緩存etc,但不會(huì)是磁盤(pán),因?yàn)榇疟P(pán)文件的地址和內(nèi)存不一樣,不是按照32位編址的,而是按照ext2 etc方式編址的,需要使用文件管理系統(tǒng),在Linux中使用VFS和實(shí)際文件管理系統(tǒng)來(lái)管理文件,所以對(duì)于Linux,有兩個(gè)方式使用系統(tǒng)資源:VMM,VFS,前者用來(lái)管理絕大部分的內(nèi)存,后者用來(lái)管理所有的文件和部分特殊文件系統(tǒng)(eg:/shm是內(nèi)存的一塊區(qū)域)
- page cache可以看作二者的橋梁,把磁盤(pán)文件放到高速緩存,就可以按照內(nèi)存的使用方式使用磁盤(pán)的文件,使用完再釋放或?qū)懟卮疟P(pán)page cache中的頁(yè)可能是下面的類(lèi)型:
- 含有普通文件數(shù)據(jù)的頁(yè)
- 含有目錄的頁(yè)
- 含有直接從塊設(shè)備(跳過(guò)文件系統(tǒng)層)讀出的數(shù)據(jù)的頁(yè)
- 含有用戶態(tài)進(jìn)程數(shù)據(jù)的頁(yè),但頁(yè)中的數(shù)據(jù)已經(jīng)被交換到硬盤(pán)
- 屬于他書(shū)文件系統(tǒng)文件的頁(yè)
- 映射:一個(gè)線性區(qū)可以和磁盤(pán)文件系統(tǒng)的普通文件的某一部分或者塊設(shè)備文件相關(guān)聯(lián),這就意味著內(nèi)核把對(duì)區(qū)線性中頁(yè)內(nèi)某個(gè)字節(jié)的訪問(wèn)轉(zhuǎn)換成對(duì)文件中相應(yīng)字節(jié)的操作
- TLB(Translation Lookaside Buffer)高速緩存用于加快線性地址的轉(zhuǎn)換,當(dāng)一個(gè)線性地址第一次被使用時(shí),通過(guò)慢速訪問(wèn)RAM中的頁(yè)表計(jì)算出相應(yīng)的物理地址,同時(shí),物理地址被存放在TLB表項(xiàng)(TLB entry),以便以后對(duì)同一個(gè)線性地址的引用可以快速得到轉(zhuǎn)換
- 在初始化階段,內(nèi)核必須建立一個(gè)物理地址映射來(lái)指定哪些物理地址范圍對(duì)內(nèi)核可用,哪些不可用
- swap(內(nèi)存交換空間)的功能是應(yīng)付物理內(nèi)存不足的情況下所造成的內(nèi)存擴(kuò)展記錄的功能,CPU所讀取的數(shù)據(jù)都來(lái)自于內(nèi)存,那當(dāng)內(nèi)存不足的時(shí)候,為了讓后續(xù)的程序可以順序運(yùn)行,因此在內(nèi)存中暫不使用的程序和數(shù)據(jù)就會(huì)被挪到swap中,此時(shí)內(nèi)存就會(huì)空出來(lái)給需要執(zhí)行的程序加載,由于swap是使用硬盤(pán)來(lái)暫時(shí)放置內(nèi)存中的信息,所以用到swap時(shí),主機(jī)硬盤(pán)燈就會(huì)開(kāi)始閃個(gè)不同
- Q:CPU只能對(duì)內(nèi)存進(jìn)行讀寫(xiě),但又是怎么讀寫(xiě)硬盤(pán)的呢???A:把數(shù)據(jù)寫(xiě)入page cache,再經(jīng)由。。。寫(xiě)入磁盤(pán)(包括swap) --《鳥(niǎo)哥》P10
- 內(nèi)存本身沒(méi)有計(jì)算能力,尋址之類(lèi)的都是CPU的事,只是為了簡(jiǎn)便起見(jiàn),我們通常畫(huà)成從內(nèi)存地址A跳到內(nèi)存地址B
- OS是軟件的核心,CPU是執(zhí)行的核心
- 前者給后者發(fā)指令我要干什么,CPU把他的指令變成現(xiàn)實(shí)
- 二者必須很好的匹配計(jì)算機(jī)才能很好的工作
- Linux內(nèi)核中與文件Cache操作相關(guān)的API有很多,按其使用方式可以分成兩類(lèi):一類(lèi)是以拷貝方式操作的相關(guān)接口, 如read/write/sendfile等,其中sendfile在2.6系列的內(nèi)核中已經(jīng)不再支持;另一類(lèi)是以地址映射方式操作的相關(guān)接口,如mmap等。
- 第一種類(lèi)型的API在不同文件的Cache之間或者Cache與應(yīng)用程序所提供的用戶空間buffer之間拷貝數(shù)據(jù),其實(shí)現(xiàn)原理如圖7所示。
- 第二種類(lèi)型的API將Cache項(xiàng)映射到用戶空間,使得應(yīng)用程序可以像使用內(nèi)存指針一樣訪問(wèn)文件,Memory map訪問(wèn)Cache的方式在內(nèi)核中是采用請(qǐng)求頁(yè)面機(jī)制實(shí)現(xiàn)的,首先,應(yīng)用程序調(diào)用mmap(),陷入到內(nèi)核中后調(diào)用do_mmap_pgoff。該函數(shù)從應(yīng)用程序的地址空間中分配一段區(qū)域作為映射的內(nèi)存地址,并使用一個(gè)VMA(vm_area_struct)結(jié)構(gòu)代表該區(qū)域,之后就返回到應(yīng)用程。當(dāng)應(yīng)用程序訪問(wèn)mmap所返回的地址指針時(shí)(圖中4),由于虛實(shí)映射尚未建立,會(huì)觸發(fā)缺頁(yè)中斷。之后系統(tǒng)會(huì)調(diào)用缺頁(yè)中斷處理函數(shù),在缺頁(yè)中斷處理函數(shù)中,內(nèi)核通過(guò)相應(yīng)區(qū)域的VMA結(jié)構(gòu)判斷出該區(qū)域?qū)儆谖募成?,于是調(diào)用具體文件系統(tǒng)的接口讀入相應(yīng)的Page Cache項(xiàng),并填寫(xiě)相應(yīng)的虛實(shí)映射表。經(jīng)過(guò)這些步驟之后,應(yīng)用程序就可以正常訪問(wèn)相應(yīng)的內(nèi)存區(qū)域了。
mumap()
//接觸文件或設(shè)備對(duì)內(nèi)存的映射,成功返回0,失敗返回-1設(shè)errnoint munmap(void *addr, size_t length);
shm_unlink()
//關(guān)閉進(jìn)程打開(kāi)的共享內(nèi)存對(duì)象,成功返回0,失敗返回-1//Link with -lrt.int shm_unlink(const char *name);
評(píng)論
查看更多