目錄
一、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);