Stat(系統調用)

stat
命令行STAT()是一個UNIX系統調用,它返回有關Inode的文件屬性。 STAT()的語義在操作系統之間有所不同。例如, Unix命令LS使用此系統調用來檢索包括:
- 當場:上次訪問時間( LS -LU )
- MTIME:最後修改時間( LS -L )
- CTIME:最後狀態更改的時間( LS -LC )
stat
出現在版本1 Unix中。它是少數原始的UNIX系統更改調用之一,版本4的組添加組權限和較大的文件大小。
stat()功能
在POSIX和其他類似Unix的操作系統上找到的C POSIX庫標題SYS/Stat.h聲明了stat()
函數,以及稱為fstat()
和lstat()
的相關功能。該功能採用struct stat
Buffer參數,該參數用於返回文件屬性。在成功時,函數返回零,並且在錯誤時,返回-1並適當設置errno 。
stat()
和lstat()
函數採用文件名參數。如果文件是符號鏈接, stat()
返回鏈接的最終目標的屬性,而lstat()
返回鏈接本身的屬性。 fstat()
函數改用文件描述符參數,並返回其標識的文件的屬性。
功能家族擴展到實施大量文件支持。在struct Stat64結構中名為stat64()
, lstat64()
和fstat64()
返回屬性的功能,該函數屬於struct stat64
結構,該屬性代表具有64位類型的文件大小,允許該函數在文件2 GIB和較大的文件上工作(最多8 EIB)。將_FILE_OFFSET_BITS
宏定義為64時,這些64位功能以原始名稱可用。
這些功能定義為:
int stat(const char *filename, struct stat *buf);
int lstat(const char *filename, struct stat *buf);
int fstat(int filedesc, struct stat *buf);
統計結構
該結構在SYS/STAT.H標頭文件中定義如下,儘管實現可以自由定義其他字段:
struct stat {
mode_t st_mode;
ino_t st_ino;
dev_t st_dev;
dev_t st_rdev;
nlink_t st_nlink;
uid_t st_uid;
gid_t st_gid;
off_t st_size;
struct timespec st_atim;
struct timespec st_mtim;
struct timespec st_ctim;
blksize_t st_blksize;
blkcnt_t st_blocks;
};
POSIX.1不需要st_rdev
, st_blocks
和st_blksize
成員;這些字段定義為單個Unix規範中XSI選項的一部分。
在POSIX.1標準的較舊版本中,將時間相關的字段定義為st_atime
, st_mtime
和st_ctime
,並且是類型time_t
。自2008年版本的標準版本以來,這些字段分別被重命名為type struct timespec
的st_atim
, st_mtim
和st_ctim
,因為該結構提供了更高的分辨率時間單元。為了兼容,實現可以根據tv_sec
的struct timespec
定義舊名稱。例如, st_atime
可以定義為st_atim.tv_sec
。
struct stat
結構至少包括以下成員:
-
st_dev
- 裝有文件的設備標識符 -
st_ino
- Inode編號 -
st_mode
- 保護模式;另請參閱UNIX權限 -
st_nlink
-硬鏈接的參考計數 -
st_uid
- 所有者的用戶標識符 -
st_gid
- 所有者的組標識符 -
st_rdev
- 設備標識符(如果特殊文件) -
st_size
- 總文件大小,字節 -
st_atime
- 上次訪問時間 -
st_mtime
- 最後修改的時間 -
st_ctime
- 最後狀態更改的時間 -
st_blksize
- 文件系統I/O的首選塊大小,這可以取決於系統和文件系統的類型 -
st_blocks
- 在DEV_BSIZE
的倍數中分配的塊數(通常為512個字節)。
st_mode
字段是位字段。它結合了文件訪問模式,還指示任何特殊的文件類型。有許多宏可以使用不同的模式標誌和文件類型。
批評當場
讀取文件更改其在需要磁盤寫入的水場,由於它與僅讀取文件系統不一致,因此受到批評。文件系統緩存可能會大大將此活動大大減少到每個緩存齊平寫入一個磁盤。
Linux內核開發人員IngoMolnár公開批評了2007年ATIME的概念和績效影響,2009年,相對安裝選項已成為默認值,這解決了這一批評。相對安裝選項背後的行為為大多數目的提供了足夠的性能,並且不應破壞任何重要的應用程序,因為已經進行了廣泛的討論。最初,僅在atime <mtime或atime <ctime時才更新相對性的空缺;隨後對此進行了修改,以更新24小時或以上的Atimes,以使TMPWatch和Debian的受歡迎程度計數器(Popcon)的行為正確。
Linux內核的當前版本支持四個掛載選項,可以在FSTAB中指定:
- 嚴格(以前是氣電場,以前是默認設備;截至2.6.30) - 始終更新Atime,它符合POSIX定義的行為
- pelatime (“相對氣站”,在2.6.20中引入和默認值為2.6.30) - 僅在某些情況下更新空缺:如果先前的電費率大於MTIME或CTIME,或者先前的排氣場均超過24小時。過去
- Nodiratime - 切勿更新目錄的潮流,但請更新其他文件
- noatime - 切勿更新任何文件或目錄的空缺;暗示nodiratime ;最高的性能,但兼容
- Lazytime - 根據以下特定情況進行更新
Linux , MacOS , Solaris , FreeBSD和NetBSD的當前版本支持/etc /fstab中的Noatime Mount選項,這會導致Atime字段永遠不會更新。關閉ATIME更新Breaks Posix合規性,以及某些應用程序,例如Mbox驅動的“新郵件”通知,以及某些文件使用情況觀看公用事業,尤其是TMPWatch。
OpenBSD上的Noatime選項的行為更像Linux相對。
Linux內核Mainline的4.0版,於2015年4月12日發布,推出了新的Mount選項lazytime 。它允許在內存中執行POSIX風格的ATIME更新,並與同一文件上的一些非時代相關的I/O操作一起沖洗到磁盤;當執行某些同步系統調用時,或者在文件的內存中被驅逐從文件系統緩存驅逐時,ATIME更新也將被沖入磁盤。此外,可以配置無需花費多長時間的空缺時間。這樣,Lazytime在提供性能改進的同時保留了POSIX兼容性。
ctime
很容易相信CTIME最初意味著創造時間。但是,儘管早期Unix確實有修改和創建時間,但後者已更改為訪問時間,然後才有任何C結構來調用任何CTIME 。文件系統僅保留訪問時間(當地時間)和修改時間( MTIME )至第6版UNIX。 ctime時間戳是在版本7 Unix發生的文件系統重組中添加的,並且始終引用Inode更改時間。它可以在Inode更改中存儲的任何時間文件元數據進行更新,例如文件權限,文件所有權以及硬鏈接的創建和刪除。在某些實現中, CTIME會受重命名文件的影響:兩者都是Original Unix,該文件通過建立鏈接(更新CTIME )而實現了重命名,然後將舊名稱鏈接(再次更新CTIME ),而Modern Linux則傾向於這樣做。
與ATIME和MTIME不同,例如, CTIME無法使用touch實用程序使用的UTIME()設置為任意值。取而代之的是,當使用utime()或用於訪問文件引起的氣電場更新以外的任何其他更改時, CTIME值將設置為當前時間。
時間粒度
- Time_T提供準確至一秒鐘的時間。
- 一些文件系統提供了更細的粒度。 Solaris 2.1在1992年引入了UFS的微秒分辨率,並用ZFS進行了納秒分辨率。
- 在Linux內核2.5.48及更高版本中,統計結構支持三個文件時間戳字段的納秒分辨率。這些被暴露為統計結構中的其他字段。
- 在脂肪文件系統上創建時間的分辨率為10毫秒,而其寫入時間的分辨率為2秒,並且訪問時間的分辨率為一天,因此作為訪問日期。
例子
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <sys/types.h>
#include <pwd.h>
#include <grp.h>
#include <sys/stat.h>
int
main(int argc, char *argv[])
{
struct stat sb;
struct passwd *pwuser;
struct group *grpnam;
if (argc < 2)
{
fprintf(stderr, "Usage: %s: file ...\n", argv[0]);
exit(EXIT_FAILURE);
}
for (int i = 1; i < argc; i++)
{
if (-1 == stat(argv[i], &sb))
{
perror("stat()");
exit(EXIT_FAILURE);
}
if (NULL == (pwuser = getpwuid(sb.st_uid)))
{
perror("getpwuid()");
exit(EXIT_FAILURE);
}
if (NULL == (grpnam = getgrgid(sb.st_gid)))
{
perror("getgrgid()");
exit(EXIT_FAILURE);
}
printf("%s:\n", argv[i]);
printf("\tinode: %u\n", sb.st_ino);
printf("\towner: %u (%s)\n", sb.st_uid, pwuser->pw_name);
printf("\tgroup: %u (%s)\n", sb.st_gid, grpnam->gr_name);
printf("\tperms: %o\n", sb.st_mode & (S_IRWXU | S_IRWXG | S_IRWXO));
printf("\tlinks: %d\n", sb.st_nlink);
printf("\tsize: %ld\n", sb.st_size); /* you may use %lld */
printf("\tatime: %s", ctime(&sb.st_atim.tv_sec));
printf("\tmtime: %s", ctime(&sb.st_mtim.tv_sec));
printf("\tctime: %s", ctime(&sb.st_ctim.tv_sec));
printf("\n");
}
return 0;
}