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