Linux 基礎 IO 全面介紹

Linux - 基礎 IO

文件的宏觀理解:

狹義理解:

廣義理解:

文件操作的歸類認知:

系統角度:

文件 IO 相關操作

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

fputs 函數是將 s 所指向的數據往 stream 中所指向的文件中寫


char * fgets ( char * str, int num, FILE * stream )

注:

fwrite 的使用方法

stdin & stdout & stderr



注:

系統文件 I/O

open

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

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

pathname: 要打開或創建的目標文件
flags: 打開文件時,可以傳入多個參數選項,用下面的一個或者多個常量進行“或”運算,構成flags。
參數:
   O_RDONLY: 只讀打開
   O_WRONLY: 只寫打開
   O_RDWR : 讀,寫打開
     這三個常量,必須指定一個且只能指定一個
   O_CREAT : 若文件不存在,則創建它。需要使用mode選項,來指明新文件的訪問權限
   O_APPEND: 追加寫
 返回值:
     成功:新打開的文件描述符
    失敗:-1

注:


注:write read close lseek…… 與 C 語言文件相關接口用法類似

文件描述符 fd


注:

文件描述符就是從 0 開始的小整數。當打開文件時,操作系統在內存中要創建相應的數據結構來描述目標文件。於是就有了 file 結構體。表示一個已經打開的文件對象。而進程執行 open 系統調用,所以必須讓進程和文件關聯起來。每個進程都有一個指針 files_struct*, 指向一張表 files_struct, 該表最重要的部分就是包涵一個指針數組,每個元素都是一個指向打開文件的指針!所以,本質上,文件描述符就是該數組的下標。只要拿着文件描述符,就可以找到對應的文件

補充:

文件描述符的分配規則

總結:

重定向



補充:程序替換的時候不會影響重定向對應的數據結構的數據(程序替換影響的是進程虛擬地址空間部分,而重定向影響的是 files_struct 部分)

使用 dup2 系統調用

#include <unistd.h>
int dup2(int oldfd, int newfd);

注:

FILE

因爲 IO 相關函數與系統調用接口對應,並且庫函數封裝系統調用,所以本質上,訪問文件都是通過 fd 訪問的。因此 C 庫當中的 FILE 結構體內部,必定封裝了 fd

typedef struct _IO_FILE FILE; 在/usr/include/stdio.h
在/usr/include/libio.h
struct _IO_FILE {
 int _flags; /* High-order word is _IO_MAGIC; rest is flags. */
#define _IO_file_flags _flags
 //緩衝區相關
 /* The following pointers correspond to the C++ streambuf protocol. */
 /* Note: Tk uses the _IO_read_ptr and _IO_read_end fields directly. */
 char* _IO_read_ptr; /* Current read pointer */
 char* _IO_read_end; /* End of get area. */
 char* _IO_read_base; /* Start of putback+get area. */
 char* _IO_write_base; /* Start of put area. */
  char* _IO_write_ptr; /* Current put pointer. */
 char* _IO_write_end; /* End of put area. */
 char* _IO_buf_base; /* Start of reserve area. */
 char* _IO_buf_end; /* End of reserve area. */
 /* The following fields are used to support backing up and undo. */
 char *_IO_save_base; /* Pointer to start of non-current get area. */
 char *_IO_backup_base; /* Pointer to first valid character of backup area */
 char *_IO_save_end; /* Pointer to end of non-current get area. */
 struct _IO_marker *_markers;
 struct _IO_FILE *_chain;
 int _fileno; //封裝的文件描述符
#if 0
 int _blksize;
#else
 int _flags2;
#endif
 _IO_off_t _old_offset; /* This used to be _offset but it's too small. */
#define __HAVE_COLUMN /* temporary */
 /* 1+column number of pbase(); 0 is unknown. */
 unsigned short _cur_column;
 signed char _vtable_offset;
 char _shortbuf[1];
 /* char* _save_gptr; char* _save_egptr; */
 _IO_lock_t *_lock;
#ifdef _IO_USE_OLD_IO_FILE
};

總結:

  1. 底層對應的文件描述符下標
  1. 應用層 C 語言提供的緩衝區數據

一般 C 庫函數寫入文件時是全緩衝的,而寫入顯示器是行緩衝。printf fprintf 等庫函數會自帶緩衝區,當發生重定向到普通文件時,數據的緩衝方式由行緩衝變成了全緩衝。而我們放在緩衝區中的數據,就不會被立即刷新,甚至 fork 之後但是進程退出之後,會統一刷新,寫入文件當中。但是 fork 的時候,父子數據會發生寫時拷貝,所以當你父進程準備刷新的時候,子進程也就有了同樣的一份數據,隨即產生兩份數據。write 沒有變化,說明沒有所謂的緩衝
printf fputs 等 庫函數會自帶緩衝區,而 write 系統調用沒有帶緩衝區。另外,我們這裏所說的緩衝區,都是用戶級緩衝區。其實爲了提升整機性能,OS 也會提供相關內核級緩衝區。printf fprintf 是庫函數, write 是系統調用,庫函數在系統調用的 “上層”, 是對系統調用的 “封裝”,但是 write 有內核級緩衝區,而 printf fwrite fputs 等緩衝區是用戶級緩衝區,由 C 標準庫提供

