Linux下的C編程實戰之文件系統編程

 在Linux平臺下對文件編程可以使用兩類函數:(1Linux操作系統文件API;(2C語言I/O庫函數。前者依賴于Linux系統調用,后者實際上與操作系統是獨立的,因為在任何操作系統下,使用C語言I/O庫函數操作文件的方法都是相同的。本章將對這兩種方法進行實例講解。


1. 文件IO操作


S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP


? fd=open("/dev/globalvar", O_RDWR, S_IRUSR|S_IWUSR); ? ? //可讀寫方式打開設備文件

S_IRUSR

Permits the file's owner to read it.
S_IWUSR
Permits the file's owner to write to it.
S_IRGRP
Permits the file's group to read it.
S_IWGRP
Permits the file's group to write to it.
S_ISDIR ( ) 目錄文件
S_ISCHR ( ) 字符特殊文件
S_ISBLK ( ) 塊特殊文件
S_ISFIFO ( ) 管道或F I F O
S_ISLNK ( ) 符號連接( P O S I X . 1S V R 4無此類型)
S_ISSOC K ( ) 套接字(P O S I X . 1S V R 4無此類型)

S_ISREG ( ) 普通文件



   2.Linux 文件 API

   Linux 的文件操作 API 涉及到創建、打開、讀寫和關閉文件。

  創建

int creat(const char *filename, mode_t mode);


  參數mode指定新建文件的存取權限,它同umask一起決定文件的最終權限(mode&umask),其中umask代表了文件在創建時需要去掉的一些存取權限。umask可通過系統調用umask()來改變:

int umask(int newmask);


  該調用將umask設置為newmask,然后返回舊的umask,它只影響讀、寫和執行權限。

  打開

int open(const char *pathname, int flags);?

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


  open函數有兩個形式,其中pathname是我們要打開的文件名(包含路徑名稱,缺省是認為在當前路徑下面)flags可以去下面的一個值或者是幾個值的組合:

標志含義

O_RDONLY以只讀的方式打開文件

O_WRONLY以只寫的方式打開文件

O_RDWR以讀寫的方式打開文件

O_APPEND以追加的方式打開文件

O_CREAT 創建一個文件

O_EXEC如果使用了O_CREAT而且文件已經存在,就會發生一個錯誤

O_NOBLOCK以非阻塞的方式打開一個文件

O_TRUNC如果文件已經存在,則刪除文件的內容

? ?


  O_RDONLYO_WRONLYO_RDWR三個標志只能使用任意的一個。

  如果使用了O_CREATE標志,則使用的函數是int open(const char *pathname,int flags,mode_t mode);這個時候我們還要指定mode標志,用來表示文件的訪問權限。mode可以是以下情況的組合:

標志含義

S_IRUSR 用戶可以讀

S_IWUSR 用戶可以寫

S_IXUSR 用戶可以執行

S_IRWXU用戶可以讀、寫、執行

S_IRGRP 組可以讀

S_IWGRP 組可以寫

S_IXGRP 組可以執行

S_IRWXG 組可以讀寫執行

S_IROTH 其他人可以讀

S_IWOTH 其他人可以寫

S_IXOTH 其他人可以執行

S_IRWXO其他人可以讀、寫、執行

S_ISUID 設置用戶執行ID

S_ISGID 設置組的執行ID


  除了可以通過上述宏進行邏輯產生標志以外,我們也可以自己用數字來表示,Linux總共用5個數字來表示文件的各種權限:第一位表示設置用戶ID;第二位表示設置組ID;第三位表示用戶自己的權限位;第四位表示組的權限;最后一位表示其他人的權限。每個數字可以取1(執行權限)2(寫權限)4(讀權限)0()或者是這些值的和。例如,要創建一個用戶可讀、可寫、可執行,但是組沒有權限,其他人可以讀、可以執行的文件,并設置用戶ID位。那么,我們應該使用的模式是1(設置用戶ID)0(不設置組ID)7(1+2+4,讀、寫、執行)0(沒有權限)5(1+4,讀、執行)10705

open("test", O_CREAT, 10705);


  上述語句等價于:

open("test", O_CREAT, S_IRWXU | S_IROTH | S_IXOTH | S_ISUID );


  如果文件打開成功,open函數會返回一個文件描述符,以后對該文件的所有操作就可以通過對這個文件描述符進行操作來實現。

  讀寫

  在文件打開以后,我們才可對文件進行讀寫了,Linux中提供文件讀寫的系統調用是readwrite函數:

int read(int fd, const void *buf, size_t length);

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


  其中參數buf為指向緩沖區的指針,length為緩沖區的大小(以字節為單位)。函數read()實現從文件描述符fd所指定的文件中讀取length個字節到buf所指向的緩沖區中,返回值為實際讀取的字節數。函數write實現將把length個字節從buf指向的緩沖區中寫到文件描述符fd所指向的文件中,返回值為實際寫入的字節數。

  以O_CREAT為標志的open實際上實現了文件創建的功能,因此,下面的函數等同creat()函數:

int open(pathname, O_CREAT | O_WRONLY | O_TRUNC, mode);


  定位

  對于隨機文件,我們可以隨機的指定位置讀寫,使用如下函數進行定位:

int lseek(int fd, offset_t offset, int whence);


  lseek()將文件讀寫指針相對whence移動offset個字節。操作成功時,返回文件指針相對于文件頭的位置。參數whence可使用下述值:

  SEEK_SET:相對文件開頭
  SEEK_CUR:相對文件讀寫指針的當前位置
  SEEK_END:相對文件末尾

  offset可取負值,例如下述調用可將文件指針相對當前位置向前移動5個字節:

lseek(fd, -5, SEEK_CUR);


  由于lseek函數的返回值為文件指針相對于文件頭的位置,因此下列調用的返回值就是文件的長度:

lseek(fd, 0, SEEK_END);


  關閉

  當我們操作完成以后,我們要關閉文件了,只要調用close就可以了,其中fd是我們要關閉的文件描述符:

int close(int fd);


  例程:編寫一個程序,在當前目錄下創建用戶可讀寫文件“hello.txt”,在其中寫入“Hello, software weekly”,關閉該文件。再次打開該文件,讀取其中的內容并輸出在屏幕上。

#include <sys/types.h>

#include <sys/stat.h>

#include <fcntl.h>

#include <stdio.h>

#define LENGTH 100

main()

{

 int fd, len;

 char str[LENGTH];?

 fd = open("hello.txt", O_CREAT | O_RDWR, S_IRUSR | S_IWUSR); /*創建并打開文件 */

 if (fd)?

 {

  write(fd, "Hello, Software Weekly", strlen("Hello, software weekly")); /*寫入 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);

}


 3.C語言庫函數

  C庫函數的文件操作實際上是獨立于具體的操作系統平臺的,不管是在DOSWindowsLinux還是在VxWorks中都是這些函數:

  創建和打開

FILE *fopen(const char *path, const char *mode);


  fopen()實現打開指定文件filename,其中的mode為打開模式,C語言中支持的打開模式如下表:

標志含義

r, rb以只讀方式打開

w, wb以只寫方式打開。如果文件不存在,則創建該文件,否則文件被截斷

a, ab以追加方式打開。如果文件不存在,則創建該文件

r+, r+b, rb+ 以讀寫方式打開

w+, w+b, wh+以讀寫方式打開。如果文件不存在時,創建新文件,否則文件被截斷

a+, a+b, ab+以讀和追加方式打開。如果文件不存在,創建新文件


  其中b用于區分二進制文件和文本文件,這一點在DOSWindows系統中是有區分的,但Linux不區分二進制文件和文本文件。

  讀寫

  C庫函數支持以字符、字符串等為單位,支持按照某中格式進行文件的讀寫,這一組函數為:

int fgetc(FILE *stream);

int fputc(int c, FILE *stream);

char *fgets(char *s, int n, FILE *stream);

int fputs(const char *s, FILE *stream);

int fprintf(FILE *stream, const char *format, ...);

int fscanf (FILE *stream, const char *format, ...);

size_t fread(void *ptr, size_t size, size_t n, FILE *stream);

size_t fwrite (const void *ptr, size_t size, size_t n, FILE *stream);


  fread()實現從流stream中讀取加n個字段,每個字段為size字節,并將讀取的字段放入ptr所指的字符數組中,返回實際已讀取的字段數。在讀取的字段數小于num時,可能是在函數調用時出現錯誤,也可能是讀到文件的結尾。所以要通過調用feof()ferror()來判斷。

  write()實現從緩沖區ptr所指的數組中把n個字段寫到流stream中,每個字段長為size個字節,返回實際寫入的字段數。

  另外,C庫函數還提供了讀寫過程中的定位能力,這些函數包括

int fgetpos(FILE *stream, fpos_t *pos);

int fsetpos(FILE *stream, const fpos_t *pos);

int fseek(FILE *stream, long offset, int whence);?

等。


  關閉

  利用C庫函數關閉文件依然是很簡單的操作:

int fclose (FILE *stream);


  例程:將第2節中的例程用C庫函數來實現。

#include <stdio.h>

#define LENGTH 100

main()

{

 FILE *fd;

 char str[LENGTH];


 fd = fopen("hello.txt", "w+"); /*創建并打開文件 */

 if (fd)

 {

  fputs("Hello, Software Weekly", fd); /*寫入Hello, software weekly字符串 */

  fclose(fd);

 }


 fd = fopen("hello.txt", "r");

 fgets(str, LENGTH, fd); /*讀取文件內容 */

 printf("%s\n", str);

 fclose(fd);

}


  4.小結

  Linux提供的虛擬文件系統為多種文件系統提供了統一的接口,Linux的文件編程有兩種途徑:基于Linux系統調用;基于C庫函數。這兩種編程所涉及到文件操作有新建、打開、讀寫和關閉,對隨機文件還可以定位。本章對這兩種編程方法都給出了具體的實例。



帶緩沖I/O 不帶緩沖I/O詳解


?以下是我對這兩者的理解:

首先要明白不帶緩沖的概念:所謂不帶緩沖,并不是指內核不提供緩沖,而是只單純的系統調用,不是函數庫的調用。系統內核對磁盤的讀寫都會提供一個塊緩沖,當用write函數對其寫數據時,直接調用系統調用,將數據寫入到塊緩沖進行排隊,當塊緩沖達到一定的量時,才會把數據寫入磁盤。因此所謂的不帶緩沖的I/O是指進程不提供緩沖功能。每調用一次writeread函數,直接系統調用。

而帶緩沖的I/O是指進程對輸入輸出流進行了改進,提供了一個流緩沖,當用fwrite函數網磁盤寫數據時,先把數據寫入流緩沖區中,當達到一定條件,比如流緩沖區滿了,或刷新流緩沖,這時候才會把數據一次送往內核提供的塊緩沖,再經塊緩沖寫入磁盤。

因此,帶緩沖的I/O在往磁盤寫入相同的數據量時,會比不帶緩沖的I/O調用系統調用的次數要少。


下面的東西是我從網上查到的對這兩者的理解,我覺得還是很到位的:

以下主要討論關于open,write等基本系統IO的帶緩沖與不帶緩沖的差別


? ? ? 帶緩存的文件操作是標準C 庫的實現,第一次調用帶緩存的文件操作函數時標準庫會自動分配內存并且讀出一段固定大小的內容存儲在緩存中。所以以后每次的讀寫操作并不是針對硬盤上的文 件直接進行的,而是針對內存中的緩存的。何時從硬盤中讀取文件或者向硬盤中寫入文件有標準庫的機制控制。不帶緩存的文件操作通常都是系統提供的系統調用, 更加低級,直接從硬盤中讀取和寫入文件,由于IO瓶頸的原因,速度并不如意,而且原子操作需要程序員自己保證,但使用得當的話效率并不差。另外標準庫中的 帶緩存文件IO 是調用系統提供的不帶緩存IO實現的。


術語不帶緩沖指的是每個readwrite都調用嗯內核中的一個系統調用。所有的磁盤I/O都要經過內核的塊緩沖(也稱內核的緩沖區高速緩 存),唯一例外的是對原始磁盤設備的I/O。既然readwrite的數據都要被內核緩沖,那么術語不帶緩沖的I/O“指的是在用戶的進程中對這兩個 函數不會自動緩沖,每次readwrite就要進行一次系統調用。“--------摘自<unix環境編程>


程序中用openwrite打開創建并把“hello world“寫入文件test.txt,相應用fopenfwrite操作文件test2.txt。程序執行到openfopen之后,sleep 15秒,這時用ls查看生成了文件沒,這時用open打開的test.txt出現了,但是fopentest2.txt沒有;當程序執行完write fwrite之后,fopentest2.txt仍然沒有出現(還是用ls查看),再用cattest.txt,可以看到 “helloworld”;最后再關閉test.txttest2.txt,這時test2.txt出現了,并且其內容也是“hello world“

?? 該例子證明了openwrite是不帶緩沖的,即程序一執行其io操作也立即執行,不會停留在系統提供的緩沖里,不需等到close操作完才執行。與之相比的fopenfwrite則是帶緩沖的,(一般)要等到fclose操作完后才會執行。

??

? 相關的源碼示例如下:

?i nclude <unistd.h>

i nclude <iostream>

i nclude <fcntl.h>

i nclude <string>

i nclude <sys/types.h>

i nclude <sys/stat.h>

using namespace std;


int main(){

?int fd;

?FILE *file;

?char *s="hello,world\n";

?if((fd=open("test.txt",O_WRONLY|O_CREAT,S_IRUSR|S_IWUSR))==-1){

? cout<<"Error open file"<<endl;

? return -1;

?}

?if((file=fopen("test2.txt","w"))==NULL){

? cout<<"Error Open File."<<endl;

? return -1;

?}

?cout<<"File has been Opened."<<endl;

?sleep(15);

?if(write(fd,s,strlen(s))<strlen(s)){

? cout<<"Write Error"<<endl;

? return -1;

?}

?if(fwrite(s,sizeof(char),strlen(s),file)<strlen(s)){

? cout<<"Write Error in 2."<<endl;

? return -1;

?}

?cout<<"After write"<<endl;

?sleep(15);

?cout<<"After sleep."<<endl;

?close(fd);

?return 0;

}

詳情請見:http://blog.csai.cn/user1/27828/archives/2007/14285.html


ssize_t write(int filedes, const void *buff, size_t nbytes)size_t fwrite(const void *ptr, size_t size, size_t nobj, FILE *fp)來講講自己對unix系統下帶緩存的I/O和不帶緩存的I/O的區別。


?

? ? 首先要清楚一個概念,所謂的代緩存并不是指上面兩個函數的buff參數,而是指unix系統在內核中所設的緩沖存儲器。


? ? 當將數據寫到文件上時,內核先將該數據寫到緩存,如果該緩存未滿,則并不將其排入輸出隊列,直到緩存寫滿或者內核再次需要重新使用此緩存時才將其排入輸入隊列,待其到達對首,在進行實際的I/O操作,也就是此時才把數據真正寫到磁盤,這種技術叫延遲寫。


? ? 現在假設內核所設的緩存是100個字節,如果你使用write,且buffsize10,當你要把9個同樣的buff寫到文件時,你需要調用9write,也就是9次系統調用,此時也并沒有寫到硬盤,如果想立即寫到硬盤,調用fsync,可以進行實際的I/O操作。


? ? 標準I/O,也就是帶緩存的I/O采用FILE*FILE實際上包含了為管理流所需要的所有信息:實際I/O的文件描述符,指向流緩存的指針(標準I /O緩存,由malloc分配,又稱為用戶態進程空間的緩存,區別于內核所設的緩存),緩存長度,當前在緩存中的字節數,出錯標志等,假設流緩存的長度為 50字節,把以上的數據寫到文件,則只需要2次系統調用(fwrite調用write系統調用),因為先把數據寫到流緩存,當其滿以后或者調用 fflush時才填入內核緩存,所以進行了2次的系統調用write


? ? fflush將流所有未寫的數據送入(刷新)到內核(內核緩沖區),fsync將所有內核緩沖區的數據寫到文件(磁盤)。

?

? ? 不帶緩存的readwrite是相對于fread/fwrite等流函數來說明的,因為freadfwrite是用戶函數(3),所以他們會在用戶層 進行一次數據的緩存,而read/write是系統調用(2)所以他們在用戶層是沒有緩存的,所以稱readwrite是無緩存的IO,其實對于內核來 說還是進行了緩存,不過用戶層看不到罷了。




本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/383476.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/383476.shtml
英文地址,請注明出處:http://en.pswp.cn/news/383476.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

【kali】kali設置burpsuite抓包dvwa

kali自帶burpsuite 配置代理 burpsuite是通過代理來抓包dvwa的 burpsuite&#xff1a;proxy—> options 里邊監聽的應該是127.0.0.1:8080 &#xff08;端口ip如果撞車了都可以自己改&#xff09; 火狐&#xff1a; preferences–>最下邊的network settings點擊settings —…

fopen函數簡介

函數簡介 函數功能&#xff1a;打開一個文件 函數原型&#xff1a;FILE * fopen(const char * path,const char * mode); 相關函數&#xff1a;open&#xff0c;fclose&#xff0c;fopen_s[1]&#xff0c;_wfopen 所需庫&#xff1a;<stdio.h> 返回值&#xff1a;文件順利…

【kali】kali換了root權限后無法打開firefox瀏覽器

從普通權限換成root權限后發現火狐進不去鳥&#xff01;&#xff01; 終端報錯&#xff1a; Running firefox as root in a regular user’s sessin is not supported.($HOME is /home/miehahaha which is owned by uid 1000) 分析&#xff1a; 是的&#xff0c;原來普通權限m…

【win10】局域網內兩臺win10共享文件夾

https://jingyan.baidu.com/article/93f9803f3e9788e0e46f55c8.html

CentOS 7關閉firewalld啟用iptables

在CentOS7中&#xff0c;有很多CentOS 6中的常用服務發生了變化。 其中iptables是其中比較大的一個。防火墻iptables被firewalld取代。 本文將介紹&#xff0c;如果采用systemctl關閉firewalld&#xff0c;開啟iptables。 1.關閉firewalld [roothwcentos70-01 system]# systemc…

wpa_supplicant wifi密碼錯誤檢測

system("/usr/sbin/wpa_supplicant -Dnl80211 -iwlan0 -f /tmp/wpa_log -c/tmp/wpa_supplicant.conf -d -t -B &"); 在/tmp/wpa_log中查看是否有如下的字符串&#xff1a; 1. 針對WPA&#xff0f;WPA2加密方式 //1473218403.305655: wlan0: WPA: 4-Way Handsh…

CentOS 7 安裝nginx

1.安裝pcre pcre-devel yum install pcre pcre-devel -y rpm -qa pcre pcre-devel 2.安裝openssl-devel yum install -y openssl-devel rpm -qa openssl-devel openssl 3.下載nginx cd /home/testuser/mkdir toolscd tools/wget -q http://nginx.org/download/nginx-1.9.9.…

MPEG4與.mp4

流媒體應用中TS和MP4格式分析應該是封包格式。不能簡單理解成MPEG4的簡稱。要詳細解釋這個問題&#xff0c;需要提一下MPEG4和.mp4在概念上的區別。 一般來說&#xff0c;僅提“MPEG4”&#xff0c;是指一種視頻壓縮算法。可以把原始畫面通過數學運算變換成一組二進制數據&…

MP4文件格式的解析,以及MP4文件的分割算法

mp4應該算是一種比較復雜的媒體格式了&#xff0c;起源于QuickTime。以前研究的時候就花了一番的功夫&#xff0c;尤其是如何把它完美的融入到視頻點播應用中&#xff0c;更是費盡了心思&#xff0c;主要問題是處理mp4文件龐大的“媒體頭”。當然&#xff0c;流媒體點播也可以采…

MP4文件格式詳解

一、基本概念 1.mp4概述 MP4文件中的所有數據都裝在box&#xff08;QuickTime中為atom&#xff09;中&#xff0c;也就是說MP4文件由若干個box組成&#xff0c;每個box有類型和長度&#xff0c;可以將box理解為一個數據對象塊。box中可以包含另一個box&#xff0c;這種box稱為c…

H264—MP4格式及在MP4文件中提取H264的SPS、PPS及碼流

SkySeraph Apr 1st 2012 Email&#xff1a;skyseraph00163.com 一、MP4格式基本概念 MP4格式對應標準MPEG-4標準(ISO/IEC14496) 二、MP4封裝格式核心概念 1 MP4封裝格式對應標準為 ISO/IEC 14496-12&#xff08;信息技術 視聽對象編碼的第12部分: ISO 基本媒體文件格式/Info…

AAC音頻格式分析

關于AAC音頻格式基本情況&#xff0c;可參考維基百科http://en.wikipedia.org/wiki/Advanced_Audio_Coding AAC音頻格式分析 AAC音頻格式有ADIF和ADTS&#xff1a; ADIF&#xff1a;Audio Data Interchange Format 音頻數據交換格式。這種格式的特征是可以確定的找到這個音頻數…

tar壓縮隱藏文件

如果想tar 壓縮包含隱藏文件的目錄&#xff0c;同時排除掉部分無用的目錄 tar -czvf 20161009.tar.gz * .[!.]* --exclude .git 在Linux下打包tar文件時添加密碼的方法 在當前目錄下有一個pma目錄的文件夾: 1、使用tar對文件壓縮加密&#xff1a; 代碼如下: # tar -zcvf - pma…

linux 怎么把^M去掉

在linux下&#xff0c;不可避免的會用VIM打開一些windows下編輯過的文本文件。我們會發現文件的每行結尾都會有一個^M符號&#xff0c;這是因為 DOS下的編輯器和Linux編輯器對文件行末的回車符處理不一致&#xff0c; 對于回車符的定義&#xff1a; windows&#xff1a;0D0A un…

關于cp命令中拷貝所有的寫法

今天在編寫一個腳本的時候&#xff0c;發現一個比較奇怪的問題&#xff1a;就是在使用cp拷貝當前目錄下所有文件到目標目錄的時候&#xff0c;源和目標大大不同。原來一直沒有留意有這樣的問題&#xff0c;后來查了些資料&#xff0c;才知道以前一直使用的格式有誤&#xff0c;…

cp -r 和 cp -R 的區別

今天倒騰linux根文件系統的時候發現 cp -r /dev /dev_bak 時&#xff0c;竟然會 將磁盤設備中的數據進行一次拷貝&#xff0c;而不是僅僅建立設備文件。于是到網上搜了一把&#xff0c;收獲不小。http://www.loveunix.net/html/200407/33920.html這里有人問同樣的問題&#xff…

Makefile選項CFLAGS,LDFLAGS,LIBS

CFLAGS 表示用于 C 編譯器的選項&#xff0c; CXXFLAGS 表示用于 C 編譯器的選項。 這兩個變量實際上涵蓋了編譯和匯編兩個步驟。 CFLAGS&#xff1a; 指定頭文件&#xff08;.h文件&#xff09;的路徑&#xff0c;如&#xff1a;CFLAGS-I/usr/include -I/path/include。同樣地…

smbclient和掛載samba共享目錄

1&#xff0c;列出某個IP地址所提供的共享文件夾 smbclient -L 198.168.0.1 -U marsaber%12332112345672,像FTP客戶端一樣使用smbclient smbclient //192.168.0.1/tmp -U marsaber%1233211234567 執行smbclient命令成功后&#xff0c;進入smbclient環境&#xff0c;出現提示符…

linux 下source命令

當我修改了/etc/profile文件&#xff0c;我想讓它立刻生效&#xff0c;而不用重新登錄&#xff1b;這時就想到用 source 命令&#xff0c;如:source /etc/profile對source進行了 學習 &#xff0c;并且用它與sh 執行腳本進行了對比&#xff0c;現在總結一下。source命令&#x…

make Image uImage與zImage的區別

內核編譯&#xff08;make&#xff09;之后會生成兩個文件&#xff0c;一個Image&#xff0c;一個zImage&#xff0c;其中Image為內核映像文件&#xff0c;而zImage為內核的一種映像壓縮文件&#xff0c;Image大約為4M&#xff0c;而zImage不到2M。 那么uImage又是什么的&#…