在Linux下的檔案IO操作
系統呼叫
系統呼叫: 作業系統提供給使用者程式呼叫的一組“特殊”介面,使用者程式可以通過這組“特殊”介面來獲得作業系統核心提供的服務
為什麼使用者程式不能直接訪問系統核心提供的服務為了更好地保護核心空間,將程式的執行空間分為 核心空間 和 使用者空間(也就是常稱的核心態和使用者態),它們分別執行在不同的級別上 在邏輯上是相互隔離的 。 因此 使用者程序在通常情況下不允許訪問核心資料 ,也無法使用核心函式,它們只能在使用者空間操作使用者資料 ,呼叫使用者空間的函式 。
進行系統呼叫時 ,程式執行空間從使用者空間進入核心空間 ,處理完後再返回到使用者空間系統呼叫並不是直接與程式設計師進行互動的,它僅僅是一個通過軟中斷機制
Linux 中的系統呼叫包含在 Linux 的 libc 庫中,通過標準的 C 函式呼叫方法可以呼叫系統命令相對 API 更高了一層,它實際上是一個可執行程式,它的內部呼叫了使用者程式設計介面 (API )來實現相應的功能 。核心如何區分和引用特定的檔案
通過檔案描述符 。檔案描述符是一個非負的整數 ,是一個索引值 ,指向在核心中每個程序開啟檔案的記錄表 。 當開啟一個現存檔案或建立一個新檔案時,核心就向程序返回一個檔案描
述符;當需要讀寫檔案時 也需要把檔案描述符作為引數傳遞給相應的函式 。是一個非負的整數(通常是小整數),posix標準要求每次開啟一個檔案,必須使用當前程序最小的檔案描述符號碼,因此開啟一定是3.
一個程序啟動時 通常會開啟 3 個檔案:
標準輸入 | 標準輸入 | 標準出錯 |
---|---|---|
STDIN_FILENO | STDOUT_FILENO | STDERR_FILENO |
stdin | stdout | stderr |
#include <stdio.h> #include <string.h> #include <unistd.h> #define MSG_STR "hello world\n" int main(int argc, char **argv) { printf("%s",MSG_STR); fputs(MSG_STR,stdout); write(STDOUT_FILENO,MSG_STR,strlen(MSG_STR));//標準輸出到螢幕MSG_STR return 0; }
關於fputs和weitey
fputs(const char *s, FILE *stream);
write(int fd, const void *buf, size_t count);
檔案的建立/開啟
open() 函式用於開啟或者建立檔案。其在開啟或者建立檔案時可以指定檔案的屬性及使用者的許可權等各種引數。
int open(const char *pathname, int flags);//開啟已存在的檔案
int open(const char *pathname, int flags, mode_t mode);//開啟不存在的檔案
//flags:read write操作檔案的許可權
//mode:該檔案在磁碟中 相對於 使用者的許可權
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 追加開啟,所寫資料附加到檔案末
- O_CREAT 若此檔案不存在則建立它
- O_EXCL 若檔案存在則報錯返回
- O_TRUNC 如果檔案已存在,並且以只寫或可讀可寫方式開啟
則將其長度截斷為0位元組
[mode_t mode] : 檔案許可權,只有在建立檔案時需要使用
return:
檔案描述符,非負整數是成功,-1是失敗
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int main(int argc ,char **argv)
{
int fd = -1;
if((fd = open("test.txt",O_CREAT|O_RDWR,0666))<0)
if(-1==fd)
{
printf("檔案建立失敗\n");
}
else
{
printf("檔案開啟成功,fd = %d\n",fd);
}
return 0;
}
寫檔案
當檔案開啟後,我們就可以向該檔案寫資料了。在Linux系統中,用 write() 向開啟的檔案寫入資料,要使用這個函式,需要包含 #include <unistd.h> 。下面是函式的說明:
ssize_t write(int fildes, const void *buf, size_t nbyte);
args:
int fildes : 寫入檔案的檔案描述符
const void *buf: 寫入資料在記憶體空間儲存的地址
size_t nbyte : 期待寫入資料的最大位元組數
return:
檔案實際寫入的位元組數,非負整數是成功,-1是失敗(磁碟已滿或者超出該檔案的長度等)
if((rv = write(fd, MSG_STR,strlen(MSG_STR)))<0)
if(rv == -1)
{
printf("寫入資料失敗\n");
}
else
{
printf("寫入資料成功\n");
}
讀檔案
同寫檔案類似,要使用讀檔案函式 read() ,需要包含 #include <unistd.h> 。下面是函式的說明:
ssize_t read(int fildes, void *buf, size_t nbyte);
args:
int fildes : 讀取檔案的檔案描述符
void *buf : 讀取資料在記憶體空間儲存的地址
size_t nbyte: 期待讀取資料的最大位元組數
return:
檔案實際讀取的位元組數,非負整數是成功,-1是失敗
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#define BUFSIZE 1024
#define MSG_STR "i love linux\n"
int main(int argc ,char **argv)
{
int fd = -1;
int rv = -1;
char buf[BUFSIZE];
if((fd = open("test.txt",O_CREAT|O_RDWR,0666))<0)
if(fd==-1)
{
printf("檔案建立失敗\n");
}
else
{
printf("檔案開啟成功,fd = %d\n",fd);
}
if((rv = write(fd, MSG_STR,strlen(MSG_STR)))<0)
if(rv == -1)
{
printf("寫入資料失敗\n");
}
else
{
printf("寫入資料成功\n");
}
memset(buf,0,sizeof(buf));//把buf的值為0,
if((rv = read(fd,buf,sizeof(buf)))<0)
if(rv == -1)
{
printf("讀寫失敗\n");
}
else
{
printf("讀寫成功\n");
goto cleanup;
}
printf("讀寫的資料 %d\n %s\n",rv,buf);
return 0;
}
但是這樣讀出是空的,所以要使用lseek檔案偏離量,通俗來說:就是改變游標的位置,從哪裡讀資料.
memset函式
memset是計算機中C/C++語言初始化函式。作用是將某一塊記憶體中的內容全部設定為指定的值, 這個函式通常為新申請的記憶體做初始化工作。
void *memset(void *s, int ch, size_t n);
str -- 指向要填充的記憶體塊。
c -- 要被設定的值。該值以 int 形式傳遞,但是函式在填充記憶體塊時是使用該值的無符號字元形式。
n -- 要被設定為該值的字元數。
檔案的偏移量
在每個開啟的檔案中都有一個檔案的偏移量,檔案的偏移量會根據檔案的讀寫而改變位置。我們可以通過 lseek() 函式來調整檔案的偏移量。預設情況下,新開啟檔案的檔案偏移量在檔案的開始。同 write() 和 read() 函式類似,要使用這個函式,需要包含 #include <unistd.h> 。下面是函式的說明:
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:
當前檔案指標的位置,非負整數是成功,-1是失敗
lseek(fd , 0 ,SEEK_SET);//將檔案偏移量設定到檔案開始第一個位元組上
lseek(fd , -1 ,SEEK_END);//將檔案偏移量設定在檔案倒數第一個位元組上
在資料寫入成功後,把檔案偏移量移到檔案開始第一行位元組上
if((rv = write(fd, MSG_STR,strlen(MSG_STR)))<0)
if(rv == -1)
{
printf("寫入失敗\n");
}
else
{
printf("寫入資料成功\n");
goto cleanup;
}
lseek(fd,0,SEEK_SET);
關閉檔案
當檔案不再被使用時,可以呼叫 close() 函式來關閉被開啟的檔案。 除了用 close() 顯示地關閉檔案外,通過結束程序也能隱式地關閉被該程序開啟的所有檔案。要使用該函式,需要包含 #include <unistd.h> 。下面是函式的說明:
int close(int fildes);
args:
int fildes: 要關閉檔案的檔案描述符
return:
檔案關閉狀態,0是成功,-1是失敗
程式碼實現
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#define BUFSIZE 1024
#define MSG_STR "i love linux\n"
int main(int argc ,char **argv)
{
int fd = -1;
int rv = -1;
char buf[BUFSIZE];
if((fd = open("test.txt",O_CREAT|O_RDWR,0666))<0)
if(fd==-1)
{
printf("檔案建立失敗\n");
goto cleanup;
}
else
{
printf("檔案開啟成功,fd = %d\n",fd);
}
if((rv = write(fd, MSG_STR,strlen(MSG_STR)))<0)
if(rv == -1)
{
printf("寫入資料失敗\n");
goto cleanup;
}
else
{
printf("寫入資料成功\n");
}
lseek(fd,0,SEEK_SET);
memset(buf,0,sizeof(buf));
if((rv = read(fd,buf,sizeof(buf)))<0)
if(rv == -1)
{
printf("讀寫失敗\n");
goto cleanup;
}
else
{
printf("讀寫成功\n");
}
printf("讀寫的資料 %d\n %s\n",rv,buf);
cleanup:
close(fd);
return 0;
}
strerror函式
C 庫函式 char *strerror(int errnum) 從內部陣列中搜索錯誤號 errnum,並返回一個指向錯誤訊息字串的指標。strerror 生成的錯誤字串取決於開發平臺和編譯器。
char *strerror(int errnum)
進一步程式碼實現
#include <stdio.h>
#include <string.h>
#include <fcntl.h>
#include <errno.h>
#include <sys/stat.h>
#include <sys/types.h>
#define BUFSIZE 1024
#define MSG_STR "hello world\n"
int main(int argc ,char **argv)
{
int fd = -1;
int rv = -1;
char buf[BUFSIZE];
fd = open("test.txt",O_RDWR|O_CREAT|O_TRUNC,0666);
if(fd < 0)
{
perror("建立/開啟檔案失敗");
return 0;
}
printf("開啟檔案成功 [%d]\n",fd);
if((rv = write(fd,MSG_STR,strlen(MSG_STR))) < 0)
{
printf("檔案寫入失敗:%s\n",strerror(errno));
goto cleanup;
}
lseek(fd, 0 ,SEEK_SET);
memset(buf, 0 ,sizeof(buf));
if((rv = read(fd,buf,sizeof(buf))) < 0)
{
printf("讀檔案失敗:%s\n",strerror(errno));
goto cleanup;
}
printf("從檔案讀出%d資料:%s\n",rv,buf);
cleanup:
close(fd);
return 0;
}