1. 程式人生 > >linux 系統編程 文件IO

linux 系統編程 文件IO

ble erro 每次 system fwrite trigger 無效 width tps

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時,設置邊緣觸發。

例子:

  1. 生產者向管道寫入數據1KB
  2. 消費者管道調用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