[單刷APUE系列]第四章——文件和目錄[2]

原文來自靜雅齋,轉載請註明出處。javascript

PS:原先寫好文章了,結果備份Boom了,結果只好重寫了一遍,文件系統部分原本是準備了圖片的,可是如今沒了。各位將就着看。java

黏着位(S_ISVTX)

The ISVTX (the sticky bit) indicates to the system which executable files are shareable (the default) and the system maintains the program text of the files in the swap area. The sticky bit may only be set by the super user on shareable executable files.

If mode ISVTX (the `sticky bit') is set on a directory, an unprivileged user may not delete or rename files of other users in that directory. The sticky bit may be set by any user on a directory which the user owns or has appropriate permissions. For more details of the properties of the sticky bit, see sticky(8).複製代碼

黏着位是一個頗有意思的參數,S_ISVTX其實是save text bit的縮寫,咱們知道,一個二進制程序其實是由如下幾部分組成node

  1. 正文段
  2. 數據段
  3. bss段

正文段包含了程序幾乎大部分的內容,並且因爲正文段的特殊性,它是隻讀的,那麼在早期Unix環境資源很是稀少的狀況下,爲了保證快速加載程序,就出現了保存正文位這一個特殊bit,若是一個程序的stx位被設置了,當程序第一次載入執行,在其終止時,程序正文段依舊會有一個副本被保存在交換區。在如今看來其實是一種緩存技術,Node.JS的模塊加載機制就與此相似。可是因爲如今的Unix系統虛擬內存技術、預存取技術的存在,這個機制已經被廢棄了。
正如上面Unix系統手冊上所說,當黏着位設置在目錄上的時候,沒有特權的用戶就不能刪除或者重命名其餘用戶在這個目錄中的文件。很典型的案例就是/tmp/var/tmp目錄,tmp文件夾通常都是rwxrwxrwt的權限,由root用戶擁有,根據規定,只有擁有文件、擁有目錄、超級用戶的三者之一才能刪除或改名文件,這樣就能保證了不會對其餘用戶的文件誤操做。
man 8 sticky的手冊頁中咱們也能看到關於黏着位的描述shell

The sticky bit has no effect on executable files. All optimization on whether text images remain resident in memory is handled by the kernel's virtual memory system.複製代碼

黏着位已經對二進制文件沒有任何做用了,虛擬內存機制取代了一切。數組

更改擁有者函數族

int chown(const char *path, uid_t owner, gid_t group);
int fchown(int fildes, uid_t owner, gid_t group);
int lchown(const char *path, uid_t owner, gid_t group);
int fchownat(int fd, const char *path, uid_t owner, gid_t group, int flag);

DESCRIPTION
The owner ID and group ID of the file named by path or referenced by fildes is changed as specified by the arguments owner and group.  The owner of a file may change the group to a group of which he or she is a member, but the change owner capability is restricted to the super-user.

The chown() system call clears the set-user-id and set-group-id bits on the file to prevent accidental or mischievous creation of set-user-id and set-group-id programs if not executed by the super-user.  The chown() system call follows symbolic links to operate on the target of the link rather than the link
itself.

The fchown() system call is particularly useful when used in conjunction with the file locking primitives (see flock(2)).

The lchown() system call is similar to chown() but does not follow symbolic links.

The fchownat() system call is equivalent to the chown() and lchown() except in the case where path specifies a relative path.  In this case the file to be changed is determined relative to the directory associated with the file descriptor fd instead of the current working directory.

Values for flag are constructed by a bitwise-inclusive OR of flags from the following list, defined in <fcntl.h>:

AT_SYMLINK_NOFOLLOW
If path names a symbolic link, ownership of the symbolic link is changed.

If fchownat() is passed the special value AT_FDCWD in the fd parameter, the current working directory is used and the behavior is identical to a call to chown() or lchown() respectively, depending on whether or not the AT_SYMLINK_NOFOLLOW bit is set in the flag argument.

One of the owner or group id's may be left unchanged by specifying it as -1.複製代碼

上面是Mac OS X系統用戶手冊關於chown函數的介紹,從它和原著內容的對照,能夠發現,實際上原著上面提到的內容,均可以從系統手冊上找到,筆者我的很是推崇Unix自帶的系統手冊,除了方便快捷,內容也很是的詳細,能夠說,原著就是講系統手冊內容組合起來,而後加上了做者本身的理解。
這個函數族也很是的簡單,除了fchownat函數多了一個flag參數之外,其餘的參數都是同樣的,從手冊最後一句能夠知道,ownergroup兩個參數任意一個爲-1,就表明對應的ID不變。當文件是符號連接的時候,lchownflag參數爲AT_SYMLINK_NOFOLLOW的fchownat是同樣的行爲,都是符號連接自己的全部者。
從手冊中瞭解到,文件擁有者能夠將組改成擁有者的其餘附屬組,可是隻有root用戶才能改變文件擁有者,原著中提到,基於BSD的系統一直規定只有超級用戶才能更改文件全部者,Mac OS X系統也是BSD系的,因此有這規定不足爲奇,而原著後面也提到了這個限制在FreeBSD8.0、Linux3.2.0和Mac OS X 10.6.8中一直存在,但是筆者在基於Linux2.6系列內核的CentOS6.x中發現,CentOS6.x實際上也有此限制,而且在用戶手冊上寫到緩存

Only a privileged process (Linux: one with the CAP_CHOWN capability) may change the owner of a file.  The owner of a file may change the group of the file to any group of which that owner is a member.  A privileged process (Linux: with CAP_CHOWN) may change the group arbitrarily.複製代碼

這點很是讓人疑惑,因爲筆者並無測試其餘2.6.x內核的Linux發行版,因此不知道這個限制是Linux2.6就有的,仍是說是RedHat公司打的補丁。若是有朋友知道能夠指正一下。
chown系統調用還會清除設置用戶ID和設置組ID位,若是函數不是被root權限調用。cookie

文件長度

stat結構體中有一個成員變量st_size用於表示以字節爲單位的文件長度,咱們知道,Unix有7種文件數據結構

  1. 普通文件
  2. 目錄文件
  3. 塊特殊文件
  4. 字符特殊文件
  5. FIFO
  6. 套接字
  7. 符號連接

很容易就能想到這個字段只對普通文件、目錄文件和符號連接纔有意義,可是請注意,因爲FIFO的特殊性,文件長度的存在是能讓進程通訊更加便利,因此在在現代Unix系統中,FIFO也有這一屬性。
你們應該還記得stat結構體中的st_blksizest_blocks屬性,這兩個屬性就是關於文件在文件系統中佔據空間的屬性,第一個是最優文件系統塊大小,第二個是分配給該文件的磁盤塊數。
在前面的例程中,提到了普通文件存在文件空洞,也解釋了文件空洞存在的緣由,當文件長度小於所佔用的磁盤塊大小,就說明存在着文件空洞。當咱們使用cat命令複製存在空洞的文件,新的文件就不存在空洞了,全部的空洞都會被填滿。更有意思的是,使用od命令去查看這兩個文件的二進制內容,能夠發現空洞和新文件被填滿的部分都是以null的形式(0字節)存在,app

文件截斷

在不少時候,開發者須要將文件截斷一部分用於縮短文件。一個很典型的案例就是將文件截斷爲0,也就是使用O_TRUNC參數打開文件,可是系統也提供了兩個函數用於文件的截斷less

int truncate(const char *path, off_t length);
int ftruncate(int fildes, off_t length);複製代碼

這兩個函數能夠將文件截斷也能夠擴展,若是length大於文件長度,那麼就會擴展當前的文件,而且擴展的空間將以文件空洞的形式存在。
並且注意,這兩個函數不會修改當前文件的偏移量,若是不注意這一點,頗有可能形成偏移量超出了新的文件長度,從而造成空洞。

文件系統

文件系統是一個很是龐大的模型,筆者在這裏講本身的理解,若是有須要更深一步瞭解的朋友,能夠自行觀看原著或者《鳥哥的Linux私房菜》中有關文件系統的部分。
目前Unix系統幾乎都有不少本身的文件系統實現,每種文件系統都有其本身的特殊性,可是對於大部分文件系統來講,都是基於同一個模型創建的,因此實際上很類似。
文件系統有不少部分組成,廣泛的實現中,都是將實際文件數據和文件屬性分開,分別存放。這就是數據塊和索引塊,一般也叫inode塊,還有包含了整個文件系統屬性的超級塊,爲了方便管理,一些文件系統還將塊進行分組,造成塊組,可是這個概念實際上可有可無。對於開發者來講重要的是索引塊和數據塊。對於用戶來講,使用ls命令查看當前目錄下的全部文件,一般能夠看到以下內容

drwxr-xr-x   9 uid  gid   306  2  3 18:14 .
drwxr-xr-x  11 uid  gid   374  2  3 18:12 ..
-rw-r--r--   1 uid  gid  2290  2  3 18:13 error.c
-rw-r--r--   1 uid  gid  2229  2  3 18:13 errorlog.c
drwxr-xr-x   4 uid  gid   136  2  3 18:12 include複製代碼

有文件有目錄,還有兩個...特殊文件,實際上ls命令並無檢索真正的數據塊,這些項目就是索引塊體現的,每一個文件都對應了一個獨一無二的索引塊,可是並非每一個文件都有獨一無二的數據塊。索引塊中記錄了文件的基本信息,包括權限、大小、類型等等,上面第二列是項目的連接計數,也就是stat結構體中的st_nlink成員。咱們知道,Unix的連接分爲兩種,硬連接和軟連接(符號連接),每一個inode塊都有連接計數,只有當連接計數爲0的時候,文件系統才真正的刪除數據塊,這一點和C++中的引用計數很是類似。
軟連接其實是一種文件,它存在着本身的索引塊和數據塊,並且軟連接的存在不會讓索引計數改變,只是數據塊內容是另外一個文件的位置,這點和C語言中的指針很是類似。
當使用mkdir建立目錄的時候,能夠發現新目錄的引用必然是2,由於目錄下默認就有兩個特殊文件...,目錄自己指向自身,.文件也指向自身,因此新目錄索引計數必然爲2.
從上面的信息能夠知道,因爲硬連接是文件系統inode塊產生的,那麼硬連接就必然沒法跨越文件系統,而軟連接是實實在在的文件,因此軟連接能夠跨文件系統。而且,這就是爲何系統提供的是unlink函數而不是delete函數的緣由。

連接函數族

int link(const char *path1, const char *path2);
int linkat(int fd1, const char *name1, int fd2, const char *name2, int flag);

int unlink(const char *path);
int unlinkat(int fd, const char *path, int flag);複製代碼

系統提供了上面四個函數用於連接的建立和釋放,具體的介紹則在上一節講述了。通過了這麼多函數的介紹,想必你們也對Unix函數的風格有所瞭解了。和其餘的Unix函數同樣,文件描述符能夠用AT_FDCWD來代替,這表明着路徑將以當前工做目錄來計算。
linkat函數的flag參數只有AT_SYMLINK_FOLLOW可用,這和以前咱們所遇到的函數都不大同樣,當這個參數存在時,函數將建立指向連接目標的連接,不然建立指向符號連接自己的連接。
unlinkat的flag則只有AT_REMOVEDIR可用,當此參數被傳入時,unlinkat將和rmdir同樣刪除目錄。

#include "include/apue.h"
#include <fcntl.h>

int main(int argc, char *argv[])
{
    if (open("tempfile", O_RDWR) < 0)
        err_sys("open error");
    if (unlink("tempfile") < 0)
        err_sys("unlink error");
    printf("file unlinked\n");
    sleep(15);
    printf("done\n");
    exit(0);
}複製代碼

The unlink() function removes the link......and no process has the file open這是Unix手冊中的一句話,當文件被進程打開時,unlink函數不會刪除連接,因此這個特性在實際開發中十分實用,能夠用來保證進程崩潰時,刪除臨時文件。
並且在ISO C標準函數庫中,有一個remove函數,一樣也是用於解除連接。

重命名函數

int rename(const char *old, const char *new);
int renameat(int fromfd, const char *from, int tofd, const char *to);

DESCRIPTION
The rename() system call causes the link named old to be renamed as new.  If new exists, it is first removed.  Both old and new must be of the same type(that is, both must be either directories or non-directories) and must reside on the same file system.

The rename() system call guarantees that an instance of new will always exist, even if the system should crash in the middle of the operation.

If the final component of old is a symbolic link, the symbolic link is renamed, not the file or directory to which it points.

The renameat() system call is equivalent to rename() except in the case where either from or to specifies a relative path.  If from is a relative path, the
file to be renamed is located relative to the directory associated with the file descriptor fromfd instead of the current working directory.  If the to is a
relative path, the same happens only relative to the directory associated with tofd.  If the renameat() is passed the special value AT_FDCWD in the fromfd or tofd parameter, the current working directory is used in the determination of the file for the respective path parameter.複製代碼

因爲這個函數原著已經講得足夠詳細,因此這裏只補充如下幾點

  1. 新項目存在時,將先刪除,而後在將老項目轉移爲新的名稱
  2. 若是文件是符號連接,則只對符號連接自己起做用而不是其對應的文件

符號連接

符號連接,即一般說的軟連接,是對一個文件的間接指針。硬連接是直接指向索引塊,而軟連接其實是一個文件,其實從上面你們應該看到了硬連接的侷限和危險,直接操做硬連接後果不好,因此引入符號連接就是爲了解決硬連接存在的問題。
因爲符號連接和普通文件幾乎同樣,因此任何涉及文件的函數,都須要注意是否處理符號連接,通常來講都是以flag參數的形式來改變函數的行爲,從前面這麼多的函數應該瞭解到了。
Unix系統也提供了建立符號連接的函數

int symlink(const char *path1, const char *path2);
int symlinkat(const char *name1, int fd, const char *name2);複製代碼

函數很簡單,兩個函數就只有相對路徑和絕對路徑的區別。因爲符號連接自己是一個文件,因此也應該能夠讀寫,可是open函數是跟隨符號連接的,因此Unix系統也提供了打開符號連接的函數

ssize_t readlink(const char *restrict path, char *restrict buf, size_t bufsize);
ssize_t readlinkat(int fd, const char *restrict path, char *restrict buf, size_t bufsize);複製代碼

這兩個函數實際上就是一個相對路徑和絕對路徑的區別,並且這兩個函數都整合了open、read、close的操做

文件的時間

在前面的文章裏講到了stat結構體,而且能夠看到,stat結構體中時間字段有秒和納秒兩種精度,可是從上面講的文件系統能夠知道,inode塊記錄了文件的詳細信息,天然也包括了文件的時間信息,而inode塊的實現是根據文件系統來肯定的,這意味着精度是根據文件系統的不一樣而不一樣。
在學習Unix系統的時候你們應當知道了文件有三個時間
|字段|說明|例子|ls(1)選項|
|----|---|---|--------|
|st_atime|文件的最後訪問時間|read|-u|
|st_mtime|文件的最後修改時間|write|默認|
|st_ctime|inode塊狀態的最後更改時間|chmod、chown|-c|
從上面咱們知道,如今大多數的文件系統索引塊和數據塊是分別存放的,也就是說,修改文件信息和修改文件內容徹底是兩碼事,當執行影響inode塊的命令的時候就會修改st_ctime,當執行內容修改的時候就會修改st_mtime
咱們知道,目錄也是一種文件,增長、刪除或修改目錄項會影響到它所在的目錄的相關的三個時間,通常有如下幾點規律

  1. 任何修改目錄內容的操做也一定會致使目錄的inode塊被修改,即mtime修改同時ctime修改
  2. 任何建立子目錄的行爲都會致使父目錄被更改,這點很好理解,由於子目錄一定會致使父目錄索引數目增長
  3. 目錄下項目數目的增長都會致使目錄被更改
  4. 重命名一個文件實際就是修改了inode塊,一定會致使目錄修改

其實最關鍵的就是理解文件系統索引塊和數據塊分離的事實,這樣就很容易理解上面的概念了。
順便提一句,atime是access time、mtime是modification time、ctime是change time,有一些Linux教程書裏面將ctime認爲是文件內容更改時間,是錯誤的。

文件時間修改函數

int utimes(const char *path, const struct timeval times[2]);
int futimes(int fildes, const struct timeval times[2]);複製代碼

爲了管理文件時間,Unix系統也提供了文件時間的管理函數,在原著中,有三個函數提供,可是很是惋惜的是,Mac OS X系統只提供了以上兩個函數,utimensat函數並未提供,而且futimensfutimes替代了,而Linux系統則是提供utimensat函數的。

int utimensat(int dirfd, const char *pathname, const struct timespec times[2], int flags);

If times is NULL, the access and modification times are set to the current time.  The caller must be the owner of the file, have permission to write the file, or be the super-user.

If times is non-NULL, it is assumed to point to an array of two timeval structures.  The access time is set to the value of the first element, and the modi-fication time is set to the value of the second element.  The caller must be the owner of the file or be the super-user.

In either case, the inode-change-time of the file is set to the current time.複製代碼

上面的是CentOS6.x系統提供的函數原型,經過對比以前那些函數族,能夠發現,實際上系統應當提供這一功能強大的方法,具體緣由並不清楚,若是有朋友瞭解能夠指點一二。
這兩個參數都是傳入一個timeval結構體數組,timeval其實是如下結構體

_STRUCT_TIMEVAL
{
        __darwin_time_t         tv_sec;         /* seconds */
        __darwin_suseconds_t    tv_usec;        /* and microseconds */
};複製代碼

實際上這個和timespec結構體是同樣樣的,筆者也不知道爲何已經有了timespec還要再搞一個結構體出來。這個參數的兩個時間都是日曆時間,也就是俗稱的Unix時間戳,一個是acess time一個是modification time,具體規則上面實際上已經有了。

  1. times參數爲空,兩個時間設置爲當前時間
  2. 若是非空,兩個時間分別被設置爲第一個元素和第二個元素
  3. 任何狀況下,change time都被設置爲當前時間,其實這點很好理解,修改兩個時間實際上就是修改了索引塊

原著中實際上寫了不少,可是很惋惜,系統手冊上只有這麼點東西,筆者這裏就只按照系統手冊來寫,若是有朋友想要了解原著內容請查看原著。
而後下面是原著中的一個例程,這個程序其實是無法跑的,因此在這裏筆者對其修改了。

#define _DARWIN_C_SOURCE 1
#include "include/apue.h"
#include <sys/time.h>
#include <fcntl.h>

int main(int argc, char *argv[])
{
    int i, fd;
    struct stat statbuf;
    struct timeval times[2];

    for (i = 1; i < argc; ++i) {
        if (stat(argv[i], &statbuf) < 0) {
            err_ret("%s: stat error", argv[i]);
            continue;
        }
        if ((fd = open(argv[i], O_RDWR | O_TRUNC)) < 0) {
            err_ret("%s: open error", argv[i]);
            continue;
        }
        times[0].tv_sec = statbuf.st_atimespec.tv_sec;
        times[0].tv_usec = statbuf.st_atimespec.tv_nsec;
        times[1].tv_sec = statbuf.st_mtimespec.tv_sec;
        times[1].tv_usec = statbuf.st_mtimespec.tv_nsec;
        if (futimes(fd, times) < 0)
            err_ret("%s: futimens error", argv[i]);
        close(fd);
    }
    exit(0);
}複製代碼

蘋果系統內的futimesutimes函數是歸類給標準C庫的,可是確實放在BSD系統調用中的,不要問我爲何,由於我也不知道爲何,並且能夠經過查看<sys/stat.h>頭文件發現,程序所須要的結構體定義是放在#if !defined(_POSIX_C_SOURCE) || defined(_DARWIN_C_SOURCE),因爲APUE頭文件已經定義了_POSIX_C_SOURCE,因此必需要增長_DARWIN_C_SOURCE宏定義,不增長的後果自行嘗試,並且因爲標準C庫本身一套數據結構,必須在這裏手工轉換timespec->timeval類型。實際上兩個是同樣的。因爲系統提供精確到納秒的時間,而標準C庫提供的是精確到毫秒的時間,因此須要單位轉換。
上面說了那麼多,實際上並無什麼卵用,這個程序只是爲了證實修改文件信息會致使st_ctime的時間改變罷了。

建立刪除目錄函數族

int mkdir(const char *path, mode_t mode);
int mkdirat(int fd, const char *path, mode_t mode);複製代碼

沒什麼好說的,因爲前面說過的BSD系列的特色,新目錄的擁有者ID是進程的有效擁有者ID,組ID則是父目錄的組ID,並且還有一點須要注意,mkdir函數對mode超出低九位的行爲是沒有反應的,因此最好在mkdir後使用chmod確認權限。

int rmdir(const char *path);複製代碼

前面說過,目錄是一個文件,因此它也具備文件的特色,例若有進程打開時,刪除會執行,可是不會釋放實際目錄,這個函數只能用來刪除空目錄

讀目錄

對於用戶來講,目錄的讀寫是透明的,可是實際上只有內核才能寫目錄,這樣就不會出現文件系統的混亂,POSIX標準定義了一套有關目錄的函數

DIR *opendir(const char *filename);
DIR *fdopendir(int fd);

struct dirent *readdir(DIR *dirp);

long telldir(DIR *dirp);

void seekdir(DIR *dirp, long loc);

void rewinddir(DIR *dirp);
int closedir(DIR *dirp);複製代碼

這裏只討論原著中的函數原型,頭兩個很簡單,就是打開一個目錄返回DIR *
在第一篇文章中有一個ls程序的實現,蘋果系統下的實現以下

struct dirent {
        ino_t d_ino;                    /* file number of entry */
        __uint16_t d_reclen;            /* length of this record */
        __uint8_t  d_type;              /* file type, see below */
        __uint8_t  d_namlen;            /* length of string in d_name */
        char d_name[__DARWIN_MAXNAMLEN + 1];    /* name must be no longer than this */
};複製代碼

Unix規範規定,至少必須包含d_inod_name實現,DIR結構體的實現以下

typedef struct {
        int     __dd_fd;        /* file descriptor associated with directory */
        long    __dd_loc;       /* offset in current buffer */
        long    __dd_size;      /* amount of data returned */
        char    *__dd_buf;      /* data buffer */
        int     __dd_len;       /* size of data buffer */
        long    __dd_seek;      /* magic cookie returned */
        long    __dd_rewind;    /* magic cookie for rewinding */
        int     __dd_flags;     /* flags for readdir */
        __darwin_pthread_mutex_t __dd_lock; /* for thread locking */
        struct _telldir *__dd_td; /* telldir position recording */
} DIR;複製代碼

是否是和FILE結構體很像,這個結構體實際上纔是保存了目錄信息。打開的兩個函數返回的DIR最終是被其餘函數所使用,並且咱們看到,目錄實際上也有偏移量一說。
readdir函數返回下一個目錄項的指針,直到最終到底部返回null。
telldir是查詢偏移量,seekdir是設置偏移量,rewinddir是將偏移量放到最開始,closedir就是關閉目錄。
原著還寫了一個例程用於遍歷目錄層次,可是筆者認爲並無什麼必要,若是還有不明白的徹底能夠查詢Unix用戶手冊,因此這裏就再也不贅述。

工做目錄函數族

每一個進程實際上都有一個當前工做目錄,這個目錄在前面不少函數中都很是有用,全部的相對路徑都是以此計算,當前工做目錄是進程一個屬性,Unix系統也提供了系統函數。

int chdir(const char *path);
int fchdir(int fildes);複製代碼

咱們知道,shell實際上也是一個進程,它在啓動時讀取/etc/passwd中有關登陸用戶的家目錄的字段,而後將自身工做目錄改變到家目錄。
筆者在這裏強調,工做目錄是進程一個屬性,它和其餘進程無關,這樣咱們就理解了爲什麼cd命令是shell內建命令。
並且咱們知道,子進程會繼承父進程的屬性,因此shell打開一個程序,它的初始工做目錄就是shell的工做目錄。
講到這裏,可能有些朋友已經以爲奇怪,工做目錄既然是一個屬性,爲何沒有獲取當前工做目錄完整路徑的函數,其實是這樣,因爲內核的實現中,內核只保存了當前工做目錄的一個指針,並無保存完整的路徑名,因此沒有直接獲得當前工做目錄路徑的函數。
可是因爲內核持有當前工做目錄的指針,那麼,是否是能夠經過反向向上查找,一級級反推,最終組裝出當前工做目錄呢。答案是能夠的,並且系統還將其封裝好了。

char *getcwd(char *buf, size_t size);
char *getwd(char *buf);複製代碼

這兩個是標準C函數,第二個函數其實是一個兼容性函數,不須要提供緩衝區大小指示。

#include "include/apue.h"

int main(int argc, char *argv[])
{
    char *ptr;
    size_t size;
    if (chdir("/var") < 0)
        err_sys("chdir error");
    ptr = path_alloc(&size);
    if (getcwd(ptr, size) == NULL)
        err_sys("getcwd failed");
    printf("cwd = %s\n", ptr);
    exit(0);
}複製代碼

這是原著一個例程,在這裏有一個path_alloc函數,也是須要咱們手動將其編譯爲靜態庫,而後連接到程序中。爲了保證Mac OS X系統能運行,這裏對目錄作了修改,將其指定爲/var

> ./a.out
cwd = /private/var
> ls -l /var
lrwxr-xr-x 1 root  wheel  11  2  2 03:17 /var -> private/var複製代碼

因此能夠知道chdir是跟隨符號連接的,可是當getcwd沿着目錄向上時,仍是會根據實際路徑計算。
getcwd說明中,系統還教了一種簡便方法

A much faster and less error-prone method of accomplishing this is to open the current directory (`.') and use the fchdir(2) function to return.複製代碼

在更換到其餘目錄前,直接使用open打開當前工做目錄(.),而後保存文件描述符,當但願回到原工做目錄時,只須要將其傳遞給fchdir就好了。

設備特殊文件

原著講的很詳細了,並且這個並非很是有用,因此這裏就不說起了。

相關文章
相關標籤/搜索