3.2.1.linux中各種檔案類型
3.2.1.1、普通檔案(— regular file)
(1)文本檔案。檔案中的内容是由文本構成的,文本指的是ASCII碼字元。檔案裡的内容本質上都是數字(不管什麼檔案内容本質上都是數字,因為計算機中本身就隻有1和0),而文本檔案中的數字本身應該被了解為這個數字對應的ASCII碼。常見的.c檔案, .h檔案 .txt檔案等都是文本檔案。文本檔案的好處就是可以被人輕松讀懂和編寫。是以說文本檔案天生就是為人類發明的。
(2)二進制檔案。二進制檔案中存儲的本質上也是數字,隻不過這些數字并不是文字的編碼數字,而是就是真正的數字。常見的可執行程式檔案(gcc編譯生成的a.out,arm-linux-gcc編譯連接配接生成的.bin)都是二進制檔案。
(3)對比:從本質上來看(就是刨除檔案屬性和内容的了解)文本檔案和二進制檔案并沒有任何差別。都是一個檔案裡面存放了數字。差別是了解方式不同,如果把這些數字就當作數字處理則就是二進制檔案,如果把這些數字按照某種編碼格式去解碼成文本字元,則就是文本檔案。
(4)我們如何知道一個檔案是檔案檔案還是二進制檔案?在linux系統層面是不區分這兩個的(譬如之前學過的open、read、write等方法操作檔案檔案和二進制檔案時一點差別都沒有),是以我們無法從檔案本身準确知道檔案屬于哪種,我們隻能本來就知道這個檔案的類型然後用這種類型的用法去用他。有時候會用一些字尾名來人為的标記檔案的類型。
(5)使用文本檔案時,正常用法就是用文本檔案編輯器去打開它、編輯它。常見的文本檔案編輯器如vim、gedit、notepad++、SourceInsight等,我們用這些文本檔案編輯器去打開檔案的時候,編輯器會read讀出檔案二進制數字内容,然後按照編碼格式去解碼将其還原成文字展現給我們。如果用文本檔案編輯器去打開一個二進制檔案會如何?這時候編輯器就以為這個二進制檔案還是文本檔案然後試圖去将其解碼成文字,但是解碼過程很多數字并不對應有意義的文字是以成了亂碼。
(6)反過來用二進制閱讀工具去讀取文本檔案會怎麼樣?得出的就是文本文字所對應的二進制的編碼。
3.2.1.2、目錄檔案(d directory) 相當于一個門
(1)目錄就是檔案夾,檔案夾在linux中也是一種檔案,不過是特殊檔案。用vi打開一個檔案夾就能看到,檔案夾其實也是一種特殊檔案,裡面存的内容包括這個檔案的路徑,還有檔案夾裡面的檔案清單。
(2)但是檔案夾這種檔案比較特殊,本身并不适合用普通的方式來讀寫。linux中是使用特殊的一些API來專門讀寫檔案夾的。
vi 3.1.11
3.2.1.3、字元裝置檔案(c character)
3.2.1.4、塊裝置檔案(b block)
(1)裝置檔案對應的是硬體裝置,也就是說這個檔案雖然在檔案系統中存在,但是并不是真正存在于硬碟上的一個檔案,而是檔案系統虛拟制造出來的(叫虛拟檔案系統,如/dev /sys /proc等)
(2)虛拟檔案系統中的檔案大多數不能或者說不用直接讀寫的,而是用一些特殊的API産生或者使用的,具體在驅動階段會詳解。
3.2.1.5、管道檔案(p pipe)
3.2.1.6、套接字檔案(s socket)
3.2.1.7、符号連結檔案(l link)
3.2.2.常用檔案屬性擷取
3.2.2.1、stat、fstat、lstat函數簡介
(1)每個檔案中都附帶了這個檔案的一些屬性(屬性資訊是存在于檔案本身中的,但是它不像檔案的内容一樣可以被vi打開看到,屬性資訊隻能被專用的API打開看到)
指令stat a.out
(2)檔案屬性資訊檢視的API有三個:stat、fstat、lstat,三個作用一樣,參數不同,細節略有不同。
(3)linux指令行下還可以去用stat指令去檢視檔案屬性資訊,實際上stat指令内部就是使用stat系統調用來實作的。
(4)stat這個API的作用就是讓核心将我們要查找屬性的檔案的屬性資訊結構體的值放入我們傳遞給stat函數的buf中,當stat這個API調用從核心傳回的時候buf中就被填充了檔案的正确的屬性資訊,然後我們通過檢視buf這種結構體變量的元素就可以得知這個檔案的各種屬性了。
(5)fstat和stat的差別是:
stat是從檔案名出發得到檔案屬性資訊結構體,
fstat是從一個已經打開的檔案fd出發得到一個檔案的屬性資訊。
是以用的時候如果檔案沒有打開(我們并不想打開檔案操作而隻是希望得到檔案屬性)那就用stat,如果檔案已經被打開了然後要屬性那就用fstat效率會更高(stat是從磁盤去讀取檔案的,而fstat是從記憶體讀取動态檔案的)。
(6)lstat和stat/fstat的差别在于:對于符号連結檔案,stat和fstat查閱的是符号連結檔案指向的檔案的屬性,而lstat查閱的是符号連結檔案本身的屬性。
3.2.2.2、struct stat結構體簡介
(1)struct stat是核心定義的一個結構體,在<sys/stat.h>中聲明,是以我們可以用。這個結構體中的所有元素加起來就是我們的檔案屬性資訊。
struct stat { dev_t st_dev; /* ID of device containing file -檔案所在裝置(硬碟,或者其他)的ID*/ ino_t st_ino; /* inode number -inode節點号*/ mode_t st_mode; /* protection -檔案對應的模式,檔案,目錄等*/ nlink_t st_nlink; /* number of hard links -鍊向此檔案的連接配接數(硬連接配接)*/ uid_t st_uid; /* user ID of owner -user id-檔案所有者*/ gid_t st_gid; /* group ID of owner - group id-檔案所有者的組号*/ dev_t st_rdev; /* device ID (if special file) -裝置号,針對裝置檔案*/ off_t st_size; /* total size, in bytes -檔案大小,位元組為機關*/ blksize_t st_blksize; /* blocksize for filesystem I/O -系統緩沖塊的大小*/ blkcnt_t st_blocks; /* number of blocks allocated -檔案實際所占塊數,即位元組數*/ time_t st_atime; /* time of last access -最近存取時間*/ time_t st_mtime; /* time of last modification -最近修改内容時間*/ time_t st_ctime; /* time of last status change - 最近修改屬性時間*/ };
3.2.2.3、寫個程式來檢視一些常見屬性資訊
用法執行個體:
ret = stat(NAME, &buf);
printf("file owner: %u.\n", buf.st_mode);
3.2.3.解析 st_mode
3.2.3.1、用代碼判斷檔案類型
(1)檔案類型就是-、d、l····
(2)檔案屬性中的檔案類型标志在struct stat結構體的mode_t st_mode元素中,這個元素其實是一個按位來定義的一個位标志(有點類似于ARM CPU的CPSR寄存器的模式位定義)。這個東西有很多個标志位共同構成,記錄了很多資訊,如果要查找時按位&操作就知道結果了,但是因為這些位定義不容易記住,是以linux系統給大家事先定義好了很多宏來進行相應操作。
(3)譬如S_ISREG宏傳回值是
1表示這個檔案是一個普通檔案,
0表示不是普通檔案則傳回值是.
S_ISREG(st_mode) 是否為一般檔案?
S_ISDIR(st_mode) 是否為目錄?
S_ISCHR(st_mode) 是否為字元裝置檔案?
S_ISBLK(st_mode) 是否為塊裝置檔案?
S_ISFIFO(st_mode) FIFO檔案 (named pipe)?
S_ISLNK(st_mode) 連結檔案? (Not in POSIX.1-1996.)
S_ISSOCK(st_mode) socket檔案? (Not in POSIX.1-1996.)
用這樣的方式來使用
結果:
3.2.3.2、用代碼判斷檔案權限設定
(1)st_mode中除了記錄了檔案類型之外,還記錄了一個重要資訊:檔案權限。
(2)linux并沒有給檔案權限測試提供宏操作,而隻是提供了位掩碼,是以我們隻能用位掩碼來自己判斷是否具有相應權限。
用這樣的方式來使用
例如:unsigned int result = (buf.st_mode & S_IRUSR) >> 8
S_IFMT 0170000 檔案類型的位遮罩 S_IFSOCK 0140000 scoket S_IFLNK 0120000 符号連接配接 S_IFREG 0100000 一般檔案 S_IFBLK 0060000 區塊裝置 S_IFDIR 0040000 目錄 S_IFCHR 0020000 字元裝置 S_IFIFO 0010000 先進先出 S_ISUID 04000 檔案的 (set user-id on execution)位 S_ISGID 02000 檔案的 (set group-id on execution)位 S_ISVTX 01000 檔案的sticky 位
S_IRWXU 00700 檔案所有者權限的 掩碼
(用這個掩碼會得到一個8進制的數字,用來表示權限) S_IRUSR 00400 檔案所有者具可讀取權限 這些不能當作掩碼 S_IWUSR 00200 檔案所有者具可寫入權限 S_IXUSR 00100 檔案所有者具可執行權限
S_IRWXG 00070 檔案組權限的 掩碼
(用這個掩碼會得到一個8進制的數字,用來表示權限) S_IRGRP 00040 使用者組具可讀取權限 S_IWGRP 00020 使用者組具可寫入權限 S_IXGRP 00010 使用者組具可執行權限
S_IRWXO 00007 其他使用者權限的 掩碼
(用這個掩碼會得到一個8進制的數字,用來表示權限)
S_IROTH 00004 其他使用者具可寫讀權限
S_IWOTH 00002 其他使用者具可寫入權限 S_IXOTH 00001 其他使用者具可執行權限
傳回的錯誤代碼:
1、ENOENT 參數file_name 指定的檔案不存在
2、ENOTDIR 路徑中的目錄存在但卻非真正的目錄
3、ELOOP 欲打開的檔案有過多符号連接配接問題, 上限為16 符号連接配接
4、EFAULT 參數buf 為無效指針, 指向無法存在的記憶體空間
5、EACCESS 存取檔案時被拒絕
6、ENOMEM 核心記憶體不足
7、ENAMETOOLONG 參數file_name 的路徑名稱太長
3.2.4.檔案權限管理1
我們在操作檔案時要判斷檔案對目前使用者的權限,
3.2.4.1、st_mode中記錄的檔案權限位
(1)st_mode本質上是一個32位的數(類型就是unsinged int),這個數裡的每一個位表示一個含義。
(2)檔案類型和檔案的權限都記錄在st_mode中。我們用的時候使用專門的掩碼去取出相應的位即可得知相應的資訊。
3.2.4.2、ls -l列印出的權限清單
(1)123456789一共9位,3個一組。第一組三個表示檔案的屬主(owner、user)對該檔案的可讀、可寫、可執行權限;第2組3個位表示檔案的屬主所在的組(group)對該檔案的權限;第3組3個位表示其他使用者(others)對該檔案的權限。
(2)屬主就是這個檔案屬于誰,一般來說檔案建立時屬主就是建立這個檔案的那個使用者。但是我們一個檔案建立之後還可以用chown指令去修改一個檔案的屬主,還可以用chgrp指令去修改一個檔案所在的組。
3.2.4.3、檔案操作時的權限檢查規則
(1)一個程式a.out被執行,a.out中試圖去操作一個檔案1.txt,這時候如何判定a.out是否具有對1.txt的某種操作權限呢?
(2)判定方法是:首先1.txt具有9個權限位,規定了3種人(user、group、others)對該檔案的操作權限。是以我們判定1.txt是否能被a.out來操作,關鍵先搞清楚a.out對1.txt到底算哪種人。準确的說是看a.out被誰執行,也就是目前程式(程序)是哪個使用者的程序。
(3)剛才上面說的是我的分析,到底對不對還得驗證。
3.2.5.檔案權限管理2
3.2.5.1、access函數檢查權限設定
(1)文本權限管控其實蠻複雜,一般很難很容易的确定對一個檔案是否具有某種權限。設計優秀的軟體應該是:在操作某個檔案之前先判斷目前是否有權限做這個操作,如果有再做如果沒有則提供錯誤資訊給使用者。
(2)access函數可以測試得到目前執行程式的那個使用者在目前那個環境下對目标檔案是否具有某種操作權限。
int access( const char *pathname, int mode);
mode:
R_OK(讀) W_OK(可寫) X_OK(可執行) F_OK(檔案是否存在)
3.2.5.2、chmod/fchmod與權限修改
(1)chmod是一個linux指令,用來修改檔案的各種權限屬性。chmod指令隻有root使用者才有權利去執行修改。
(2)chmod指令其實内部是用linux的一個叫chmod的API實作的。
int chmod( const char *path, mode_t mode);
int fchmod( int fd, mode_t mode);
用法執行個體:
ret = chmod("1.txt", S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IWOTH);
3.2.5.3、chown/fchown/lchown與屬主修改
(1)linux中有個chown指令來修改檔案屬主
(2)chown指令是用chown API實作的
int chown( const char *path, uid_t owner, gid_t group);
int fchown( int fd, uid_t owner, gid_t group);
int lchown( const char *path, uid_t owner, gid_t group);
用stat可以查找ID号
3.2.5.4、umask與檔案權限掩碼 umask時權限的的取反的結果
(1)檔案掩碼是linux系統中維護的一個全局設定,umask的作用是用來設定我們系統中新建立的檔案的預設權限的。
(2)umask指令就是用umask API實作的
umask預設為0022
改變umask後,建立6.txt
umask()函數的用法:
用法執行個體:
umask(S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH);
3.2.5.5建立檔案函數creat()
int creat(const char *pathname, mode_t mode);
S_IRWXU 00700 user (file owner) has read, write and execute permission
S_IRUSR 00400 user has read permission
S_IWUSR 00200 user has write permission
S_IXUSR 00100 user has execute permission
S_IRWXG 00070 group has read, write and execute permission
S_IRGRP 00040 group has read permission
S_IWGRP 00020 group has write permission
S_IXGRP 00010 group has execute permission
S_IRWXO 00007 others have read, write and execute permission
S_IROTH 00004 others have read permission
S_IWOTH 00002 others have write permission
S_IXOTH 00001 others have execute permission
用法執行個體:
creat("foo", S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH );
3.2.6.讀取目錄檔案
3.2.6.1、opendir與readdir函數
(1)opendir打開一個目錄後得到一個DIR類型的指針給readdir使用
(2)readdir函數調用一次就會傳回一個struct dirent類型的指針,這個指針指向一個結構體變量,這個結構體變量裡面記錄了一個目錄項(所謂目錄項就是目錄中的一個子檔案)。
(3)readdir調用一次隻能讀出一個目錄項,要想讀出目錄中所有的目錄項必須多次調用readdir函數。readdir函數内部戶記住哪個目錄項已經被讀過了哪個還沒讀,是以多次調用後不會重複傳回已經傳回過的目錄項。當readdir函數傳回NULL時就表示目錄中所有的目錄項已經讀完了。
3.2.6.2、dirent結構體
3.2.6.3、讀取目錄實戰演練
DIR *opendir( const char *name);
DIR *fdopendir( int fd);
struct dirent *readdir( DIR *dirp);
int readdir_r( DIR *dirp, struct dirent *entry, struct dirent **result);
檔案目錄結構體:
struct dirent {
ino_t d_ino; /* inode number 索引節點号*/
off_t d_off; /* not an offset; see NOTES 在目錄檔案中的偏移*/
unsigned short d_reclen; /* length of this record 檔案名長*/
unsigned char d_type; /* type of file; not supported by all filesystem types 檔案類型*/
char d_name[256]; /* filename 檔案名,最長255字元*/
};
d_type的值為:
DT_BLK This is a block device.
DT_CHR This is a character device.
DT_DIR This is a directory.
DT_FIFO This is a named pipe (FIFO).
DT_LNK This is a symbolic link.
DT_REG This is a regular file.
DT_SOCK This is a UNIX domain socket.
DT_UNKNOWN The file type is unknown.
3.2.6.4、可重入函數介紹
(1)有些函數是可重入的有些是不可重入的,具體概念可以去百度。
(2)readdir函數和我們前面接觸的一些函數是不同的,首先readdir函數直接傳回了一個結構體變量指針,因為readdir内部申請了記憶體并且給我們傳回了位址。多次調用readdir其實readir内部并不會重複申請記憶體而是使用第一次調用readdir時配置設定的那個記憶體。這個設計方法是readdir不可重入的關鍵。
(3)readdir在多次調用時是有關聯的,這個關聯也标明readdir函數是不可重入的。
(4)庫函數中有一些函數當年剛開始提供時都是不可重入的,後來意識到這種方式不安全,是以重新封裝了C庫,提供了對應的可重複版本(一般是不可重入版本函數名_r)