日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

[单刷 APUE 系列] 第十四章——高级 I/O

發(fā)布時(shí)間:2023/12/4 编程问答 43 豆豆
生活随笔 收集整理的這篇文章主要介紹了 [单刷 APUE 系列] 第十四章——高级 I/O 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

非阻塞I/O

在最前面,我們講過IO分成帶緩沖的IO和不帶緩沖的IO,但是實(shí)際上,這個(gè)區(qū)別并不是很大,因?yàn)榫彌_區(qū)并沒有影響到實(shí)際的讀寫。我們知道,系統(tǒng)調(diào)用實(shí)際上分成兩種,高速的系統(tǒng)調(diào)用和低速的系統(tǒng)調(diào)用,換句話說,低速的調(diào)用會(huì)導(dǎo)致系統(tǒng)永久性阻塞,但是需要注意的是,并不是磁盤IO都是低速調(diào)用。比如open、read、write函數(shù),如果這些操作不能完成就會(huì)立刻出錯(cuò)返回,并不會(huì)導(dǎo)致系統(tǒng)阻塞。在前面的時(shí)候我們也學(xué)到過,如果在open的時(shí)刻,指定O_NONBLOCK,或者在一個(gè)已打開的文件描述符上調(diào)用fcntl函數(shù),附加上O_NONBLOCK參數(shù)。實(shí)際上雖然指定了參數(shù),但是在某些情況下很有可能丟失信息。在大量傳輸信息的時(shí)候容易出現(xiàn)系統(tǒng)調(diào)用大量失敗的情況。

記錄鎖

在很多情況下,我們需要面對(duì)多方一起操作文件的情況,這就是一個(gè)典型的資源競爭沖突,為了保證文件的正確讀寫,Unix系統(tǒng)提供了文件記錄鎖的機(jī)制,也就是上文中提到過的文件記錄鎖。為了提供這個(gè)功能,各個(gè)系統(tǒng)都自行實(shí)現(xiàn)了API,其中,POSIX1.x標(biāo)準(zhǔn)規(guī)定的是fcntl方法,而BSD系列則是規(guī)定flock方法,SystemV在fcntl方法的基礎(chǔ)上構(gòu)建了lockf函數(shù)

fcntl函數(shù)

int fcntl(int fildes, int cmd, ...);The commands available for advisory record locking are as follows:F_GETLK Get the first lock that blocks the lock description pointed to by the third argument, arg, taken as a pointer to a struct flock (see above). The information retrieved overwrites the information passed to fcntl in the flock structure. If no lock is found that would prevent this lock from being created, the structure is left unchanged by this function call except for the lock type which is set to F_UNLCK.F_SETLK Set or clear a file segment lock according to the lock description pointed to by the third argument, arg, taken as a pointer to a struct flock (see above). F_SETLK is used to establish shared (or read) locks (F_RDLCK) or exclusive (or write) locks, (F_WRLCK), as well as remove either type of lock (F_UNLCK). If a shared or exclusive lock cannot be set, fcntl returns immediately with EAGAIN.F_SETLKW This command is the same as F_SETLK except that if a shared or exclusive lock is blocked by other locks, the process waits until the request can be satisfied. If a signal that is to be caught is received while fcntl is waiting for a region, the fcntl will be interrupted if the signal han-dler has not specified the SA_RESTART (see sigaction(2)).復(fù)制代碼

前面也介紹過這個(gè)函數(shù),不過這次會(huì)講解記錄鎖的內(nèi)容,對(duì)于記錄所來說,cmd參數(shù)是F_GETLK、F_SETLK或者FSETLKW,第三個(gè)參數(shù)是一個(gè)紙箱flock結(jié)構(gòu)體的指針

struct flock {off_t l_start; /* starting offset */off_t l_len; /* len = 0 means until end of file */pid_t l_pid; /* lock owner */short l_type; /* lock type: read/write, etc. */short l_whence; /* type of l_start */ };復(fù)制代碼