注:系統調用函數與庫函數儘量不要混在一起使用,可能會與統一使用的函數的運行結果有所差異

文件系統

文件:打開的文件、普通未打開的文件
打開的文件:屬性與操作方法的表現就是 struct file{} 屬於內存級文件
普通未打開的文件:磁盤上面未被加載到內存的
文件系統功能:將上述的這些文件管理起來

磁盤

磁盤是計算機主要的存儲介質,可以存儲大量的二進制數據,並且斷電後也能保持數據不丟失。早期計算機使用的磁盤是軟磁盤(Floppy Disk,簡稱軟盤),如今常用的磁盤是硬磁盤(Hard disk,簡稱硬盤)。


補充:

磁盤的劃分

我們可以將磁盤想象成磁帶(線性結構),將磁盤看成一個線性空間(數組),類型爲扇區的數組、數組個數爲 10 億多

這樣劃分就不用讓 OS 讀取數據時在哪個盤面、哪個磁道、哪個扇區找了,OS 與磁盤映射關係可以通過磁盤驅動來完成,這樣也就做到強解耦性。無論換機械硬盤還是固態硬盤,OS 都不用改變讀取磁盤數據的數據結構,只需改變磁盤的驅動程序即可

注:操作系統讀取磁盤數據時的下標——LBA

inode

Linux ext2 文件系統,上圖爲磁盤文件系統圖(內核內存映像肯定有所不同),磁盤是典型的塊設備,硬盤分區被劃分爲一個個的 block。一個 block 的大小是由格式化的時候確定的,並且不可以更改。例如 mke2fs 的 - b 選項可以設 定 block 大小爲 1024、2048 或 4096 字節。而啓動塊(Boot Block)的大小是確定的,

注:

總結:

創建一個新文件主要有一下 4 個操作:

大多是操作系統在同一個目錄下是不允許存在同名文件的 刪除文件不需要清空該文件佔據的所有的空間數據(只需將該文件的 inode 和對應的數據塊無效化即可(文件對應 inode 和 Block 位圖中的數字 1 設置爲 0,並將該文件所對應的目錄中的數據塊的關於該文件內容清空即可) Linux 下屬性和內容是分離的,屬性 inode 保存的(在同一塊塊組 inode 編號是不同的,但是跨組的 inode 編號可能相同),內容 Data blocks 保存的

補充:

軟硬連接

硬鏈接:

硬鏈接的應用場景:方便進行相對路徑的路徑的設置


因此,可以看出.、… 的底層實現是通過硬鏈接的方式來實現的
注:

軟鏈接:

注:硬鏈接是通過 inode 引用另外一個文件,軟鏈接是通過名字引用另外一個文件

總結:軟硬鏈接的區別:本質是是否是獨立文件,有無獨立 inode;用途:軟鏈接可以指向特定的文件方便進行快速索引,硬鏈接是能進行相對路徑設置

補充:

文件的 ACM


總結:

文件的 ACM 的應用場景:

動態庫和靜態庫

靜態庫與動態庫


注:

總結:

生成靜態庫

[root@localhost linux]# ls
add.c add.h main.c sub.c sub.h
[root@localhost linux]# gcc -c add.c -o add.o
[root@localhost linux]# gcc -c sub.c -o sub.o
生成靜態庫
[root@localhost linux]# ar -rc libmymath.a add.o sub.o 
ar是gnu歸檔工具,rc表示(replace and create)
查看靜態庫中的目錄列表
[root@localhost linux]# ar -tv libmymath.a 
rw-r--r-- 0/0 1240 Sep 15 16:53 2017 add.o
rw-r--r-- 0/0 1240 Sep 15 16:53 2017 sub.o
t:列出靜態庫中的文件
v:verbose 詳細信息
[root@localhost linux]# gcc main.c -I -L. -lmymath
-L 指定庫路徑
-I 指定頭文件路徑
-l 指定庫名
測試目標文件生成後,靜態庫刪掉,程序照樣可以運行

注:

C 語言編譯時直接編譯不用任何選項:

當自己的可執行程序編譯時不想用這些選項:將頭文件和庫文件分別拷貝到默認路徑下——庫的安裝(第三方庫)(使用時必須帶上 - l 庫名稱) 當只有靜態庫時,沒有動態庫,用 gcc 編譯(不加 - static)會直接用靜態鏈接生成可執行程序

補充:

生成動態庫

補充:

運行動態庫

  1. 拷貝. so 文件到系統共享庫路徑下, 一般指 / usr/lib 2. 更改 LD_LIBRARY_PATH(當系統重啓時使用之前添加的是無效的,應重新添加)
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:路徑

3.ldconfig 配置 / etc/ld.so.conf.d/,ldconfig 更新

本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/3NAvuM6FpWI4_SjFwsLcQA