1. linux中各種文件類型
1.1 普通文件(-)regular file
文本文件:文件中的內容都是由文本構成的,文本指的是ASCII碼,Unicode碼,utf8碼等字符。其實,這些字符碼的本質是數字(無論什麼編碼格式,對應的都是二級制的0和1)。
常見的.c文件.h文件.txt文件等都是文本文件,其好處是可以被人輕鬆的編寫和理解。
二進制文件:二進制文件中存儲的也是二進制的0和1,只不過這些數字並不是符合一定編碼規則的數字,而是純粹的數字0和1。
gcc編譯生成的a.out文件和arm-linux-gcc編譯生成的.bin文件都是二進制文件;
如果區分文本文件和二進制文件呢?在linux系統中,是無法區分的;例如open,read,write等操作文本文件和二進制文件是沒有任何區別的。只能通過文件的後綴來區別。
常見的文本編輯器:vi,vim,gedit,notepad++,sourceinsight等這些都屬於文本編輯器;當我們使用這些編輯器打開文本文件的時候,編輯器會read出文本文件的二進制內容,然後按照編碼格式去解碼,將其還原成文本(字符)展現給我們。
但如果用文本文件編輯器打開二進制文件時,看到的結果是亂碼!(或者使用不符合編碼解碼規則的文件時,打開也是亂碼)。
反過來使用二進制閱讀工具讀取文本文件時,得到的就是文本文件中編碼字符所對應的二進制數字。
1.2 目錄文件(d)direct
目錄就是文件夾,文件夾是一種頁數的文件!
使用vi打開一個文件夾,就可以看到如下信息,裏面包含文件路徑和文件列表;但是文件夾比較特殊,不適合用普通的方式(read,write)來讀寫操作;linux中使用特殊的API來專門讀寫文件夾。
1.3 字符設備文件(c)character
1.4 塊設備文件(b)block
設備文件對應的是“設備”,設備文件雖然存在與linux系統中,但是設備文件並不存在於硬盤上,而是linux文件系統虛擬製造出來的(叫做虛擬文件系統,如/dev /sys /proc)。
虛擬文件中的大多數不能直接讀寫,而是用一些特殊的API使用,詳見linux驅動編寫部分。
1.5 管道文件(p)pipe
使用管道作爲通信方式時,會使用的到管道文件。
1.6 套接字文件(s)socket
linux網絡的基礎
1.7 符號連接文件(l)link
符號連接文件是一種快捷方式!!!
ln-s 軟連接?軟連接相當於是一個快捷方式,因此其inode與源文件不同;
ln硬連接?硬連接的inode與源文相同;
2. 常用文件屬性獲取
2.1 stat,fstat,lstat函數簡介
0777就是rwx-rwx-rwx
size文件大小;Blocks佔用了多少塊;
IOBlocks該文件在被讀寫時,其內部的塊的大小(linux系統操作緩衝區向硬盤寫入數據的單位大小);
regular file:該文件屬於普通文件;
Device:該文件所存放在的設備;(類似設備名,扇區名)
Inode:靜態文件存儲在硬盤裏時的編號;一個文件分配一個Inode,linux系統中有多少文件就有多少個Inod;
Links:該文件的硬連接數;
Access:該文件的可訪問性;可讀可寫可執行;
Uid:用戶id;root組
Gid:文件分爲哪一組;root組
Access:最後訪問時間,打開,讀,寫;
Modify:文件內容最後被修改的時間;
Change:文件屬性最後被修改的時間。
文件屬性信息查看的API有三個,stat,fstat,lstat,三個作用一樣,參數不同,細節略有不同;
man 1 普通文件; man 2 系統調用 ; man 3 庫函數;
linux命令行下可以使用stat命令去查看文件的屬性信息,實際上stat命令內部就是使用stat系統調用來實現的;
stat這個API的作用就是讓內核將文件的屬性信息結構體的值放入我們傳遞給stat函數的buf中,當stat這個API調用從內核返回的時候buf中就被填充了文件的正確的屬性信息,然後我們通過查看buf這種結構體變量的元素的值就可以得知文件的屬性。
向內核傳遞一個空白的結構體,該結構體從內核中帶出來的信息,就是我們想要的信息!!!
SYNOPSIS
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
int stat(const char *pathname, struct stat *buf);//以文件名爲入口
int fstat(int fd, struct stat *buf);//以文件操作符爲入口
int lstat(const char *pathname, struct stat *buf);//以文件名爲入口
fstat是從一個已經打開的文件的fd得到文件屬性信息,fstat是從內存中讀取動態文件的屬性信息的,因此效率更高;
stat是從文件名獲得文件信息,適用於未被打開的文件,stat是從磁盤獲取文件屬性信息的;
lstat與“fstat和stat”的區別:對於符號連接文件,lstat查閱的是符號連接文件本身的文件屬性信息(快捷方式文件自身的文件屬性信息),而stat和fstat查閱的是符號鏈接文件指向的源文件的文件屬性信息!
2.2 struct stat結構體簡介
struct stat是llinux系統內核定義的一個結構體,在<sys/stat.h>中聲明,這個結構體中的所有元素加起來就是我們的文件屬性信息。
struct stat {
dev_t st_dev; /* ID of device containing file */
ino_t st_ino; /* inode number */
mode_t st_mode; /* protection */文件類型,位標誌!!!
nlink_t st_nlink; /* number of hard links */
uid_t st_uid; /* user ID of owner */
gid_t st_gid; /* group ID of owner */
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 512B blocks allocated */
/* Since Linux 2.6, the kernel supports nanosecond
precision for the following timestamp fields.
For the details before Linux 2.6, see NOTES. */
struct timespec st_atim; /* time of last access */
struct timespec st_mtim; /* time of last modification */
struct timespec st_ctim; /* time of last status change */
#define st_atime st_atim.tv_sec /* Backward compatibility */
#define st_mtime st_mtim.tv_sec
#define st_ctime st_ctim.tv_sec
};
寫個程序查看文件信息:
3. stat函數的應用案例
3.1 使用代碼判斷文件類型
文件類型就是“普通文件,目錄文件,字符設備文件,塊設備文件,管道文件,套接字文件,符號連接文件”;
文件屬性中的文件類型標誌在struct stat的mode_t st_mode元素中,這個元素是一個按位來定義的一個位標誌,由很多的標誌位共同構成,記錄了很多信息,查找是按位&操作就知道結果了,但是由於這些位定義不容易記住,因此linux事先定義好了很多宏來進行操作。
Because tests of the above form are common, additional macros are defined by POSIX to allow the test of the file type in st_mode to be written more concisely:
S_ISREG(m) is it a regular file?//是否是普通文件
S_ISDIR(m) directory?//是否是目錄文件
S_ISCHR(m) character device?//是否是字符設備文件
S_ISBLK(m) block device?//是否是塊設備文件
S_ISFIFO(m) FIFO (named pipe)?
S_ISLNK(m) symbolic link? (Not in POSIX.1-1996.)
S_ISSOCK(m) socket? (Not in POSIX.1-1996.)
例如,S_ISREG宏返回值是1表示這個文件是一個普通文件,如果文件不是普通文件則返回值是0;
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>
#define NAME "1.txt"
int main()
{
int ret=-1;
struct stat buff;
memset(&buff,0,sizeof(buff));//memset後,buff中全部是0;
ret=stat(NAME,&buff);//stat後,buff中有內容了;
if(ret<0)
{
perror("stat");
exit(-1);
}
//判斷文件屬性
int result=S_ISREG(buff.st_mode);
printf("result is %d\n",result);
return 0;
}
3.2 利用代碼判斷文件權限設置
st_mode中除了記錄文件類型以外,還記錄了“文件權限”;
linux並沒有提供對文件權限測試的宏操作,而只提供了掩碼,所以我們只能使用位掩碼來判斷是否具有響應的權限;
The following mask values are defined for the file mode component of the st_mode field:
S_ISUID 04000 set-user-ID bit
S_ISGID 02000 set-group-ID bit (see below)
S_ISVTX 01000 sticky bit (see below)
//屬主的權限user
S_IRWXU 00700 owner has read, write, and execute permission
S_IRUSR 00400 owner has read permission
S_IWUSR 00200 owner has write permission
S_IXUSR 00100 owner has execute permission
//所在組的權限group
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
//other的權限
S_IRWXO 00007 others (not in group) have read, write, and exe‐
cute permission
S_IROTH 00004 others have read permission
S_IWOTH 00002 others have write permission
S_IXOTH 00001 others have execute permission
4. 文件權限管理1
4.1 st_mode中記錄的文件權限位
st_mode本質上是一個32位的數,類型是unsigned int,這個數裏的每一位表示一個含義;
文件類型和文件權限都記錄在st_mode中,我們使用的時候用專門的掩碼取出響應的位即可得到響應的信息。
4.2 ls -l打印出的權限列表
123-456-789,一共9位,3個一組。
第一組三個表示文件的屬主(owner或者user,簡寫u)對該文件的可讀、可寫、可執行權限;
第二組三個表示文件的屬主所在的組(group,簡寫g)對該文件的可讀、可寫、可執行權限;
第三組三個表示其他用戶(others,簡寫o)對該文件的可讀、可寫、可執行權限。
“屬主”就是這個文件屬於誰,一般來說文件創建時屬主就是創建這個文件的那個用戶;
在一個文件創建以後,還可以使用chown命令去修改這個文件的屬主,也可以使用chgrp命令去修改文件所在的組。
4.3 文件操作時的權限檢查規則
一個程序a.out被執行,a.out試圖去操作一個文件1.txt,這時如何判定a.out是否具有對1.txt的某種操作權限呢?
判定方法是:首先1.txt具有9個權限位,依次規定了3種人(user,group,others)對該文件的操作權限。所以我們判定1.txt能否被a.out來操作,首先搞清楚執行a.out的人是屬於哪個角色,即a.out被哪種角色的人執行了,也就是當前程序(進程)是哪個用戶的進程。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>
#define NAME "1.txt"
int main(viod)
{
int ret=open(NAME,O_RDONLY);
if(ret>0)
{
printf("可讀");
close(ret);
}
else
{
perror("read");
}
ret=open(NAME,O_WRONLY);
if(ret>0)
{
printf("可寫");
close(ret);
}
else
{
perror("write");
}
return 0;
}
5. 文件權限管理2
5.1 access函數檢查權限設置
文件的權限管理並不簡單,一般不容易確定對於以個文件是具有某種操作權限。設計優秀的軟件,在操作某個文件之前應該自動判斷是否具備對該文件的操作權限,如果有權限則執行,沒有則應明確提示用戶沒有操作權限。
access函數可以測試當前執行程序的用戶是否具有對目標文件的操作權限。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>
#define NAME "1.txt"
int main(void)
{
int ret = -1;
ret = access(NAME,F_OK);
if(ret<0)
{
printf("文件不存在!\n");
}
else
{
printf("文件存在OK\n");
}
ret = access(NAME,R_OK);
if(ret<0)
{
printf("文件不可讀!\n");
}
else
{
printf("文件可讀OK\n");
}
ret = access(NAME,W_OK);
if(ret<0)
{
printf("文件不可寫!\n");
}
else
{
printf("文件可寫OK\n");
}
return 0;
}
5.2 chmod/fchmod與權限修改
chmod是一個linux命令,用來修改文件的各種權限屬性。chmod命令只有root用戶纔有權利去執行修改。
chmod命令其實內部是用linux的一個叫chmod的API實現的。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>
#define NAME "1.txt"
int main(int argc , char **argv)
{
int ret = -1;
if(agrc != 2)
{
printf("usage : %s filename\n",argv[0]);
return -1;//不用包含頭文件?
}
//chmod成功返回0,失敗返回-1
ret=chmod(argv[1],S_IRUSR|S_IWUSR|S_IXUSR);//argv[1]就是filename!!!
if(ret<0)
{
perror("chmod");
return -1;
}
return 0;
}
5.3 chown/fchown/lchown與屬主修改
linux中使用chown命令來修改文件的屬主
chown命令是用chown API來實現的。
5.4 umask與文件權限掩碼
umask是linux系統中維護的一個全局設置,umask的作用是用來設定我們系統中新建文件的默認權限的。
6. 讀取目錄文件
6.1 opendir與readdir函數
opendir打開一個目錄後得到一個DIR類型的指針給readdir使用;
readdir調用一次就會返回一個struct dirent類型的指針,該指針指向一個結構體變量,這個結構體變量裏面記錄了一個目錄項(目錄中的一個子文件);
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 */
};
redir調用一次只能讀取一個目錄項,要想讀出目錄中所有的目錄項必須多次調用readdir函數。readdir函數內部記住了哪個目錄項已經被讀過了哪個還沒被讀過,所以多次調用後不會重複返回已經返回過的目錄項。當readdir函數返回NULL時就表示目錄中所有的目錄項已經讀完了。
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.
6.2 dirent結構體
6.3 讀取目錄實戰演練
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>
#include <dirent.h>
int main(int argc,int **argv)
{
DIR *pDir = NULL;
struct dirent *pEnt= NULL;
unsigned count = 0;
if(argc != 2)
{
printf("usage: %s dirname\n",argv[0]);
return -1;
}
pDir = opendir(argv[1]);
if(NULL == pDir)
{
perror("opendir");
}
while(1)
{
pEnt=readdir(pDir);
if(pEnt!=NULL)
{
//還有子文件,再次處理子文件
printf("name : [%s] \n",pEnt->d_name);
count++;
/*
if(pEnt->D_type == DT_REG)
{
printf("是普通文件哦\n");
}
else
{
printf("不是普通文件\n");
}
*/
}
else
{
break;
}
}
printf("總文件書是:%d\n",count);
return 0;
}
6.4 可重入函數介紹
有些函數是可重入的,有些是不可重入的;
readdir函數與之前的函數不同之處在於,readdir函數直接返回了一個結構體變量指針,因爲readdir內部申請了內存並且給我們返回了地址。多次調用readdir其實readdir內部並不會重複申請內存而是使用第一次調用readdir時分配的那個內存。這個設計方法設計readdir不可重入的關鍵,
readdir在多次調用時是有關聯的,這個關聯也表明readdir是不可重入的。
庫函數中有一些函數在設計之初是不可重入的,後來意識到這種方式是不安全的,所以重新封裝了C庫,提供了對應的可重入版本(一般是不可重入函數名_r):readdir(不可重入) readdir_r(可重入)。