基本上也不用講解了,注釋早已說明一切。這個(gè)結(jié)構(gòu)體就是通過指定文件區(qū)域和鎖的類型等參數(shù)鎖定文件。不過需要注意的是,l_type實(shí)際上是取值SEEK_SET、SEEK_CUR、或SEEK_END。并且上面提到的類型只有兩種:共享讀鎖和獨(dú)占寫鎖,實(shí)際上就是讀寫鎖。

  • F_GETLK參數(shù)判斷flockptr參數(shù)所描述的鎖是否會(huì)被另一把鎖排斥
  • F_SETLK參數(shù)設(shè)置由flockptr所描述的鎖
  • F_SETLKW這是F_SETLK的阻塞版本

很容易想到,在開發(fā)中肯定是先用F_GETLK參數(shù)測試是否能建立一把鎖,而后使用F_SETLK或者F_SETLKW建立鎖,但是這兩者并不是原子操作,前面已經(jīng)講過,非原子操作很容易導(dǎo)致操作沖突。
在設(shè)置釋放鎖的時(shí)候,內(nèi)核是根據(jù)字節(jié)數(shù)維持鎖的范圍的,也就是說,實(shí)際上內(nèi)核只是維護(hù)了一個(gè)flock結(jié)構(gòu)體的鏈表,然后每次的鎖更改都會(huì)導(dǎo)致鏈表被遍歷并且合并。
對(duì)于記錄鎖的自動(dòng)繼承和釋放有3條規(guī)則:

  • 鎖和進(jìn)程、文件相關(guān)聯(lián),換言之,一個(gè)進(jìn)程結(jié)束的時(shí)候,所有的鎖全部釋放,這實(shí)際上是exit函數(shù)做清理的,第二就是文件描述符關(guān)閉的時(shí)候,該文件所有的鎖都會(huì)關(guān)閉
  • fork產(chǎn)生的子進(jìn)程不繼承父進(jìn)程的鎖。因?yàn)殒i是用于限制多個(gè)進(jìn)程讀寫同一個(gè)文件的,如果fork能繼承鎖,那就起不到約束作用了
  • 執(zhí)行exec后,新程序繼承原執(zhí)行程序的鎖,但是close_on_exec則會(huì)不一樣。
  • 其實(shí)鎖對(duì)數(shù)據(jù)庫這種大量讀寫IO的程序才是最有用的,所以基本上鎖就可以直接考慮數(shù)據(jù)庫的環(huán)境,如果數(shù)據(jù)庫的客戶端庫使用的是同一套鎖機(jī)制,那就能保證文件的共享訪問,但是建議性鎖無法保證其他有權(quán)限存取數(shù)據(jù)庫文件的進(jìn)程讀寫此文件。而強(qiáng)制性鎖則會(huì)讓進(jìn)程檢查每一個(gè)open、read和write函數(shù),驗(yàn)證調(diào)用進(jìn)程是否違背了正在訪問文件的鎖,這就是強(qiáng)制性鎖和建議性鎖的區(qū)別。

    IO多路轉(zhuǎn)接

    前面談到過,對(duì)于內(nèi)核來說,IO只有兩種方式:阻塞和非阻塞,阻塞IO會(huì)導(dǎo)致CPU等待IO從而浪費(fèi)等待時(shí)間,所以系統(tǒng)提供了非阻塞IO,但是非阻塞IO帶來的問題就是完整IO沒有完成,為了獲取完整的數(shù)據(jù),應(yīng)用程序需要重復(fù)調(diào)用IO操作來確認(rèn)是否完成,也就是輪詢。
    當(dāng)從一個(gè)文件描述符讀,然后又寫到另一個(gè)描述符時(shí),通常會(huì)寫出以下代碼

    while ((n = read(STDIN_FILENO, buf, BUFSIZE)) > 0)if (write(STDOUT_FILENO, buf, n) != n)err_sys("write error");復(fù)制代碼

    這種循環(huán)獲取的形式就是輪詢,非常簡單,但是消耗了CPU資源,并且如果需要有更高的要求,比如必須從兩個(gè)文件描述符讀取。
    典型的應(yīng)用就是網(wǎng)絡(luò)守護(hù)進(jìn)程,例如Nginx和Telnet,這里直接拿原著中的Telnet講解,telnet由于存在兩個(gè)輸入兩個(gè)輸出,所以不能使用阻塞式的IO函數(shù),開發(fā)者的第一反應(yīng),應(yīng)該是fork函數(shù),使用兩個(gè)進(jìn)程,每個(gè)進(jìn)程都負(fù)責(zé)一條讀寫通道,但是這就需要進(jìn)程同步,而多線程編程也同樣是這樣的問題。
    另一個(gè)方法就是使用一個(gè)進(jìn)程,但是使用非阻塞IO讀取數(shù)據(jù)。其基本思想很簡單,兩個(gè)描述符都讀取,但是一直處于循環(huán),每次循環(huán)都查詢一次兩個(gè)文件描述符,如果沒有就立刻返回不阻塞,這種循環(huán)就是典型的輪詢,這是種非常常見的技術(shù),實(shí)際上卻是非常浪費(fèi)CPU資源的技術(shù),所以目前,基本開發(fā)以及不能也不推薦了。
    還有幾種技術(shù)就是異步IO,這種技術(shù)實(shí)質(zhì)上就是類似通知,當(dāng)描述符準(zhǔn)備完畢后,進(jìn)程通知內(nèi)核,但是實(shí)際上目前原生API并不能做到移植,所以,目前大部分的開發(fā),包括Node.js等在內(nèi)的網(wǎng)絡(luò)服務(wù),基本都是使用第三方或者自己實(shí)現(xiàn)線程池。不過,目前Linux系統(tǒng)已經(jīng)有了名為AIO的原生異步IO。
    現(xiàn)在目前大部分的使用方式就是IO多路轉(zhuǎn)接,系統(tǒng)構(gòu)造一張鏈表,里面存儲(chǔ)所有的文件描述符,然后調(diào)用函數(shù)偵聽,知道其中一個(gè)已經(jīng)準(zhǔn)備完畢的時(shí)候返回。poll、pselect和select三個(gè)函數(shù)就是這樣執(zhí)行的。

    select和pselect函數(shù)

    這連個(gè)函數(shù)是POSIX規(guī)定的

    int select(int nfds, fd_set *restrict readfds, fd_set *restrict writefds, fd_set *restrict errorfds, struct timeval *restrict timeout); int pselect(int nfds, fd_set *restrict readfds, fd_set *restrict writefds, fd_set *restrict errorfds, const struct timespec *restrict timeout, const sigset_t *restrict sigmask);復(fù)制代碼

    第一個(gè)參數(shù)nfds的意思就是“最大文件描述符編號(hào)值+1”,因?yàn)槲募枋龇际菑?開始的,從后面readfds、writefds、errorfds中找出最大描述符編號(hào)值并+1就是這個(gè)參數(shù)的值,中間三個(gè)參數(shù)是指向描述符集的指針,使用fd_set數(shù)據(jù)結(jié)構(gòu)表示,實(shí)際上有下列五個(gè)函數(shù)

    void FD_CLR(fd, fd_set *fdset); void FD_COPY(fd_set *fdset_orig, fd_set *fdset_copy); void FD_ISSET(fd, fd_set *fdset); void FD_SET(fd, fd_set *fdset); void FD_ZERO(fd_set *fdset);復(fù)制代碼

    是不是發(fā)現(xiàn)比原著多了一個(gè)FD_COPY函數(shù),實(shí)際上就是復(fù)制用的,無關(guān)緊要。最后一個(gè)參數(shù)就是制定愿意等待的時(shí)間長度,使用timeval結(jié)構(gòu)體,也就是可以指定秒和微妙單位。

  • timeout == NULL,永遠(yuǎn)等待
  • timeout->tv_sec == 0 && timeout->tv_usec == 0,不等待
  • timeout->tv_sec != 0 || timeout->tv_usec != 0,等待指定時(shí)間
  • select實(shí)際上和描述符本身阻塞無關(guān),它只是簡化了我們監(jiān)聽一堆文件描述符的繁瑣操作,除了select以外,上面還有一個(gè)select的變體pselect,pselect和select很像,但是select得超時(shí)值用timeval結(jié)構(gòu)體定義,pselect使用timespec結(jié)構(gòu),pselect可使用可選信號(hào)屏蔽字,如果sigmask為null,則兩者一樣,但是sigmask指向屏蔽字的時(shí)候,將以原子操作形式安裝屏蔽字。

    poll函數(shù)

    除了select以外,大家應(yīng)該還見過poll函數(shù)

    int poll(struct pollfd fds[], nfds_t nfds, int timeout);復(fù)制代碼

    看起來poll函數(shù)相對(duì)于select更加簡潔易懂,select函數(shù)對(duì)三種類型都指定了參數(shù)用于構(gòu)造描述符集,但是poll函數(shù)使用的則是pollfd結(jié)構(gòu)體數(shù)組,pollfd結(jié)構(gòu)體如下

    struct pollfd {int fd; /* file descriptor */short events; /* events to look for */short revents; /* events returned */ };復(fù)制代碼

    nfds參數(shù)指定了fds數(shù)組的大小,從上面的注釋中應(yīng)該也看得出來結(jié)構(gòu)體究竟是怎么構(gòu)造的,events是我們關(guān)心fd的事件,而revents則是內(nèi)核設(shè)置,返回的時(shí)候用于說明每個(gè)描述符發(fā)生了哪些事件。

    The event bitmasks in events and revents have the following bits:POLLERR An exceptional condition has occurred on the device or socket. This flag is output only, and ignored if present in the input events bitmask.POLLHUP The device or socket has been disconnected. This flag is output only, and ignored if present in the input events bitmask. Note that POLLHUPand POLLOUT are mutually exclusive and should never be present in the revents bitmask at the same time.POLLIN Data other than high priority data may be read without blocking. This is equivalent to ( POLLRDNORM | POLLRDBAND ).POLLNVAL The file descriptor is not open. This flag is output only, and ignored if present in the input events bitmask.POLLOUT Normal data may be written without blocking. This is equivalent to POLLWRNORM.POLLPRI High priority data may be read without blocking.POLLRDBAND Priority data may be read without blocking.POLLRDNORM Normal data may be read without blocking.POLLWRBAND Priority data may be written without blocking.POLLWRNORM Normal data may be written without blocking.復(fù)制代碼

    上面是兩個(gè)參數(shù)可取的值,每個(gè)系統(tǒng)實(shí)現(xiàn)可能存在偏差,所以需要自行嘗試。

    異步I/O

    前面講過,非阻塞IO帶來的就是輪詢,前面內(nèi)容包括前面的章節(jié)整合一下,可以歸納出以下主流輪詢技術(shù):

  • read,最原始,性能最低的一種,重復(fù)檢查IO狀態(tài)來完成完整數(shù)據(jù)的讀取,也就是前面一小節(jié)的開頭代碼
  • select,在read基礎(chǔ)上改進(jìn)的方案,通過對(duì)文件描述符上的事件狀態(tài)判斷
  • poll,使用鏈表作為文件描述符的存儲(chǔ)方式,和select類似
  • epoll,目前Linux下最高效的IO事件通知機(jī)制,進(jìn)入輪詢時(shí)候如果沒有檢查到IO事件就會(huì)休眠,直到事件將其喚醒
  • queue,和epoll類似,不過是FreeBSD下的
  • 雖然輪詢滿足了非阻塞IO獲取完整數(shù)據(jù)的需求,但是依舊是同步的,也需要花費(fèi)CPU用于便利文件描述符或者休眠等待事件發(fā)生。所以就有了異步IO,目前據(jù)筆者所知,只有Linux下有AIO技術(shù)算是真正原生提供的API。
    但是,實(shí)際上,是有模擬方式的,信號(hào)機(jī)構(gòu)提供了異步形式通知事件發(fā)生的方法,使用一個(gè)信號(hào)通知進(jìn)程,但是,由于信號(hào)是有限的,如果使用一個(gè)信號(hào),則進(jìn)程不知道是哪個(gè)文件描述符發(fā)生的事件,如果用多個(gè)信號(hào),文件描述符的數(shù)量可能遠(yuǎn)遠(yuǎn)超出信號(hào)的數(shù)量。
    實(shí)際上,最容易想到的辦法就是多線程。讓部分線程進(jìn)行阻塞IO或者非阻塞IO加輪詢技術(shù)來完成數(shù)據(jù)獲取,讓另一個(gè)線程進(jìn)行計(jì)算,而后通過線程間通信將IO得到的數(shù)據(jù)進(jìn)行傳遞,就能輕松實(shí)現(xiàn)異步IO。

    SystemV異步IO

    SystemV中異步IO是歸屬給STREAMS系統(tǒng)的,他只能用于STREAMS設(shè)備和管道,異步IO信號(hào)是SIGPOLL。實(shí)際上由于這種機(jī)制本身的限制,目前已經(jīng)找不到Unix環(huán)境會(huì)去采用它了,所以這里也不需要再講解了。

    BSD異步IO

    對(duì)于BSD系列的系統(tǒng)來說,異步IO信號(hào)是SIGIO和SIGURG信號(hào)的組合,SIGIO是通用異步IO的信號(hào),SIGURG則是通知網(wǎng)絡(luò)連接的數(shù)據(jù)已經(jīng)到達(dá)。

    POSIX異步IO

    POSIX標(biāo)準(zhǔn)對(duì)不同類型文件異步IO提供了可移植的模型,異步IO使用AIO控制塊來描述IO操作。

    struct aiocb {int aio_fildes; /* File descriptor */off_t aio_offset; /* File offset */volatile void *aio_buf; /* Location of buffer */size_t aio_nbytes; /* Length of transfer */int aio_reqprio; /* Request priority offset */struct sigevent aio_sigevent; /* Signal number and value */int aio_lio_opcode; /* Operation to be performed */ };復(fù)制代碼

    上面是蘋果系統(tǒng)下的AIO控制塊實(shí)現(xiàn),實(shí)際上和POSIX規(guī)定幾乎一樣,它是繼承于FreeBSD3.0的AIO實(shí)現(xiàn),
    從上面可以看出,每個(gè)字段究竟的意義,aio_fildes就是文件描述符,讀寫操作從aio_offset指定的偏移量位置開始,對(duì)于讀操作,會(huì)將數(shù)據(jù)復(fù)制到aio_buf的緩沖區(qū)內(nèi),對(duì)于寫操作,會(huì)從這個(gè)緩沖區(qū)寫入磁盤,aio_nbytes字段指定了讀寫的字節(jié)數(shù)。
    除了上面4個(gè)字段以外,aio_reqprio就是異步IO請(qǐng)求的順序,aio_sigevent就是IO事件完成后如何通知,而aio_lio_opcode就是執(zhí)行的操作。

    struct sigevent {int sigev_notify; /* Notification type */int sigev_signo; /* Signal number */union sigval sigev_value; /* Signal value */void (*sigev_notify_function)(union sigval); /* Notification function */pthread_attr_t *sigev_notify_attributes; /* Notification attributes */ };復(fù)制代碼

    sigevent結(jié)構(gòu)體是歸屬于signal信號(hào)機(jī)制模型中的數(shù)據(jù)結(jié)構(gòu),其中sigev_notify字段是通知類型

    • SIGEV_NONE 不通知進(jìn)程
    • SIGEV_SIGNAL 異步IO完成后,產(chǎn)生sigev_signo指定的信號(hào),
    • SIGEV_THREAD 異步請(qǐng)求完成后,由sigev_notify_function指定的函數(shù)被調(diào)用
    int aio_read(struct aiocb *aiocbp); int aio_write(struct aiocb *aiocbp);復(fù)制代碼

    在異步IO之前需要先初始化AIO控制塊,當(dāng)函數(shù)返回成功時(shí)候,異步IO請(qǐng)求就已經(jīng)被放在了等待處理隊(duì)列中。這些返回值與實(shí)際IO擦做的結(jié)果沒有任何關(guān)系,如果想要強(qiáng)制所有等待中的異步操作不等待直接寫入存儲(chǔ),則調(diào)用aio_fsync函數(shù)
    當(dāng)然,好像aio_fsync函數(shù)并不是非常廣泛,所以在使用的時(shí)候記得運(yùn)行時(shí)檢查。
    為了獲取一個(gè)異步讀寫的完成狀態(tài),可以調(diào)用aio_error函數(shù)

    int aio_error(const struct aiocb *aiocbp);復(fù)制代碼

    返回如下:

  • 返回值為0,異步操作成功,使用aio_return函數(shù)獲得返回值
  • 返回值為-1,對(duì)aio_error操作失敗
  • 返回值為EINPROGRESS,讀寫操作仍處于等待狀態(tài)
  • ssize_t aio_return(struct aiocb *aiocbp);復(fù)制代碼

    記住在aio_error檢查已經(jīng)成功之前,不要調(diào)用aio_return函數(shù),而且需要當(dāng)心每個(gè)異步操作只能調(diào)用一次aio_return函數(shù)。
    如果在其他操作完成之后,異步操作還未完成,那可以使用

    int aio_suspend(const struct aiocb *const list[], int nent, const struct timespec *timeout);復(fù)制代碼

    aio_suspend函數(shù)會(huì)阻塞當(dāng)前進(jìn)程直到操作完成,一般情況下很少會(huì)使用。
    如果我們想要取消已經(jīng)處于進(jìn)行中的異步操作,可以使用如下函數(shù)

    int aio_cancel(int fildes, struct aiocb *aiocbp);復(fù)制代碼

    這個(gè)函數(shù)會(huì)返回4個(gè)返回值:

  • AIO_ALLDONE,所有操作已經(jīng)完成
  • AIO_CANCELED,所有操作已經(jīng)取消
  • AIO_NOtCANCELED,至少有一個(gè)請(qǐng)求沒有取消
  • -1,函數(shù)本身失敗
  • 除了上述函數(shù)以外,還有一個(gè)函數(shù)也被包含在異步請(qǐng)求函數(shù)中,但是實(shí)際上很少見到,所以這里就不多做講解。

    readv和writev函數(shù)

    ssize_t readv(int d, const struct iovec *iov, int iovcnt); ssize_t writev(int fildes, const struct iovec *iov, int iovcnt);復(fù)制代碼

    這兩個(gè)函數(shù)用于在一次讀寫中讀寫多個(gè)非連續(xù)的緩沖區(qū),也就是說可以將傳統(tǒng)的多個(gè)函數(shù)讀寫調(diào)用壓縮到一個(gè),這連個(gè)函數(shù)第二個(gè)參數(shù)就是一個(gè)指向iovec結(jié)構(gòu)體的指針,實(shí)際上是一個(gè)指向數(shù)組的指針

    struct iovec {char *iov_base; /* Base address. */size_t iov_len; /* Length. */ };復(fù)制代碼

    第三個(gè)參數(shù)就是數(shù)組的長度。iov數(shù)組中的元素最大值就是IOV_MAX。

    存儲(chǔ)映射IO

    存儲(chǔ)映射IO能將一個(gè)磁盤文件映射到存儲(chǔ)空間中的一個(gè)緩沖區(qū)上,于是,當(dāng)從緩沖區(qū)中讀取數(shù)據(jù)的時(shí)候,就等同于讀取文件。Unix系統(tǒng)提供了此類函數(shù)

    void *mmap(void *addr, size_t len, int prot, int flags, int fd, off_t offset);復(fù)制代碼

    addr指定映射存儲(chǔ)區(qū)的起始地址。通常為0,也就是系統(tǒng)自動(dòng)分配區(qū)域。fd參數(shù)指定被映射文件的文件描述符,也就代表必須先打開這個(gè)文件。prot參數(shù)指定了映射存儲(chǔ)區(qū)的保護(hù)要求如下:
    |prot|說明|
    |----|---|
    |PROT_READ|存儲(chǔ)區(qū)可讀|
    |PROT_WRITE|存儲(chǔ)區(qū)可寫|
    |PROT_EXEC|存儲(chǔ)區(qū)可執(zhí)行|
    |PROT_NONE|存儲(chǔ)區(qū)不可訪問|
    當(dāng)然,這個(gè)參數(shù)的指定必然是基于文件描述符的打開方式的,很容易明白,因?yàn)榇鎯?chǔ)映射IO技術(shù)本質(zhì)上還是基于文件描述符的,所以不可能繞過文件描述符的限制讀寫。
    flag參數(shù)影響映射存儲(chǔ)區(qū)的多種屬性,如下就是可選值:

    MAP_ANONYMOUS Synonym for MAP_ANON.MAP_ANON Map anonymous memory not associated with any specific file. The offset argument is ignored. Mac OS X specific: the file descriptor usedfor creating MAP_ANON regions can be used to pass some Mach VM flags, and can be specified as -1 if no such flags are associated with theregion. Mach VM flags are defined in <mach/vm_statistics.h> and the ones that currently apply to mmap are:VM_FLAGS_PURGABLE to create Mach purgable (i.e. volatile) memoryVM_MAKE_TAG(tag) to associate an 8-bit tag with the region<mach/vm_statistics.h> defines some preset tags (with a VM_MEMORY_ prefix). Users are encouraged to use tags between 240 and 255. Tagsare used by tools such as vmmap(1) to help identify specific memory regions.VM_FLAGS_SUPERPAGE_SIZE_* to use superpages for the allocation. See <mach/vm_statistics.h> for supported architectures and sizes (oruse VM_FLAGS_SUPERPAGE_SIZE_ANY to have the kernel choose a size). The specified size must be divisible by the superpage size (except forVM_FLAGS_SUPERPAGE_SIZE_ANY), and if you use MAP_FIXED, the specified address must be properly aligned. If the system cannot satisfy therequest with superpages, the call will fail. Note that currently, superpages are always wired and not inherited by children of the process.MAP_FILE Mapped from a regular file. (This is the default mapping type, and need not be specified.)MAP_FIXED Do not permit the system to select a different address than the one specified. If the specified address cannot be used, mmap() will fail.If MAP_FIXED is specified, addr must be a multiple of the pagesize. If a MAP_FIXED request is successful, the mapping established bymmap() replaces any previous mappings for the process' pages in the range from addr to addr + len. Use of this option is discouraged.MAP_HASSEMAPHORE Notify the kernel that the region may contain semaphores and that special handling may be necessary.MAP_PRIVATE Modifications are private (copy-on-write).MAP_SHARED Modifications are shared.MAP_NOCACHE Pages in this mapping are not retained in the kernel's memory cache. If the system runs low on memory, pages in MAP_NOCACHE mappings willbe among the first to be reclaimed. This flag is intended for mappings that have little locality and provides a hint to the kernel thatpages in this mapping are unlikely to be needed again in the near future.復(fù)制代碼

    這就不講解了,原著上已經(jīng)講解的足夠清楚了。
    調(diào)用mprotect可以更改現(xiàn)有映射的權(quán)限

    int mprotect(void *addr, size_t len, int prot);復(fù)制代碼

    也就是一個(gè)修改映射區(qū)域權(quán)限的函數(shù),當(dāng)頁已經(jīng)修改完畢,可以調(diào)用msync函數(shù)沖洗到被映射的文件中。

    int msync(void *addr, size_t len, int flags);復(fù)制代碼

    基本就和fsync函數(shù)差不多,也不多說了,基本上都在Unix手冊(cè)上
    當(dāng)進(jìn)程終止的之后,自然會(huì)自動(dòng)解除存儲(chǔ)區(qū)的映射,或者可以調(diào)用munmap函數(shù)解除

    int munmap(void *addr, size_t len);復(fù)制代碼

    munmap函數(shù)刪除了指定地址的映射,如果繼續(xù)對(duì)其進(jìn)行讀寫會(huì)導(dǎo)致無效內(nèi)存引用。并且這個(gè)函數(shù)不會(huì)沖洗緩沖區(qū)內(nèi)容到文件,所以需要小心使用。

    創(chuàng)作挑戰(zhàn)賽新人創(chuàng)作獎(jiǎng)勵(lì)來咯,堅(jiān)持創(chuàng)作打卡瓜分現(xiàn)金大獎(jiǎng)

    總結(jié)

    以上是生活随笔為你收集整理的[单刷 APUE 系列] 第十四章——高级 I/O的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。