Linux C高级编程——文件操作之系统调用
Linux C高級編程文件操作之系統(tǒng)調用
宗旨:技術的學習是有限的,分享的精神是無限的!
?
? ? ? ? 庫函數是一些完成特定功能的函數,一般由某個標準組織制作發(fā)布,并形成一定的標準。使用庫函數編寫的函數一般可以應用于不同的平臺而不需要做任何修改,具有很好的可移植性。
? ? ? ? 系統(tǒng)調用函數與操作系統(tǒng)直接相關,不同的操作系統(tǒng)所使用的系統(tǒng)調用可能不太一樣,因此,如果兩個操作系統(tǒng)差異很大,系統(tǒng)調用函數的可移植性就不高。例如windows采用的系統(tǒng)調用的應用程序不能直接在Linux下編譯運行。
? ? ? ? 之所以使用系統(tǒng)調用是因為系統(tǒng)資源的有限性以及內核管理的方便,系統(tǒng)調用將上層內的應用開發(fā)與底層的硬件實現(xiàn)分開,上層應用不需要關注底層硬件的具體實現(xiàn)。Linux的系統(tǒng)調用使用軟中斷實現(xiàn),使用系統(tǒng)調用后,該程序的狀態(tài)將從用戶態(tài)切換到內核態(tài)。庫函數實現(xiàn)最終也要調用系統(tǒng)調用函數,但它封裝了系統(tǒng)調用操作,從而增加了代碼的可移植性。
?
1、open()函數
——用于打開或者創(chuàng)建一個文件
(1)函數原型:
#include<sys/types.h> #include<sys/stat.h> #include<fcntl.h> int open(constchar* pathname, int flags, ...)(2)參數
pathname:要創(chuàng)建或者打開的文件名
flags: 指定文件的打開模式、標志等信息
必須指定一個:O_RDONLY ——只讀?? O_WRONLY——只寫 O_RDWR——讀寫
可選標志(按位或):O_APPEND——追加
O_TRUNC——若文件存在,讀寫方式打開或只寫打開,則文件長度為0
O_CREAT——若文件不存在,則創(chuàng)建文件,此時,open需要第三個參數,用于指定該? 文件的訪問權限(umask可以看掩碼)
O_EXCL——若同時指定為O_CREAT標志,而文件已經存在,則會出錯,可用于文件? 是否存在
O_NONBLOCK對于設備文件,以O_NONBLOCK方式打開可以做非阻塞I/O
(3)返回值
整數類型——成功時,返回文件描述符;出錯時,返回-1?
(4)文件描述符
(文件描述符——已打開文件的索引——通過索引找到已打開文件)
文件描述符是一個非負的整數
文件描述符0,1,2分別表示標準輸入,標準輸出,標準錯誤輸出,在進程創(chuàng)建時,已經打開。open返回的文件描述符一定是該進程尚未使用的最小描述符?
(5)出錯處理
errno.h頭文件中,定義了errno,當API調用出錯時,errno說明出錯的具體原因
可簡單的將errno理解成整型數據
出錯信息轉換成可讀字符串
#include<string.h>
char* strerror(int errno);
perror函數根據當前的errno,輸出一條出錯信息(void perror(constchar* msg))
#include<stdio.h> #include<stdlib.h>#include<sys/types.h> #include<sys/stat.h> #include<fcntl.h>int main(int argc, char *argv[]) {int fd;if(argc < 2){puts("please input the open filepathname!\n");exit(1);}//如果flag參數里有O_CREAT表示,該文件如果不存在,系統(tǒng)則會創(chuàng)建該文件,該文 件的權限由第三個參數決定,此處為0755//如果flah參數里沒有O_CREAT參數,則第三個參數不起作用.此時,如果要打開的 文件不存在,則會報錯.//所以fd=open(argv[1],O_RDWR),僅僅只是打開指定文件if((fd = open(argv[1], O_CREAT | O_RDWR, 0755)) < 0){perror("open filefailure!\n");exit(1);}else{printf("open file %d success!\n", fd);}close(fd);return 0; }<span style="font-family: Arial, Helvetica, sans-serif; background-color: rgb(255, 255, 255);">?</span>2、 creat()函數
——用于創(chuàng)建一個新文件
(1)函數原型
int creat(const char *pathname,mode_t mode)
(2)參數
pathname:要創(chuàng)建的文件名(包括路徑信息)
mode:同open的第二個參數,討論文件的訪問權限位時分析:
S_IRUSR——可讀?????? S_IWUSR——可寫????? S_IXUSR——可執(zhí)行?? ? S_IRWXU——可讀、寫、執(zhí)行
除了用上述宏之外,還可以用數字來表示文件的權限:
可讀——4? 可寫——2? 可執(zhí)行——1? 無任何權限——0?
(3)返回值
成功返回只寫打開的文件描述符,出錯返回-1
(4)creat函數缺點:它以只寫方式打開創(chuàng)建的文件。若要創(chuàng)建一個臨時文件,并先寫該文件,然后又讀該文件,則必須先調用creat,close,然后再open.簡便方法:
open(pathname,O_RDWR| O_CREAT | O_TRUNC,mode);?
#include<stdio.h> #include<stdlib.h>#include<sys/types.h> #include<sys/stat.h> #include<fcntl.h>void create_file(char *filename) {/*創(chuàng)建的文件具有什么樣的屬性?*/if(creat(filename, 0755) < 0){printf("create file %sfailure!\n", filename);exit(EXIT_FAILURE);}else{printf("create file %s success!\n", filename);} }int main(int argc, char *argv[]) {int i;if(argc < 2){perror("you haven't input thefilename,please try again!\n");exit(EXIT_FAILURE);}for(i = 1; i < argc; i++){create_file(argv[i]);}return 0; }?3、lseek函數(和fseek類似)
——用于修改當前文件偏移量
(當前文件偏移量的作用:規(guī)定了從文件什么地方開始進行讀、寫操作)
——通常,讀寫操作結束時,會使文件偏移量增加讀寫的字節(jié)數
——當打開一個文件時,除非指定了O_APPEND標志,否則偏移量被設置為0
(1) 函數原型
#include<sys/types.h> #include<unistd.h> off_t lseek(int filedes, off_t offset, int whence)(2) 參數
第一個參數filedes:open/creat函數返回的文件描述符
第二個參數offset:
相對偏移量:需結合whence才能計算出真正的偏移量
類型off_t:通常情況下是32為數據類型
第三個參數whence:該參數取值是三個常量
SEEK_SET:當前文件偏移量為——距文件開始出的offset個字節(jié)
SEEK_CUR:當前文件偏移量 + offset(可正可負)
SEEK_END:當前文件長度 + offset(可正可負)
(3)返回值:
成功返回新的文件偏移量,失敗返回-1
(4)獲取當前的偏移量:
Off_tcurrent_position;
Current_position= lseek(fd,0,SEEK_CUR);
lseek操作并不引起任何I/O操作,只是修改內核中的記錄(并不引起磁盤的訪問??? 操作)
(5)空洞文件
——使用lseek修改文件偏移量后,當前文件偏移量有可能大于文件的長度
——在這種情況下,對該文件的下一次寫操作,將加長該文件
——這樣文件中形成了一個空洞。對空洞區(qū)域進行讀,均返回0
?
4、read函數
——用于從文件中讀出數據
(1)函數原型
#include<unistd.h> ssize_t read(int fd, void *buff, size_t nbytes)(2)參數
第一個參數fd:文件描述符
第二個參數buff:指向緩沖區(qū),用于存放從文件讀出的數據
第三個參數nbytes:unsigned int;需要從文件中讀出的字節(jié)數
——緩沖區(qū)的大小 >= nbytes?
(3)返回值
返回值類型:ssize_t,即int
出錯返回-1成功:返回從文件中實際讀到的字節(jié)數,當文件讀到結尾時,則返回0
(4)很多情況下,read實際讀出的字節(jié)數都小于要求讀出的字節(jié)數
——讀普通文件,在讀到要求的字節(jié)數之前,就到達了文件尾端
——當從終端設備讀時,通常一次最多讀一行
——當從網絡讀時,網絡中的緩沖機構可能造成read函數返回值小于所要求讀出????????? 的字節(jié)數
——某些面向記錄的設備,如磁盤,一次最多返回一個記錄?
#include<sys/types.h> #include<sys/stat.h> #include<fcntl.h> #include<stdio.h>#define LENGTH 100int main(void) {int fd, len;char str[LENGTH];/* 創(chuàng)建并打開文件 */fd = open("hello.txt", O_CREAT | O_RDWR, S_IRUSR | S_IWUSR);if(fd) {/* 寫入 Hello, software weekly字符串 */write(fd, "Hello,Software Weekly", strlen("Hello, software weekly"));close(fd);}fd = open("hello.txt", O_RDWR);len = read(fd, str, LENGTH);/* 讀取文件內容 */str[len] = '\0';printf("%s\n", str);close(fd);return 0; }<span style="font-family: Arial, Helvetica, sans-serif; background-color: rgb(255, 255, 255);">?</span>5、write函數
——用于向文件里面寫入數據
(1) 函數原型
#include<unistd.h>
ssize_t write(int fd,const void*buff,size_t nbytes)
(2) 參數
第一個參數fd:文件描述符
第二個參數buff:指向緩沖區(qū),存放了需要寫入文件的數據
第三個參數nbytes:需要寫入文件的字節(jié)數
(3) 返回值
返回值類型:ssize_t,即int
出錯返回-1,成功返回實際寫入文件的字節(jié)數
(4) write出錯的原因
——磁盤滿
——沒有訪問權限
——超過了給定進程的文件長度限制
?
6、close函數
——用于關閉一個已打開的文件
(1)函數原型
int close(int filedes)
(2) 返回值
成功返回0,出錯返回-1
(3)參數
filedes:文件描述符
(4)當close函數關閉文件時,會釋放進程加在該文件上的所有記錄鎖
內核會對進程打開文件表,文件對象,索引節(jié)點表項等結構進行修改,釋放相關的?????? 資源
當進程退出時,會關閉當前所有已打開的文件描述符 ? ? ? ? ? ? ? ? ? ? ? ??
?
7、ioctl函數
——用于向設備發(fā)控制和配置命令(這些數據不能用read/write讀寫)
(1) 函數原型
#include <sys/ioctl.h> int ioctl(int d, int request, ...)(2) 參數
第一個參數d:某個設備的文件描述符
第二個參數request:是ioctl的命令,可變參數取決于request,通常是是一個指向? 變量或者結構體的指針
(3)返回值
若出錯返回-1,成功返回其他值,取決于request
ioctl(STDOUT_FILENO,TIOCGWINSZ,&size)——獲得終端設備的窗口大小
manioctl_list——可以看require的各種參數?
?
8、mmap函數
——可以把磁盤文件的一部分直接映射到內存,這樣文件中的位置直接就有對應的內存地址,對文件的讀寫可以直接用指針來做而不需要read/write函數
(1) 函數原型
#include<sys/mman.h>
void* mmap(void *addr,size_t len,int prot,int flag,int filedes,off_t off)
(2) 參數
addr:NULL——內核會自己在進程地址空間中選擇合適的地址建立映射
len:需要映射的那部分文件的長度
off:從文件的什么位置開始映射,必須是頁大小的整數倍(32位——4K)
filedes:該文件的文件描述符
prot:
PROT_EXEC——映射的這一段可執(zhí)行,例如映射的共享庫
PROT_READ——映射的這一段可讀
PROT_WRITE——映射的這一段可寫
PROT_NONE——映射的這一段不可訪問
flag:
MAP_SHARED——多個進程對同一文件的映射是共享的,一個進程對映射的內存做了修改,另一個進程也會看到這種變化。
MAP_PRIVATE——多個進程對同一文件的映射不是共享的,一個進程對映射的內存做了修改,另一個進程不會看到這種變化,也不會真的寫到文件中去。
(3)返回值
成功則返回映射的首地址,出錯返回常數MAP_FAILED。
?
9、access函數
——判斷文件是否可以進行某種操作
(1)函數原型
int access(const char *pathname,int mode)
(2)參數
pathname:文件名
mode:判斷的訪問權限:R_OK:文件可讀? W_OK:文件可寫
?? X_OK:文件可執(zhí)行? F_OK:文件存在
(3)返回值
成功時,返回0;如果一個條件不符合時,返回-1
?
10 dup函數
——復制文件描述符
(1) 函數原型
#include <unistd.h> int dup(int oldfd);(2) 參數
oldfd:待復制的文件描述符
(3)返回值
成功返回新的文件描述符;失敗返回-1
?
11 dup2函數
——復制文件描述符
(1) 函數原型
#include <unistd.h> int dup2(int oldfd, int newfd);(2) 參數
oldfd:待復制的文件描述符
newfd:新的文件描述符
(3) 返回值
成功返回新的文件描述符;失敗返回-1
?
//小項目:用系統(tǒng)調用函數實現(xiàn)文件拷貝的功能: /*************************************************************************> File Name: copy.c> Author: libang> Mail: 18211438613@163.com> Created Time: 2014年07月16日 星期三 00時15分12秒************************************************************************/#include<stdio.h> #include<string.h> #include<unistd.h> #include<sys/stat.h> #include<sys/types.h> #include<fcntl.h> #include<stdlib.h>int copy_file(intsrc_fd, int dest_fd);int main(intargc, char *argv[]) //注意先搭建主框架 {if(argc < 3){printf("inputerror!\n");exit(1);}int src_fd, dest_fd;src_fd = open(argv[1], O_RDONLY);dest_fd = open(argv[2], O_WRONLY | O_CREAT | O_TRUNC, 0666);if(src_fd < 0 | dest_fd < 0){printf("file openfail!\n");exit(0);}copy_file(src_fd, dest_fd);printf("success!\n");close(src_fd);close(dest_fd);return 0; }int copy_file(intsrc, int dest) {char buf[128];int r_ret, w_ret;char *tmp;//memset(buf,0,128);printf("success!\n");while((r_ret = read(src, buf, 128)) > 0) //經典的拷貝算法{tmp = buf;while(r_ret){w_ret = write(dest, tmp, r_ret);r_ret = r_ret - w_ret;printf("%d\n", r_ret);tmp += w_ret;}//memset(buf,0,128);}printf("success!\n");return 0; }總結
以上是生活随笔為你收集整理的Linux C高级编程——文件操作之系统调用的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: CAD插件学习系列教程(四) 一款轻量级
- 下一篇: 基于Python/PYQT5的动物识别专