系統(tǒng)調(diào)用與程序運行空間
在Linux操作系統(tǒng)中,為了提高系統(tǒng)的穩(wěn)定性庐橙,保證內(nèi)核的安全琳水,程序運行時的內(nèi)存空間被分為了用戶空間和內(nèi)核空間这吻。普通應用程序工作在用戶空間吊档,不能直接訪問內(nèi)核空間。它們需要使用Linux系統(tǒng)提供給用戶的一些"特殊接口" - 系統(tǒng)調(diào)用來安全地訪問內(nèi)核空間唾糯。
要對文件進行讀寫就需要使用Linux系統(tǒng)提供的一些系統(tǒng)調(diào)用怠硼。在這篇文章中我主要介紹 open()
, write()
, read()
, lseek()
和 close()
等函數(shù)鬼贱,在下文中我會詳細講解這些函數(shù)的使用。
文件描述符
在Linux系統(tǒng)中香璃,一切都可以被看作是文件这难,這包括:普通文件、目錄文件葡秒、鏈接文件和設備文件姻乓。要訪問文件,必須使用文件描述符眯牧。文件描述符是一個非負的整數(shù)蹋岩,它是系統(tǒng)中被打開文件的索引。當打開或者創(chuàng)建一個文件時学少,內(nèi)核會返回一個文件描述符剪个;當需要讀寫文件時,也需要將相應的文件描述符作為參數(shù)傳給讀寫函數(shù)版确。程序啟動時扣囊,默認有3個文件描述符:
文件描述符 | 宏 | 說明 |
---|---|---|
0 | STDIN_FILENO | 標準輸入 |
1 | STDOUT_FILENO | 標準輸出 |
2 | STDERR_FILENO | 標準錯誤輸出 |
如果此時創(chuàng)建或打開一個文件,這個文件的文件描述符就是3.
文件IO基本操作
打開/創(chuàng)建文件
open()
函數(shù)用于打開或者創(chuàng)建文件绒疗。其在打開或者創(chuàng)建文件時可以指定文件的屬性及用戶的權限等各種參數(shù)侵歇。要使用 open()
函數(shù),需要包含 #include <sys/stat.h>
和 #include <fcntl.h>
這兩個頭文件忌堂。下面是函數(shù)的說明:
int open(const char *path, int oflag, [mode_t mode]);
args:
const char *path: 文件路徑盒至,可以是絕對,也可以是相對路徑
int oflag : 文件打開的方式
- O_RDONLY 只讀打開
- O_WRONLY 只寫打開
- O_RDWR 可讀可寫打開
以上3種必選一個士修,以下4種可以任意選擇
- O_APPEND 追加打開枷遂,所寫數(shù)據(jù)附加到文件末
- O_CREAT 若此文件不存在則創(chuàng)建它
- O_EXCL 若文件存在則報錯返回
- O_TRUNC 如果文件已存在,并且以只寫或可讀可寫方式打開棋嘲,則將其長度截斷為0字節(jié)
[mode_t mode] : 文件權限酒唉,只有在創(chuàng)建文件時需要使用
return:
文件描述符,非負整數(shù)是成功沸移,-1是失敗
在 open()
函數(shù)中痪伦,文件的打開方式不止上面的幾種,這里只列舉了常用的7種雹锣。注意网沾,新建文件的權限不是直接等于 mode
的值,而是等于 mode & ~uname
蕊爵。
寫文件
當文件打開后辉哥,我們就可以向該文件寫數(shù)據(jù)了。在Linux系統(tǒng)中,用 write()
向打開的文件寫入數(shù)據(jù)醋旦,要使用這個函數(shù)恒水,需要包含 #include <unistd.h>
。下面是函數(shù)的說明:
ssize_t write(int fildes, const void *buf, size_t nbyte);
args:
int fildes : 寫入文件的文件描述符
const void *buf: 寫入數(shù)據(jù)在內(nèi)存空間存儲的地址
size_t nbyte : 期待寫入數(shù)據(jù)的最大字節(jié)數(shù)
return:
文件實際寫入的字節(jié)數(shù)饲齐,非負整數(shù)是成功钉凌,-1是失敗(磁盤已滿或者超出該文件的長度等)
注意函數(shù)的返回類型是 ssize_t
。 ssize_t
同 size_t
類似捂人,只是 ssize_t
表示有符號數(shù)御雕。想了解更多 size_t
和 ssize_t
的區(qū)別請看這篇文章。
讀文件
同寫文件類似先慷,要使用讀文件函數(shù) read()
,需要包含 #include <unistd.h>
饮笛。下面是函數(shù)的說明:
ssize_t read(int fildes, void *buf, size_t nbyte);
args:
int fildes : 讀取文件的文件描述符
void *buf : 讀取數(shù)據(jù)在內(nèi)存空間存儲的地址
size_t nbyte: 期待讀取數(shù)據(jù)的最大字節(jié)數(shù)
return:
文件實際讀取的字節(jié)數(shù),非負整數(shù)是成功论熙,-1是失敗
同 write()
一樣福青, read()
函數(shù)的返回類型也是 ssize_t
。
文件的偏移量
在每個打開的文件中都有一個文件的偏移量脓诡,文件的偏移量會根據(jù)文件的讀寫而改變位置无午。我們可以通過 lseek()
函數(shù)來調(diào)整文件的偏移量。默認情況下祝谚,新打開文件的文件偏移量在文件的開始宪迟。同 write()
和 read()
函數(shù)類似,要使用這個函數(shù)交惯,需要包含 #include <unistd.h>
次泽。下面是函數(shù)的說明:
off_t lseek(int fildes, off_t offset, int whence);
args:
int fildes : 修改文件的文件描述符
off_t offset: 文件偏移量移動的距離
int whence : 文件偏移量的基址
- SEEK_SET 文件開始處
- SEEK_CUR 文件當前位置
- SEEK_END 文件結束處
return:
當前文件指針的位置,非負整數(shù)是成功席爽,-1是失敗
off_t
同 ssize_t
類似意荤,都是有符號數(shù)。
關閉文件
當文件不再被使用時只锻,可以調(diào)用 close()
函數(shù)來關閉被打開的文件玖像。
除了用 close()
顯示地關閉文件外,通過結束進程也能隱式地關閉被該進程打開的所有文件齐饮。要使用該函數(shù)捐寥,需要包含 #include <unistd.h>
。下面是函數(shù)的說明:
int close(int fildes);
args:
int fildes: 要關閉文件的文件描述符
return:
文件關閉狀態(tài)祖驱,0是成功握恳,-1是失敗
文件IO實例
文件基本操作
這是一個簡單的文件基本操作實例。在這個例子中捺僻,程序分兩次將內(nèi)存中的字符串寫入文件乡洼,然后又將文件內(nèi)容讀回內(nèi)存空間。
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
/**
* This is a simple example for using open(), write(), read(), lseek() and close().
*/
int main(int argc, char *argv[])
{
int fd;
ssize_t wr_size, rd_size;
char buffer[128];
char string_1[30], string_2[30] = "This is the second line!\n";
char *path = "./file_io.log";
fd = open(path, O_RDWR | O_CREAT | O_TRUNC, 511);
if (fd < 0) {
printf("File create fail...\n");
return -1;
} else {
printf("File create success...\n");
}
/* write the first line to file_io.log */
strcpy(string_1, "This is a demo for file_io!\n");
wr_size = write(fd, string_1, strlen(string_1));
if (wr_size < 0) {
printf("File write 1 fail...\n");
printf("wr_size = %d\n", wr_size);
return -1;
} else {
printf("File write 1 success...\n");
printf("wr_size = %d\n", wr_size);
}
/* write the second line to file_io.log
* in this case, we only write 10 bytes data from string_2 to file.
*/
wr_size = write(fd, string_2, 10);
/* add "\0"(not '\0'!!) to the end of the second line */
wr_size = write(fd, "\0", 1);
if (wr_size < 0) {
printf("File write 2 fail...\n");
printf("wr_size = %d\n", wr_size);
return -1;
} else {
printf("File write 2 success...\n");
printf("wr_size = %d\n", wr_size);
}
/* decrease current file offset by 20 bytes */
lseek(fd, -20, SEEK_CUR);
rd_size = read(fd, buffer, 100);
if (rd_size < 0) {
printf("File read_1 fail...\n");
printf("rd_size = %d\n", rd_size);
return -1;
} else {
printf("File read_1 success...\n");
printf("rd_size = %d,\nbuffer = %s\n", rd_size, buffer);
}
close(fd);
return 0;
}
編譯并運行該程序,程序和文件輸出結果如下:
對于上面的例子就珠,有幾點需要注意:
- 在40行處,
wr_size = write(fd, string_2, 10);
我們寫入的字節(jié)數(shù)是小于string_2
中的字節(jié)數(shù)的醒颖。如果想要寫入的字節(jié)數(shù)大于string_2
中的字節(jié)數(shù)妻怎,那string_2
外的字節(jié)也會寫入文件(這些額外的字節(jié)不是我們希望要的)。比如我們將40行改為wr_size = write(fd, string_2, 100);
其輸出結果如下:
2)如果注釋掉53行泞歉,則讀出的字節(jié)數(shù)為0逼侦,因為此時文件的偏移量處于文件的尾部。
簡易版CP指令
這是一個模仿Linux cp指令的小程序腰耙,這里并沒有考慮效率榛丢,也沒有考慮特殊情況,只是簡單地實現(xiàn)其功能挺庞。
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
/*
* This a simple version of cp command.
*/
int main(int argc, char *argv[])
{
int fd1, fd2;
ssize_t rd_size;
char buffer[128];
if (argc != 3) {
printf("You should enter enter 2 parameters\n");
return -1;
}
fd1 = open(argv[1], O_RDONLY);
if (fd1 < 0) {
printf("File %d does not exist...\n", fd1);
return -1;
}
fd2 = open(argv[2], O_WRONLY | O_CREAT | O_TRUNC, 511);
if (fd2 < 0) {
printf("File %d open fail...\n", fd2);
return -1;
}
while(read(fd1, buffer, 1))
write(fd2, buffer, 1);
close(fd1);
close(fd2);
return 0;
}
編譯并運行該程序晰赞,程序輸出結果如下:
總結
這篇文章主要介紹了如何使用文件IO的系統(tǒng)調(diào)用函數(shù)對文件進行操作,文中出現(xiàn)的代碼都可在我的[github][7]上找到选侨。
如果覺得本文對你有幫助掖鱼,請多多點贊支持,謝謝援制!