c/c++:文件IO(Linux系統IO、C標準庫IO、標準C 庫IO和 Linux系統IO的關係、虛擬地址空間、文件描述符表和文件描述符、dup,dup2,fcntl函數)

目錄

一、Linux系統IO和C標準庫IO

1.1 標準C庫IO函數

1.2 標準C 庫IO和 Linux系統IO的關係

二、 虛擬地址空間

三、文件描述符表和文件描述符

6.1 dup和dup2函數

dup  複製文件描述符

dup2 重定向文件描述符

fcntl

四、Linux系統IO函數

五、Linux其他系統函數


 

 

一、Linux系統IO和C標準庫IO

1.1 標準C庫IO函數

 

1.2 標準C 庫IO和 Linux系統IO的關係

標準C 庫IO:帶緩衝區

Linux系統IO:不帶緩衝區,(有些書上說Linux系統有緩存,那是內核的緩存,不是IO的緩存)

 

 

 

二、 虛擬地址空間

下圖以32位機爲例,2的32次方爲4G,64位機是2的64次方,太大了不好畫。

 

三、文件描述符表和文件描述符

  • 文件描述符(file descriptor)通常是一個小的非負整數,內核用以標識一個特定進程正在訪問的文件。當打開一個現有文件或創建一個新文件時,內核向進程返回一個文件描述符。
  • 每個進程在PCB(Process Control Block)中保存着一份文件描述符表,文件描述符就是這個表的索引,每個表項都有一個指向已打開文件的指針。

文件描述符表默認大小: 1024,每個進程啓動之後, 都有一個文件描述符表,所以每個進程默認能打開的文件個數: 1024
前三個文件文件描述符是默認被使用了的:  - 標準輸入 -> 0, 標準輸出 -> 1, 標準錯誤 -> 2

除去被佔用的每個進程默認能打開的文件個數: 1021

 

6.1 dup和dup2函數

  • dup  複製文件描述符

#include <unistd.h>

int dup(int oldfd);

例子:
int ret = dup(3); 

值==3的文件描述符指向一個文件a.txt,返回值是從空閒的文件描述符表中找到的最小的一個, 這時候 4 也指向a.txt

  • dup2 重定向文件描述符

#include <unistd.h>

int dup2(int oldfd, int newfd);

dup2把newfd和newfd指向的文件都close,然後把newfd指向oldfd所指向的文件。

例如:oldfd指向 a.txt, newfd 指向 b.txt,函數調用成功之後: newfd和b.txt做close, newfd指向了a.txt

 oldfd必須是一個有效的文件描述符  , oldfd和newfd是相同的值, 等於什麼也沒做

 

  • fcntl

常用用法:

#include <unistd.h>
#include <fcntl.h>

int ret = fcntl(fd, F_DUPFD);
 - 參數: F_DUPFD, 複製文件描述符, 複製的是第一個參數: fd, 會得到一個新的文件描述符: 返回值ret   (此用法 == dup)
 - 參數: F_GETFL -> 獲取文件的flags屬性
 - 參數: F_SETFL -> 設置文件的flags屬性

例子:

// 設置/獲取文件open時的狀態標誌 -> flags

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

 - 必選項: O_RDONLY,  O_WRONLY,  or  O_RDWR, 這些屬性無法修改
 	- 在open的時候設置了 O_RDONLY, 不能通過fcntl 設置 -> O_RDWR
 - 可以通過fcntl修改flags中的可選項
 	- O_APPEND    數據追加 
 		O_WRONLY | O_APPEND   寫追加 
 	- O_NONBLOCK  設置非阻塞

修改文件flags屬性測試代碼 :

#include <fcntl.h>
#include <unistd.h>
#include <string.h>

int main()
{
    // 打開一個文件
    int fd = open("1.txt", O_WRONLY);
    if(fd == -1)
    {
        perror("open");
        return -1;
    }

    // 追對文件的操作屬性,先得到原來設置的屬性
    int flag = fcntl(fd, F_GETFL); 

    // 屬性添加
    flag |= O_APPEND;

    // 設置屬性到文件描述符中
    fcntl(fd, F_SETFL, flag);

    // 寫文件
    char* p = "你好, 世界....";
    write(fd, p, strlen(p));
    close(fd);

    return 0;
}

 

四、Linux系統IO函數

 

  • errno -> 屬於Linux系統函數庫, 庫裏邊的一個全局變量, 記錄的是錯誤號
#include <stdio.h>
void perror(const char *s);           - s參數: 用戶描述, 比如:hello

perror打印的是errno對應的錯誤描述, 實際輸出: hello: xxxxx(實際的錯誤描述)

 

  •  open
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
  // open函數
  // 打開一個已經存在文件
  int open(const char *pathname, int flags);
  	參數:
          - pathname: 要打開的文件路徑
          - flags: 對文件的操作權限設置
              - O_RDONLY,  O_WRONLY,  O_RDWR 這三個設置是互斥的
       返回值: 
           代碼打開一個文件意味着獲得了這個文件的訪問句柄
  // 使用open創建一個新文件
  int open(const char *pathname, int flags, mode_t mode);
  	參數: 
    	- pathname: 要打開的文件路徑
          - flags: 對文件的操作權限設置
              - 必選項: O_RDONLY,  O_WRONLY,  O_RDWR 這三個設置是互斥的
              - 可選項: O_CREAT -> 文件不存在, 創建新文件
          - mode: 八進制的數, 表示用戶對創建出的新文件的操作權限, 比如: 0775, mode & ~umask
          		0777	-> 111111111
          		0775    -> 111111101
          	&	
                  0775       111111101
         按位與: 0和任何數都爲0

 

  • close
  #include <unistd.h>
  int close(int fd);

 

  • read
