Linux 系統調用 API 之文件 I

Linux 系統調用 API 之文件 I/O

​開始之前

正是因為 Linux 系統一切皆文件,我們便將文件的輸入/輸出作為學習 Linux 系統調用 API 的第一部分。你將從理解文件描述符的概念開始,一直學到打開文件、關閉文件、從文件中讀出、向文件寫入數據等接口。

系統幫助手冊(manual)上的第 2 章節,便為系統調用的相關手冊頁,裡面有所需的頭文件列表及其函數原型。

你可以執行以下命令獲取相關函數的使用方法。裡面有所需的頭文件列表及其函數原型。

man 2 open 

如果你還對內核、系統調用、庫函數等概念不清楚,那歡迎你在我的前文中尋找答案:

文件描述符(file descriptor)

fd 即為文件描述符約定俗成的標示,如果你剛剛查了 open() 函數的幫助手冊,便會得到以下的說明:

The return value of open() is a file descriptor, a small, nonnegative integer that is used in subs quent system calls (read(2), write(2), lseek(2), fcntl(2), etc.) to refer to the open file.

open() 的返回值便是一個文件描述符,一個小的非負整數,用以指代打開為文件,同時也將用於 read(2), write(2), lseek(2), fcntl(2) 等系統調用。

open

open() 函數通過指定的參數 pathname 來打開文件,如果指定的文件不存在(同時 flags 參數中有指明 O_CREAT),那麼 open 將創建之。

函數原型如下:

int open(const char *pathname, int flags, mode_t mode);

參數 flags 包含以下的訪問權限,O_RDONLY, O_WRONLY, O_RDWR or O_CREAT 他們分別要求著文件以只讀、只寫、讀寫的方式打開或創建文件。

// 代碼示例
#include<stdio.h>
#include
#include
#include<fcntl.h>
#include<stdlib.h>
#include<unistd.h>
nt main(int argc, char* argv[])
// 向 main 函數傳遞參數,argc為接收參數的總數,argv[0] 為此源碼文件名,argv[1] 為接收的第一個參數,以此類推
{
if(argc < 2)
{
printf("Please input filename!\\n");
exit(1);
}

else
{
int fd;
umask(0000);
// 新建文件的權限不僅僅依賴於參數 mode,也受制於進程的 umask 值。執行命令 man umask 以深入瞭解

fd = open(argv[1], O_RDWR | O_CREAT, 0666);
if(fd < 0)
{ // open 打開失敗返回 -1
printf("error\\n");
exit(1);
}
else
{
printf("success=%d\\n", fd);
close(fd);
printf("closed\\n");
}
return 0;
}
}
/<unistd.h>/<stdlib.h>/<fcntl.h>
/<stdio.h>

read

read() 函數試圖從文件描述符 fd 中讀取指定字節數數據至內存緩衝中。

函數原型如下:

ssize_t read(int fd, void *buf, size_t count);

參數 buf 為用來存放數據的內存緩衝地址,參數 count 為指定的讀取字節數,同時 read 函數返回實際讀取到的字節數,-1 表示錯誤。

系統調用不會分配內存緩衝區以返回調用者。所以,必須預先分配大小合適的緩衝區並將其指針傳遞給系統調用。與此相反,有些庫函數卻會分配內存緩衝區用以返回信息給調用者。

// 代碼示例
#define MAX_READ 20
char buf [MAX_READ];
f ( read(argv[1], buf, MAX_READ) + 1 )
exit(1);

printf("The read data is : %s\\n", buf);

write

與 open() 的調用相似,write() 函數最多從內存緩衝 buf 中讀取參數 count 所定義的字節單位數據量,並寫入至 fd 所指向的文件中。若調用成功則返回實際寫入文件的字節數,錯誤返回 -1。

函數原型:

ssize_t write(int fd, const void *buf, size_t count);

在理解時需要注意,read() 與 write() 成功讀入或寫入數據後,並不會將操作的內容輸出至屏幕。

你要關注的是 buffer,通過字符串輸出以查看緩衝區內容是否正確,訪問文件內容以查看是否寫入成功。

close

close() 關閉一個文件描述符,以不再指向任何文件。函數原型如下:

int close(int fd);

成功返回 0,失敗返回 -1,因此我們同樣可以對其進行錯誤捕獲。

lseek

內核會記錄每個打開文件的偏移量,文件偏移量是指執行下一個 read() 或 write() 操作時文件的起始位置。

文件打開時,會將文件偏移量設置為指向文件的開始,而每次的 read() 或 write() 操作便會自動對其進行調整,以指向已讀或已寫數據後的下一個字節。

以保證一次打開多次讀寫的完整性、不重複。對一次 write() 調用的編譯後的兩次執行,即為對目標文件開頭處的重複寫入。而對兩次 write() 調用的編譯後的一次執行,將有兩次寫入痕跡。

// 函數原型
off_t lseek(int fd, off_t offset, int whence);

lseek() 通過參數 offset 與 whence,對 fd 所指向的已打開的文件進行偏移量的調整。

參數 offset 為以字節為單位的偏移量,參數 whence 則為從何處進行偏移,whence 的參照點有:

  • SEEK_SET:從文件頭部開始偏移 offset 個字節
  • SEEK_CUR:相對於當前文件偏移量,調整 offset 個字節
  • SEEK_END:從文件尾部開始偏移 offset 個字節,正數向後,負數向前
// 體驗代碼
#include<stdio.h>
#include<string.h>
#include
#include
#include<fcntl.h>
#include<stdlib.h>
#include<unistd.h>
it main(int argc, char* argv[])
{
if(argc < 2)
{
printf("Please input filename!\\n");
exit(1);
}
else
{
int fd;
umask(0000);
fd = open(argv[1], O_RDWR|O_CREAT, 0644);
if(fd < 0)
{
printf("error\\n");
exit(1);
}
else
{
printf("open success=%d\\n", fd);
// write

char buf[1024] = "hello world\\n";

if( write(fd, buf, strlen(buf)) + 1)
{
printf("buf=%s\\n", buf); // 若寫入成功則將內存緩衝區內容輸出至屏幕
// lseek
lseek(fd, 6, SEEK_SET); // 從文件頭開始,向後偏移 6 個字節
char buf2[1024];

if(read(fd, buf2, 1024) + 1) // 調整了偏移量後,重新對 fd 進行讀取,此時輸出因為 world
printf("buf2=%s\\n", buf2);
}
close(fd);
printf("closed\\n");
}
return 0;
}
}
/<unistd.h>/<stdlib.h>/<fcntl.h>
/<string.h>/<stdio.h>

覺得我寫的不錯的話,不妨添加關注吧!


分享到:


相關文章: