linux 系統編程 文件IO
Fileio
1.open() 系統調用
頭文件
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
原型
int open(const char * name,int flags);
int open(const char * name,int flags,mode_t mode);
flags O_RDONLY O_WRONLY O_RDWR
O_APPEND每次寫操作都寫入文件的末尾
O_CREAT如果指定文件不存在,則創建這個文件
O_EXCL如果要創建的文件已存在,則返回-1,並且修改errno的值
O_TRUNC如果文件存在,並且允許寫,則清空文件全部內容(即將其長度截短為0)
O_NOCTTY如果路徑名指向終端設備,不要把這個設備用作控制終端。
O_NONBLOCK如果路徑名指向FIFO/塊文件/字符文件,則把文件的打開和後繼I/O設置為非阻塞模式
以下三個常量同樣是選用的,它們用於同步輸入輸出
O_DSYNC等待物理I/O結束後再write。在不影響讀取新寫入的數據的前提下,不等待文件屬性更新。
O_RSYNCread等待所有寫入同一區域的寫操作完成後再進行
O_SYNC等待物理I/O結束後再write,包括更新文件屬性的I/O
註意:
O_CREAT 選擇後一定要填寫mode參數
Mode 參數S_I + R+W+X + U
U表示設置所有者 單獨設置一項填USR
G表示設置組用戶 單獨設置一項填GRP
O表示設置其他人 單獨設置一項填OTH
Mode 還可以設置為八進制數值如 0644
2.creat()
頭文件
#include <sys/types.h>
#include
<sys/stat.h>
#include <fcntl.h>
原型
Int creat(const char* name,mode_t mode);
相當於
Open(name ,O_RDONLY|O_CREAT|O_TRUNC, mode)
3.Read()
頭文件
#include<unistd.h>
函數原型
Ssize_t read(int fd ,void buf,size_t len);
Read 是否會阻塞由所讀的設備決定,在open等函數中設定
Size_t 最大值為SIZE_MAX
Ssize_t最大值為 SSIZE_MAX
4.Write()
頭文件
#include<unistd.h>
原型
Ssize_t write(int fd, const void * buf,size_t count);
Read和write實際都是和緩沖區交互數據,不是直接和磁盤交換數據
詳解https://www.cnblogs.com/JohnABC/p/5821660.html
5.同步IO
Fsync() 和fdatasync()
頭文件
#include<unistd.h>
Int fsync(int fd);
直到fd的數據寫入磁盤緩沖之後才返回 ,同時還會建立時間戳和inode信息等元數據
Int fdatasync(int fd);
直到fd的數據寫入磁盤緩沖之後才返回
可能導致的問題是文件數據更新,但是目錄沒有更新,那麽數據會成功寫入,但是目錄項沒有更新,文件無法訪問。
解決方法:目錄也要調用fsync();
Sync()
Void sync(void);
同步所以緩存。
同步標誌 O_SYNC
在open()的時候,加上這個標誌,所有在文件上的IO都會同步
O_DSYNC 只同步普通數據
O_RSYNC
直接IO
使用0_DIRECT open(),系統將最小化IO管理,直接將用戶緩沖區和設備進行初始化,所有的IO都是同步的,操作在完成之前不會返回。
4.關閉文件close()
頭文件
#include<unistd.h>
Int close(int fd);
解除fd和文件的關聯文件描述符不在有效
同一個程序綁定的fd如果close了,然後在打開,之前的fd和後面的指相同,是可以用來執行文件操作的
5.Lseek()
頭文件
#include<sys/types.h>
#include<unistd.h>
函數原型
Off_t lseek(int fd,off_t pos,int origin);
Origin 可以設置為
SEEK_CUR 從文件當前位置移動 pos位
SEEK_END 從文件尾開始移動pos位
SEK_SET 從文件開始移動pos位
返回新的文件位置
Ret = -1 表示出錯
定位讀寫
Pread()
頭文件
#include<unistd.h>
也就是說:
#define _XOPEN_SOURCE
是為了可以使用 5. The X/Open
Portability Guide 的功能。
函數原型
Ssize_t pread(int fd,void *buf,size_t count,off_t pos);
Pwrite
函數原型
Ssize_t pwrite(int fd,const void * buf,size_t count,off_t pos);
在pos處進行讀寫
不會改變fd的位置,和read write 混合用會導致覆蓋
文件截斷
頭文件
#include<unistd>
#include<sys/types.h>
函數原型:
Int ftruncate(int fd,off_t len);
Int truncate(char * path,off_t len);
IO 多路復用
6.Select
頭文件
#include<sys/time.h>
#include<sys/types.h>
#include<unistd.h>
函數原型:
Int select(int n,fd_set *readfds,fd_set *writefds,fd_set *exceptfds,struct timeval *timeout);
n 所有集合中文件描述符最大值加一
timeout 結構體
#include<sys/time.h>
Struct{
Long tv_sec;//seconds
Long tv_usec; //microseconds
};
Select返回時這個結構體狀態未定義,最好重新初始化,較新版本會把他初始化為剩余時間
如果時間為0,select立即返回,報告文件描述符不可用,不在等待後續操作。
管理文件描述符宏定義
FD_CLR(int fd,fd_set *set);
FD_ISSET(int fd,fd_set *set); //測試一個文件描述符是否在集合內
FD_SET(int fd,fd_set *set); //添加一個文件描述符到指定集合
FD_ZERO(fd_set *set); //移除指定集合中所有描述符 ,每次調用select()前調用
文件描述符集合是靜態建立的,最大值有限制,為 FD_SETSIZE; linux 為1024
通過將select 三個集合設置為0 ,將超時設置為非空可以實現sleep;
7.Pselect
posix版本的select
頭文件
#include<sys/select.h>
#define _XOPEN_SOURCE 600
原型
Int pselect(int n,fd_set *readfds,fd_set *writefds,fd_set *exceptfds,
Const struct timespec *timeout,
Const sigset_t * sigmask);
和select比區別
超時參數類型改變,pselect 使用timespec精度為秒和納秒,比秒和毫秒精度高,事實是都在秒之後不可靠
Pselect 不修改timeout參數
Select沒有sigmask參數,pselect添加是為了解決信號和文件描述符之間的競爭條件
一般還是選擇使用select
註意:
Select在有文件描述符狀態變化時返回,返回時修改了文件描述符集合,返回後的集合中只有狀態改變的fd,每次使用時要重新賦值。
8.Poll
System v 的I/O多路復用
頭文件:
#include<sys/poll.h>
函數原型:
Int poll(struct pollfd *fds,unsigned int nfds, int timout);
Pollfd 結構
頭文件
#include<sys/poll.h>
Struct pollfd
{ int fd; //文件描述符
Short event;// 監視的文件描述符上的事件位掩碼
Short revent;// 發生在文件描述上的事件位掩碼
}
常量 |
說明 |
POLLIN |
普通或優先級帶數據可讀 |
POLLRDNORM |
普通數據可讀 |
POLLRDBAND |
優先級帶數據可讀 |
POLLPRI |
高優先級數據可讀 |
POLLOUT |
普通數據可寫 |
POLLWRNORM |
普通數據可寫 |
POLLWRBAND |
優先級帶數據可寫 |
POLLERR |
發生錯誤 |
POLLHUP |
發生掛起 |
POLLNVAL |
描述字不是一個打開的文件 |
註意:
Poll在返回後只修改了fds的revents字段,所以可以反復使用。
與select區別。
9.Ppoll
和pselect 類似
標準I/O
打開文件
10.fopen()
頭文件
#include<stdio.h>
函數原型:
FILE* fopen(const char* path,const char *mode);
文件打開後關聯到一個新的流
打開方式有: r r+ w w+ a a+
成功返回指針,失敗返回NULL。
11.fdopen
頭文件
#include<stdio.h>
函數原型:
FILE* fdopen(int fd,const char *mode);
講一個打開的文件的描述符轉化為流,轉化時mode要一致
12.fclose
頭文件
#include<stdio.h>
函數原型:
Int fclose(FILE * stream);
成功返回0 並將緩沖中沒寫入數據寫入,失敗返回EOF
13. fcloseall
#include <stdio.h>
#define _GUN_SOURCE
Int fcloseall();
14.fgetc()
#include<stdio.h>
Int fgetc(FILE*stream);
函數結果必須以int保存 ,EOF會被返回,輸出時要檢查 c== EOF
15.ungetc()
#include<stdio.h>
Int ungetc(int c,FILE*stream);
成功返回0,失敗返回EOF;
16.fgets
#include<stdio.h>
Char * fgets(char *str,int size,FILE* stream);
讀取size-1 個字節到str,成功返回str失敗返回NULL。
讀到EOF或者換行符的時候結束,如果讀到換行符就寫入str。
最大輸入長度為LINE_MAX 在limits.h中定義
17.fread
18.fputc()
#include<stdio.h>
Int fputc(int c,FILE *stream);
19fputs()
#include<stdio.h>
Int fputs(const char *str,FILE *stream);
20. fwrite
21.fseek()
#include<stdio.h>
Int fseek(FILE *stream,long offset,int whence);
Whence有如下值:
SEEK_CUR 文件位置為 當前加offset
SEEK_END文件位置為文件尾加offset
SEEK_SET文件位置為0+offset
錯誤返回-1;正確返回0
22.fsetpos()
#include<stdio.h>
Int fsetpos(FILE * stream,fpos_t *pos);
23.rewind
#include<stdio.h>
Void rewind(FILE* stream);將流置於初始位置
24 ftell()
#include<stdio.h>
long ftell(FILE* stream);
返回文件的當前流位置。錯誤返回-1.
25fgetpos
#include<stdio.h>
int fgetpos(FILE* stream,fpos_t *pos);
26.fflush
#include<stdio.h>
Int fflush(FILE* stream); 將流中數據寫回文件
成功返回0;
Int ferror 檢查是否有錯
Int feof 檢查是否文件尾
Void clearer 清空失敗和文件尾標誌,不可恢復
27.fileno
#include<stdio.h>
Int fileno (FILE *stream);
返回文件描述符失敗返回-1;
28.Setvbuf
#include<stdio.h>
Int setvbuf(FILE * stream, char * buf, int mode, size_t size);
_IONBF
_IOLBF
_IOFBF
函數在除了無緩存模式下,buf可以指向一個size大小的緩沖區,如果buf為空則glic分配
Stdio.h中的BUFSIZE定義默認的緩沖區大小
29.readv 和writev
#include<sys/uio.h>
Ssize_t readv(int fd ,const struct iovec *iov,int count);
Ssize_t writev(int fd ,const struct iovec *iov,int count);
從iov描述的緩沖區讀或者寫count個segment的數據到fd。
Struct iovec 描述的緩沖區成為segment
#include<sys/uio.h>
Struct iovec
{
Void *iov_base;
Size_t iov_len;
}
30.epoll
#include<sys/epoll.h>
Int epoll_create(int size);
返回與這個實例關聯的文件描述符 size為要監測的文件描述符數目。出錯返回-1
EINVAL size不是正數
ENFILE 系統打開文件數達到上限
ENOMEN 沒有足夠內存
返回的文件描述符需要用close關閉。
#include<sys/epoll.h>
Int epoll_ctl(int epfd,int op,int fd,struct epoll_event *event);
#include<sys/epoll.h>
Struct epoll_event{
_u32 events;
Union{
Void *ptr;
Int fd;
_u32 u32;
_64 u64;
} data;};
Epoll_ctl 調用會關聯epoll實例和epfd, op指定要進行的操作,event描述更具體的行為
Op的取值
EPOOL_CTL_ADD 把fd加到監測列表
EPOOL_CTL_DEL 刪除fd到監測列表
EPOOL_CTL_MOD 使用event修改監測列表中的fd
Event取值:
EPOLLIN :表示對應的文件描述符可以讀(包括對端SOCKET正常關閉);
EPOLLOUT:表示對應的文件描述符可以寫;
EPOLLPRI:表示對應的文件描述符有緊急的數據可讀(這裏應該表示有帶外數據到來);
EPOLLERR:表示對應的文件描述符發生錯誤;
EPOLLHUP:表示對應的文件描述符被掛斷;
EPOLLET: 將EPOLL設為邊緣觸發(Edge Triggered)模式,這是相對於水平觸發(Level Triggered)來說的。
EPOLLONESHOT:只監聽一次事件,當監聽完這次事件之後,如果還需要繼續監聽這個socket的話,需要使用epoll_ctl_mod修改fd
#include<sys/epoll.h>
Int epoll_wait ( int epfd,struct epoll_event * events,int maxevents,int timeout );
Timeout 毫秒
返回成功 events指向epoll_event結構體,其中最多有maxevents 返回值為事件個數,出錯返回-1
邊緣觸發和水平觸發
Epoll_ctl中events設置為 RPOLLE時,設置邊緣觸發。
例子:
- 生產者向管道寫入數據1KB
- 消費者管道調用epoll_wait等待數據。
在邊緣觸發中,直到1數據寫完epoll_wait才返回,水平觸發則epoll_wait立即返回
水平觸發為默認形式,select和poll都是水平模式。水平觸發只關心狀態,邊緣觸發關系事件。
內存映射
31.Mmap
#include<sys/mman.h>
void* mmap(void* addr,size_t len,int prot,int flags,int fd,off_t offset);
addr 參數告訴內核映射文件的最佳地址,只是提示不是強制。一般設置為 0;調用返回內存映射的開始地址。
Prot 設置訪問權限 PROTNONE 無法訪問,一般這樣設置沒有意義。
PROT_READ 可讀
PROT_WRITE 可寫
PROT_EXEC 可執行
訪問的權限不能與打開文件的訪問模式沖突。
Flag參數描述了映射的類型和一些行為
MAP_FIXED //使用指定的映射起始地址,如果由start和len參數指定的內存區重疊於現存的映射空間,重疊部分將會被丟棄。如果指定的起始地址不可用,操作將會失敗。並且起始地址必須落在頁的邊界上。
MAP_SHARED //與其它所有映射這個對象的進程共享映射空間。對共享區的寫入,相當於輸出到文件。直到msync()或者munmap()被調用,文件實際上不會被更新。
MAP_PRIVATE //建立一個寫入時拷貝的私有映射。內存區域的寫入不會影響到原文件。這個標誌和以上標誌是互斥的,只能使用其中一個。
MAP_DENYWRITE //這個標誌被忽略。
MAP_EXECUTABLE //同上
MAP_NORESERVE //不要為這個映射保留交換空間。當交換空間被保留,對映射區修改的可能會得到保證。當交換空間不被保留,同時內存不足,對映射區的修改會引起段違例信號。
MAP_LOCKED //鎖定映射區的頁面,從而防止頁面被交換出內存。
MAP_GROWSDOWN //用於堆棧,告訴內核VM系統,映射區可以向下擴展。
MAP_ANONYMOUS //匿名映射,映射區不與任何文件關聯。
MAP_ANON //MAP_ANONYMOUS的別稱,不再被使用。
MAP_FILE //兼容標誌,被忽略。
MAP_32BIT //將映射區放在進程地址空間的低2GB,MAP_FIXED指定時會被忽略。當前這個標誌只在x86-64平臺上得到支持。
MAP_POPULATE //為文件映射通過預讀的方式準備好頁表。隨後對映射區的訪問不會被頁違例阻塞。
MAP_NONBLOCK //僅和MAP_POPULATE一起使用時才有意義。不執行預讀,只為已存在於內存中的頁面建立頁表入口。
fd:有效的文件描述詞。一般是由open()函數返回,其值也可以設置為-1,此時需要指定flags參數中的MAP_ANON,表明進行的是匿名映射。
off_toffset:被映射對象內容的起點。
成功返回映射區域的地址
SIGBUS //進程訪問無效的映射去死產生
SIGSEGV //進程試圖在只讀映射區寫入數據時產生。
32.Sysconf
#include<unistd.h>
Long sysconf(int name);
返回name屬性的值失敗返回-1.
例子: long pagesize = sysconf(SC_PAGR_SIZE);獲取頁面大小
#include<unistd.h>
Int getpagesize(void);
#include<asm/pages.h>
Int page_size = PAGE_SIZE;
33.munmap
#include<sys/mman.h>
int munmap(void* addr,size_t len);
移除從addr開始的len字節長度的映射。移除後訪問會產生SIGSEGV信號
成功返回0,失敗返回-1;
34.mremap()
#include<sys/mman.h>
#include<unistd.h>
#define _GNU_SOURCE
Void * mremap(void *addr,size_t old_size,size_t new_size,unsigned long flags);
修改文件大小size 從old到new
35.mprotect
#include<sys/mman.h>
Int mprotect(const void *addr,size_t len,int prot);
修改映射區域權限
在有些系統中mprotect只能修改mmap分配的區域,在linux可以修改所有的
36.msync
#include<sys/mman.h>
Int msync(void *addr,size_t len,int flags);
Flag 取值
MS_ASYNC 同步操作一部發生,系統調度更新,mysnc立即返回。
MS_INVALIDATE 所有該塊映射的拷貝失效,未來對該映射的操作直接寫到硬盤
MS_SYNC 所有同步同步進行,在write執行完畢後msync才返回。
#include<sys/mman.h>
Int Madvise(void *addr,size_t len,int len);
#include<fcntl.h>
Int posix_fadvise(int fd ,odd_t offset,off_t len,int advise);
#include<fcntl.h>
Ssize_t readahead(int fd ,off64_t offset,size_t count);
同步Synchronized ,同步Synchronous 異步asynchronous
同步在讀寫操作執行完之後才返回。
異步不用讀寫操作執行完就可以返回
Synchronized保證事件發生,屬於更深層次,NoSynchronized寫操作加入隊列就返回,只確保到內核緩存區域。
linux 系統編程 文件IO