#include <unistd.h>
ssize_t read(int fd, void *buf, size_t count);
  	參數: 
  		- fd: open得到的, 通過這個fd操作某一個文件
  		- buf: 緩衝區, 存儲讀到的數據, 數組的地址
  		- count: buf的大小
  	返回值: 
  		- 成功: 
  			>0: 返回實際讀到的字節數
  			==0: 文件已經讀完了
  		- 失敗: -1

 

  • write
#include <unistd.h>
ssize_t write(int fd, const void *buf, size_t count);
  	參數: 
  		- fd: open得到的, 通過這個fd操作某一個文件
  		- buf: 要往磁盤寫入的數據
  		- count: 要寫的數據的實際大小
  	返回值:
  		成功: 實際寫入的字節數
  		失敗: -1

 

  • lseek
#include <sys/types.h>
#include <unistd.h>

  off_t lseek(int fd, off_t offset, int whence);
  	參數: 
  		- fd: open得到的, 通過這個fd操作某一個文件
  		- offset: 偏移量
  		- whence: 
  			SEEK_SET
                設置文件指針的偏移量
         		SEEK_CUR
                設置偏移量: 當前位置 + 第二個參數的值
         		SEEK_END
                設置偏移量: 文件大小 + 第二個參數的值
  int fseek(FILE *stream, long offset, int whence);
  
  
  // 1. 移動文件指針到文件頭
  lseek(fd, 0, SEEK_SET);

  // 2. 獲取當前文件指針的位置
  lseek(fd, 0, SEEK_CUR);

  // 3. 獲取文件長度
  lseek(fd, 0, SEEK_END);

  // 4. 拓展文件長度, 當前文件10b, 110b, 增加的字節的0
  lseek(fd, 100, SEEK_END);
  // 要進行一次寫操作
  write(fd, " ", 1);

 

五、Linux其他系統函數

  • stat / lstat      根據文件名字,獲取文件對應屬性。
 struct stat {
      dev_t          st_dev;        	//文件的設備編號
      ino_t           st_ino;        	//節點
      mode_t      st_mode;      	//文件的類型和存取的權限
      nlink_t        st_nlink;     	//連到該文件的硬連接數目,剛建立的文件值爲1
      uid_t           st_uid;       	//用戶ID
      gid_t           st_gid;       	//組ID
      dev_t          st_rdev;      	//(設備類型)若此文件爲設備文件,則爲其設備編號
      off_t            st_size;      	//文件字節數(文件大小)
      blksize_t     st_blksize;   	//塊大小(文件系統的I/O 緩衝區大小)
      blkcnt_t      st_blocks;    	//塊數
      time_t         st_atime;     	//最後一次訪問時間
      time_t         st_mtime;     	//最後一次修改時間
      time_t         st_ctime;     	//最後一次改變時間(指屬性)
  };
  關於變量 st_mode: 
  	- st_mode -- 16位整數
  		○ 0-2 bit -- 其他人權限
  			- S_IROTH    00004  讀權限
  			- S_IWOTH    00002  寫權限
  			- S_IXOTH    00001  執行權限
  			- S_IRWXO    00007  掩碼, 過濾 st_mode中除其他人權限以外的信息
  		○ 3-5 bit -- 所屬組權限
  			- S_IRGRP    00040  讀權限
  			- S_IWGRP    00020  寫權限
  			- S_IXGRP    00010  執行權限
  			- S_IRWXG    00070  掩碼, 過濾 st_mode中除所屬組權限以外的信息
  		○ 6-8 bit -- 文件所有者權限
  			- S_IRUSR    00400    讀權限
  			- S_IWUSR    00200    寫權限
  			- S_IXUSR    00100    執行權限
  			- S_IRWXU    00700    掩碼, 過濾 st_mode中除文件所有者權限以外的信息
  		○ 12-15 bit -- 文件類型
  			- S_IFSOCK   0140000 套接字
  			- S_IFLNK    0120000 符號鏈接(軟鏈接)
  			- S_IFREG    0100000 普通文件
  			- S_IFBLK    0060000 塊設備
  			- S_IFDIR    0040000 目錄
  			- S_IFCHR    0020000 字符設備
  			- S_IFIFO    0010000 管道
  			- S_IFMT     0170000 掩碼,過濾 st_mode中除文件類型以外的信息
  		(st_mode & S_IFMT) ==  S_IFREG
  


頭文件:
  #include <sys/types.h>
  #include <sys/stat.h>
  #include <unistd.h>

函數說明:
  int stat(const char *pathname, struct stat *buf);
  	參數: 
  		- pathname: 操作的文件的路徑
  		- buf: 結構體變量, 傳出


例子:
  struct stat st;
  stat("hello.txt", &st); 

stat是一個int類型數據,一個int數據可以表示32位2進制數,具體如下:

 

  • opendir  目錄遍歷函數
DIR *opendir(const char *name);
  	參數: 
  		- name: 要打開的目錄
  	返回值: DIR * 
  struct dirent
  {
      ino_t d_ino;               // 此目錄進入點的inode
      ff_t d_off;                // 目錄文件開頭至此目錄進入點的位移
      signed short int d_reclen; // d_name 的長度, 不包含NULL 字符
      unsigned char d_type;      // d_name 所指的文件類型 
      har d_name[256];	       // 文件名
  };
  d_type
  	DT_BLK 	- 塊設備
  	DT_CHR 	- 字符設備
  	DT_DIR 	- 目錄
  	DT_LNK 	- 軟連接
  	DT_FIFO - 管道
  	DT_REG 	- 普通文件
  	DT_SOCK - 套接字
  	DT_UNKNOWN - 未知
  struct dirent *readdir(DIR *dirp);
  	- 返回一個結構體, 這個對應一個文件
  int closedir(DIR *dirp);

 

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章