apue 第4章 文件和目录

函数stat,fstat,fstatat, lstat

#include <sys/stat.h>

int stat(const char *restrict pathname, struct stat *restrict buf);
int fstat(int fd, struct stat *buf);
int lstat(const char *restrict pathname, struct stat *restrict buf);
int lstat(int fd, const char *restrict pathname, struct stat *restrict buf, int flag);

/* 所有4个函数的返回值:若成功,返回0;若出错,返回-1 */
  • stat:获得路径所指向的文件信息
  • lstat:stat类似,除了当路径指向为符号链接时,lstat返回链接本身信息,而stat返回对应的文件信息
  • fstat:获取已经打开的文件描述符的文件信息
  • fstatat:为一个相对于当前打开目录(由fd参数指向)的路径名返回文件统计信息。当AT_FDCWD传入fd参数,并且路径参数为相对路径时,不会跟随符号链接,而是返回符号链接本身的信息。如果路径为绝对路径,则fd参数被忽略。
sturct stat { 
    mode_t          st_mode;        /* file type & mode (permissions) */
    ino_t           st_ino;         /* i-node number (serial number) */
    dev_t           st_dev;         /* device number (file system) */
    dev_t           st_rdev;        /* device number for special files */
    nlink_t         st_nlink;       /* number of links */
    uid_t           st_uid;         /* user ID of links */
    gid_t           st_gid;         /* group ID of owner */
    off_t           st_size;        /* size in bytes, for regular files  */
    struct timespec st_atime;       /* time of last access */
    struct timespec st_mtime;       /* time of last modification */
    struct timespec st_ctime;       /* time of last file status change */
    blksize_t       st_blksize;     /* best I/O block size */
    blkcnt_t        st_blocks;      /* number of disk blocks allocated */
}

文件类型

  • 普通文件(regular file):包含了某种形式的数据;
  • 目录文件(directory file):包含了其他文件的名字以及指向与这些文件有关信息的指针;
  • 块特殊文件(block special file):该类型文件提供对设备(如磁盘)带缓冲的访问,每次访问以固定长度为单位进行;
  • 字符特殊文件(character special file):该文件提供对设备不带缓冲的访问,每次访问长度不变。系统中的所有设备要么是字符特殊文件,要么是块特殊文件。
  • FIFO:该文件用于进程通信,也称为命名管道(named pipe);
  • 套接字(socket):该文件用于进程间的网络通信,也可以用在一台宿主机上进程间的非网络通信。
  • 符号链接(symbolic link):该文件指向另一个文件。

确定文件类型

文件类型
S_ISREG() 普通文件
S_ISDIR() 目录文件
S_ISCHR() 字符特殊文件
S_ISBLK() 块特殊文件
S_ISFIFO() 管道或FIFO
S_ISLNK() 符号链接
S_ISSOCK() 套接字

例子:取命令行参数然后针对每一个命令行打印其文件类型

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/stat.h>

int main(int argc, char** argv)
{
    int i;
    char* ptr;
    struct stat buf;

    for (i = 1; i < argc; ++i)
    {
        printf("%s: ", argv[i]);
        if (lstat(argv[i], &buf) < 0)
        {
            printf("lstat error!\n");
            continue;
        }

        if(S_ISREG(buf.st_mode))
            ptr = "regular";
        else if(S_ISDIR(buf.st_mode))
            ptr = "directory";
        else if(S_ISCHR(buf.st_mode))
            ptr = "character special";
        else if(S_ISBLK(buf.st_mode))
            ptr = "block special";
        else if(S_ISFIFO(buf.st_mode))
            ptr = "fifo";
        else if(S_ISLNK(buf.st_mode))
            ptr = "symbolic link";
        else if (S_ISSOCK(buf.st_mode))
            ptr = "socket";
        printf("%s\n", ptr);

    }


    exit(0);
}
yanke@yanke-pc:~/yanke/Code/apue/ch4$ ./a.out /etc/passwd /etc/ /dev/log \
> /dev/tty /var/lib/oprofile/opd_pipe /dev/sr0 /dev/cdrom
/etc/passwd: regular
/etc/: directory
/dev/log: socket
/dev/tty: character special
/var/lib/oprofile/opd_pipe: lstat error!
/dev/sr0: lstat error!
/dev/cdrom: lstat error!
各ID名字 含义
实际用户ID和实际组ID 我们实际是谁
有效用户ID,有效组ID和附属组ID 用于文件访问权限检查
保存的设置用户ID和保存的设置组ID 由exec函数保存
  • 实际用户ID和实际组ID这两个字段在登陆时候确定,一般不会改变。比如我以yanke登录,那么实际用户id即yanke的uid,实际组id为yanke的gid.
  • 有效用户ID和有效用户组ID是进程用来决定我们对资源的访问权限。一般情况下,与实际用户id和实际组id相同,但是当设置了suid(设置用户id)和sgid(设置组id),则有效用户ID等于文件的所有者的uid,有效用户组ID等于文件所有者的gid。
  • 除了rwx权限以外,还有s权限,这就是设置用户ID和设置组ID,它能让进程有效用户ID和有效组ID等于程序拥有者的uid和gid。

文件访问权限

st_mode成员包含了文件的访问权限位。
用户rwx、组rwx和其他rwx,可以使用chmod命令改变。

st_mode屏蔽 含义
S_IRUSR 用户读
S_IWUSR 用户写
S_IXUSR 用户执行
S_IRGRP 组读
S_IWGRP 组写
S_IXGRP 组执行
S_IROTH 其他读
S_IWOTH 其他写
S_IXOTH 其他执行

文件访问如下规则:
* 打开任意类型文件,需要对保护该名字的每一个目录都有执行权限
* 对于一个文件的读权限决定了我们能不能打开文件进行读操作。
* 对于一个文件的写权限决定了我们能不能打开文件进行写操作。
* 为了在一个目录中创建新文件,必须对包含该文件的目录具有写权限和执行权限

新文件和目录的所有权

新文件的用户ID设置为进程的有效用户ID
关于组ID:可以是进程的有效组ID,也可以是它所在目录的组ID

访问权限测试函数access和faccessat

函数access和faccessat是按照实际用户ID和实际组ID来进行访问权限设置的。faccessat函数更加强大,既可以测试实际用户ID,也可以测试有效用户ID,除非是少数情况,大部分都是使用faccessat函数.

#include <unistd.h>
int access(const char *path, int amode);
int faccessat(int fd, const char *path, int mode, int flag);
  • flag设置为AT_EACCESS,则检测有效用户ID和有效组ID。

access函数的用法:

// use access function
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>

int main(int argc, char** argv)
{
    if (argc != 2)
    {
        printf("usage : a.out <pathname>\n");
        exit(1);
    }
    if (access(argv[1],R_OK) < 0)
    {
        printf("access error for %s\n", argv[1]);
    }
    else
    {
        printf("read access OK\n");
    }
    if (open(argv[1], O_RDONLY) < 0)
    {
        printf("open error for %s\n", argv[1]);
    }
    else
    {
        printf("open for reading OK\n");
    }

    exit(0);

}

函数umask

为进程设置屏蔽字,返回之前的值。

#include <sys/stat.h> mode_t umask(mode_t cmask);
  • 使用umask
#include <stdio.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <fcntl.h>

#define RWRWRW (S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH)

int main()
{
    umask(0);
    if (creat("foo", RWRWRW) < 0)
    {
        printf("creat foo error!\n");
        exit(1);
    }
    umask(S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH);
    if (creat("bar", RWRWRW) < 0)
    {
        printf("creat bar error!\n");
        exit(1);
    }

    exit(0);
}

yanke@vm:~/Code/apue/ch4$ umask
0002
yanke@vm:~/Code/apue/ch4$ ll bar foo
-rw------- 1 yanke yanke 0  831 18:39 bar
-rw-rw-rw- 1 yanke yanke 0  831 18:39 foo

shell启动时,它会读取启动文件的设置从而在启动时自行设置为默认的掩码字,在运行过程中,用户可以使用umask命令手动更改掩码字。shell打开一个进程,新的进程则会继承shell的umask值,在使用Unix的时候,经常需要使用的mkdir、touch之类的创建文件的命令,这些命令在运行的时候就是继承shell的umask。
因此:如果想要保证自己能完全指定文件的权限,那么必须在运行时使用umask函数修改为0,否则非0得umask值可能会关闭我们需要的权限位置,当然,如果进程不需要关心文件的权限问题,那么完全可以指定rwxrwxrwx或者rwrwrw的权限,然后umask会自动根据默认值将其修改。

文件权限修改函数chmod,fchmod,fchmodat

#include <sys/stat.h>
int chmod(const char *path, mode_t mode);
int fchmod(int fildes, mode_t mode);
int chmodat(int fd, const char *path, mode_t mode, int flag);
  • 函数返回值:成功返回0,出错返回-1.
  • flag参数为AT_SYMLINK_NOFOLLOW时,fchmodat不会跟随符号链接。当flag为0的时候,和其他两个函数等价。

全部的权限位如下:

mode 说明
S_ISUID 执行时设置用户ID
S_ISGID 执行时设置组ID
S_ISVTX 保存正文(粘着位)
S_IRWXU 用户(所有者)读、写和执行
S_IRUSR 用户(所有者)读
S_IWUSR 用户(所有者)写
S_IXUSR 用户(所有者)执行
S_IRWXG 组读、写和执行
S_IRGRP 组读
S_IWGRP 组写
S_IXGRP 组执行
S_IRWXO 其他读、写和执行
S_IROTH 其他读
S_IWOTH 其他写
S_IXOTH 其他执行
#define S_ISUID __S_ISUID /* Set user ID on execution. */ 
#define S_ISGID __S_ISGID /* Set group ID on execution. */ 
#define S_ISVTX __S_ISVTX 
#define S_IRUSR __S_IREAD /* Read by owner. */ 
#define S_IWUSR __S_IWRITE /* Write by owner. */ 
#define S_IXUSR __S_IEXEC /* Execute by owner. */ 
#define S_IRWXU (__S_IREAD|__S_IWRITE|__S_IEXEC) 
#define S_IRGRP (S_IRUSR >> 3) /* Read by group. */ 
#define S_IWGRP (S_IWUSR >> 3) /* Write by group. */ 
#define S_IXGRP (S_IXUSR >> 3) /* Execute by group. */ 
#define S_IRWXG (S_IRWXU >> 3) 
#define S_IROTH (S_IRGRP >> 3) /* Read by others. */ 
#define S_IWOTH (S_IWGRP >> 3) /* Write by others. */ 
#define S_IXOTH (S_IXGRP >> 3) /* Execute by others. */ 
#define S_IRWXO (S_IRWXG >> 3) 
#define __S_ISUID 04000 /* Set user ID on execution. */ 
#define __S_ISGID 02000 /* Set group ID on execution. */ 
#define __S_ISVTX 01000 /* Save swapped text after use (sticky). */ 
#define __S_IREAD 0400 /* Read by owner. */ 
#define __S_IWRITE 0200 /* Write by owner. */ 
#define __S_IEXEC 0100 /* Execute by owner. */ 
  • 使用chmod
//test chmod

#include <stdio.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>

int main()
{
    struct stat statbuf;
    //
    if (stat("foo", &statbuf) < 0)
    {
        printf("stat error for foo\n");
        exit(1);
    }
    if (chmod("foo", (statbuf.st_mode & ~S_IXGRP) | S_ISGID) < 0)
    {
        printf("chmod error for foo\n");
        exit(1);
    }

    if (chmod("bar", S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH) < 0)
    {
        printf("chmod error for bar\n");
       exit(-1);
    }

    exit(0);
}

yanke@vm:~/Code/apue/ch4$ ll foo bar
-rw-r--r-- 1 yanke yanke 0  831 18:39 bar
-rw-rwSrw- 1 yanke yanke 0  831 18:39 foo

粘着位

粘着位,又称保存文本位,其实就是一个权限控制属性。通常情况下,粘着位即可以用在普通文件上,也可以用在目录文件上。当用在普通文件上时,粘着位可以把某个程序文件的SUID置位。并且它的文本映像将永久保存在交换区里。如此的话,当Unix操作系统程序获得了CPU使用权时,就可以快速的装载到内存中。故粘着位可以提高系统程序的运行效率。
如有些版本的Unix系统,就把vi等常用的程序文件的粘着位设置为1,就明显提高了这些应用程序的运行效率。不过现在很少用到这个特性。因为现在磁盘读写速度已经达到极致;而且内存的价格也便宜。
也就是说,现在磁盘速度与内存往往已经不是系统的瓶颈资源,故为普通文件设置粘着位已经没有特殊的必要了。为此笔者现在在系统设置中,基本上不会为普通文件设置粘着位。

 更改用户ID和组ID

chown、fchown、fchownat和lchown用于更用户ID和组ID。如果两个参数owner或group中的任意一个是-1,则对应的ID不变。

#include <unistd.h>

int chown(const char *pathname, uid_t owner, git_t group);
int fchown(int fd, uid_t owner, gid_t group);
int fchownat(int fd, const char *pathname, uid_t owner, gid_t group, int flag);
int lchown(const char *pathname, uid_t owner, gid_t group);
//返回值:若辰宫,返回0;若出错,返回-1

文件长度

stat结构成员st_size表示以字节为单位的单位长度。只对普通文件、目录文件和符号连接有效。

  • 对于普通文件,文件长度可以为0
  • 对于目录文件,文件长度是一个数(16,512)的整数倍
  • 对于链接文件,文件长度为文件名中的实际字节数

文件空洞

空洞是由所设置的偏移量超过文件端尾,并写入了某些数据后造成的。

文件截断

如果该文件以前的长度大于length,则超过length以外的数据就不能再访问。如果以前的长度小于length,文件长度将增加,在以前的文件尾端和新的文件尾端之间的数据将读作0。

include <unistd.h>

int truncate(const char *pathname, off_t length);
int ftruncate(int fd, off_t length);
//返回值:若成功,返回0;若失败,返回-1

文件系统

  • 每个i节点中都有一个链接计数,其值是指向该i节点的目录项数。只有当链接计数减至0时,才可删除该文件(也就是可以释放该文件占用的数据块,这里要注意的一点是虚拟文件系统中并没有关于数据块的概念,只有具体的文件系统中才有相关概念)。这也就是为什么“解除对一个文件的链接”操作并不总是意味着“释放该文件占用的磁盘块”的原因。这也是为什么删除一个目录项的函数被称之为unlink而不是delete的原因。在stat结构中,链接计数包含在st_nlink成员中。这种链接为硬链接。
  • 另外一种链接类型称为符号链接。符号链接文件的实际内容(在数据块中)包含了该符号链接所指向的文件的名字。该i节点中的文件类型是S_IFLINK,于是系统知道这是一个符号链接。
  • i节点包含了文件有关的所有信息。stat结构中的大多数信息都取自i节点。只有两项重要数据存放在目录项中:文件名和i节点编号。此处的目录项其实也是一种数据块,但它代表的是目录中的一项,因此被称为目录项。目录项中存储的数据是文件名与i节点编号,i节点编号就是文件名所引用的文件的i节点编号,通过这一编号可首先查找到具体的i节点,再通过i节点访问文件中的数据。
  • 一个文件系统中的i节点不能指向另一个文件系统中的i节点,
  • 当在不更换文件系统的情况下为一个文件重命名时,该文件的实际内容并未移动,只需构造一个指向现有i节点的新目录项,并删除老的目录项。链接计数并不会发生改变。
  • 对于任何一个叶子目录(不包含任何其他目录的目录)的链接计数总是2,数值2来自于命令该目录的目录项以及在该目录中的.项。注意,在父目录中的每一个子目录都是该父目录的链接计数增加1。

函数link、linkat、unlink、unlinkat和remove

创建一个指向现有文件的链接

#include <unistd.h>

int link(const char *existingpath, const char *newpath);
int linkat(int efd, const char *existingpath, int nfd, const char *newpath, int flag);
// 两个函数的返回值:若成功,返回0;若出错,返回-1 
  • 这两个函数创建一个新目录项newpath,它引用现有文件existingpath。* * 如果newpath已存在,则返回出错。
  • 对于linkat函数,现有文件是通过efd和existingpath指定,新的路径名是通过nfd和newpath指定的。

删除一个现有目录项

#include <unistd.h>

int unlink(const char *pathname);
int unlinkat(int fd, const char *pathname, int flag);
// 两个函数的返回值:若成功,返回0;若出错,返回-1
  • 这两个函数删除目录项,并将由pathname所引用的链接数减1。
  • 解除文件链接,必须具有写权限,并且必须要有三个条件之一:1.拥有该文件;2.拥有该目录;3.具有超级用户权限。
  • 只有连接为0时才会删除文件,但是只要有进程打开了文件,这个文件也不会删除。

使用remove删除

  • 对于文件和unlink相同
  • 对于目录,与rmdir相同
#include <stdio.h>

int remove(const char *pathname);
// 返回值:若成功,返回0;若失败,返回-1 

函数rename和renameat

#include <stdio.h>

int rename(const char *oldname, const char *newname);
int renameat(int oldfd, const char *oldname, int newfd, const char *newname);
  • 如果oldname指的是一个文件而不是目录,那么为该文件或符号连接重命名。
  • 如果oldname指的是一个空目录,那么为该目录重命名。
  • 如果oldname或newname引用符号连接,则处理的是符号链接本身,而不是它引用的文件。
  • 不能对.和..重命名。
  • 作为一个特例,如果oldname和newname引用同意文件,则函数不做任何更改而成功返回。

符号链接

符号链接,即通常说的软链接,是对一个文件的间接指针。硬链接是直接指向索引块,而软链接实际上是一个文件。

硬链接的限制:
- 硬链接通常要求链接和文件位于同一文件系统中。
- 只有超级用户才能创建指向目录的硬链接。

对符号链接以及他指向何种对象并无任何文件系统限制。任何用户都可以创建指向目录的符号链接。符号链接一般用于将一个文件或这个目录结构移动到系统中的另一个位置。

创建和读取符号连接

创建目录,其中.和..目录项是自动创建的。目录项通常至少要设置一个执行权限位,以允许访问该目录中的文件名。

#include <unistd.h>

int symlink(const char *actualpath, const char *sympath);
int symlinat(const char *acutalpath, int fd, const char *symptah);
// 两个函数的返回值:若成功,返回0;若出错,返回-1 

读取符号链接

#include <unistd.h>

ssize_t readlink(const char *actualpath, const char *symptah);
sszie_t readlinkat(int fd, const char* restrict pathname, char *restrict buf, size_t bufsize);
// 两个函数的返回值:若成功,返回读取的字节数;若出错,返回-1 

文件时间

字段 说明 例子 ls(1)选项
st_atim 文件数据的最后访问时间 read -u
st_mtim 文件数据的最后修改时间 write 默认
st_ctim i节点状态的最后更改时间 chomd、chown -c
  • 由于文件i节点和文件的实际内容是分开的,修改i节点就会修改st_ctime,修改文件内容就会修改st_mtime
  • 目录也是一种文件,也有三个时间
    • 任何修改目录内容的操作也必定会导致目录的inode块被修改,即mtime修改同时ctime修改
    • 任何创建子目录的行为都会导致父目录被更改,这点很好理解,因为子目录必定会导致父目录索引数目增加
    • 目录下项目数目的增加都会导致目录被更改
    • 重命名一个文件实际就是修改了inode块,必定会导致目录修改

函数futimens、utimensat和utimes

修改文件访问时间

#include <sys/stat.h>

int futimens(int fd, const struct timespec times[2]);
int utimensat(int fd, const char *path, const struct timespec times[2], int flag);
//两个函数返回值:若成功,返回0;若出错,返回-1
  • times数组参数第一个元素包含访问时间,第二个元素包含修改时间
  • times参数为空,两个时间设置为当前时间
  • 如果非空,两个时间分别被设置为第一个元素和第二个元素
  • 任何情况下,change time都被设置为当前时间

使用futimens

#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <time.h>

int main(int argc, char** argv)
{
    int i, fd;
    struct stat statbuf;
    struct timespec times[2];
    for (i = 1; i < argc; ++i)
    {
        //get current times
        if (stat(argv[0], &statbuf) < 0)
        {
            printf("%s:stat error\n", argv[i]);
            continue;
        }
        if ((fd=open(argv[i], O_RDWR|O_TRUNC)) < 0)
        {
            printf("%s:open error\n", argv[i]);
            continue;
        }

        times[0] = statbuf.st_atim;
        times[1] = statbuf.st_mtim;
        if (futimens(fd, times) < 0)
        {
            printf("%s:futimes error\n", argv[i]);
        }
        close(fd);
    }

    exit(0);
}
yanke@vm:~/Code/apue/ch4$ touch changemod times
yanke@vm:~/Code/apue/ch4$ ll changemod times 
-rw-rw-r-- 1 yanke yanke 0  831 21:55 changemod
-rw-rw-r-- 1 yanke yanke 0  831 21:55 times
yanke@vm:~/Code/apue/ch4$ ls -lu changemod times 
-rw-rw-r-- 1 yanke yanke 0  831 21:55 changemod
-rw-rw-r-- 1 yanke yanke 0  831 21:55 times
yanke@vm:~/Code/apue/ch4$ ls -lc changemod times 
-rw-rw-r-- 1 yanke yanke 0  831 21:55 changemod
-rw-rw-r-- 1 yanke yanke 0  831 21:55 times
yanke@vm:~/Code/apue/ch4$ ./a.out changemod times 
yanke@vm:~/Code/apue/ch4$ ls -lu changemod times 
-rw-rw-r-- 1 yanke yanke 0  831 21:55 changemod
-rw-rw-r-- 1 yanke yanke 0  831 21:55 times
yanke@vm:~/Code/apue/ch4$ ls -lc changemod times 
-rw-rw-r-- 1 yanke yanke 0  831 21:57 changemod
-rw-rw-r-- 1 yanke yanke 0  831 21:57 times

函数mkdir、mkdirat和rmdir

  • 创建新目录
include <sys/stat.h>

int mkdir(const char *pathname, mode_t mode);
int mkdir(int fd, const char *pathname, mode_t mode);
//两个函数返回值:若成功,返回0;若出错,返回-1
  • 用rmdir函数可以创建一个空目录
#include <unistd.h>

int rmdir(const char *pathname);
//返回值:若成功,返回0;若失败,返回-1

读目录

#include <dirent.h>

DIR *opendir(const char *pathname);
DIR *fdopendir(int fd);
/* 两个函数返回值:若成功,返回指针;若出错,返回NULL */

struct dirent *readdir(DIR *dp);
/* 返回值:若成功,返回指针;若在目录尾或出错,返回NULL */

void rewinddir(DIR *dp);
int closeddir(DIR *dp);
/* 返回值:若成功,返回0;若失败,返回-1 */

long telldir(DIR *dp);
/* 返回值:与dp关联的相关位置 */

void seekdir(DIR *dp, long loc);

DIR结构体的定义如下。DIR结构体类似于FILE,是一个内部结构。

struct __dirstream
   {
    void *__fd;
    char *__data;
    int __entry_data;
    char *__ptr;
    int __entry_ptr;
    size_t __allocation;
    size_t __size;
    __libc_lock_define (, __lock)
   };
typedef struct __dirstream DIR;
  • dirent结构体,首先我们要弄清楚目录文件(directory file)的概念:这种文件包含了其他文件的名字以及指向与这些文件有关的信息的指针。
    定义如下:
struct dirent
{
   long d_ino; /* inode number 索引节点号 */
    off_t d_off; /* offset to this dirent 在目录文件中的偏移 */
    unsigned short d_reclen; /* length of this d_name 文件名长 */
    unsigned char d_type; /* the type of d_name 文件类型 */
    char d_name [NAME_MAX+1]; /* file name (null-terminated) 文件名,最长255字符 */
}

通过readdir函数读取到的文件名存储在结构体dirent的d_name成员中,而函数
int stat(const char *file_name, struct stat *buf);
的作用就是获取文件名为d_name的文件的详细信息,存储在stat结构体中。以下为stat结构体的定义:

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;       //以字节为单位的文件容量
        time_t     st_atime;      //最后一次访问该文件的时间
        time_t     st_mtime;      //最后一次修改该文件的时间
        time_t     st_ctime;      //最后一次改变该文件状态的时间
        blksize_t st_blksize;    //包含该文件的磁盘块的大小
        blkcnt_t   st_blocks;     //该文件所占的磁盘块
      };

想要获取某目录下(比如a目下)b文件的详细信息,我们应该怎样做?
- 使用opendir函数打开目录a,返回指向目录a的DIR结构体c。
- 调用readdir( c)函数读取目录a下所有文件(包括目录),返回指向目录a下所有文件的dirent结构体d。
- 遍历d,调用stat(d->name,stat *e)来获取每个文件的详细信息,存储在stat结构体e中。
总体就是这样一种逐步细化的过程,在这一过程中,三种结构体扮演着不同的角色。

 工作目录函数族 chdir、fchdir和getcwd

  • 每个进程都有一个当前工作目录,调用chdir和fchdir可以更改当前工作目录
#include <unistd.h>

int chdir(const char *pathname);
int fchdir(int fd);
  • 实例
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>

int main()
{
    if (chdir("/tmp") < 0)
    {
        printf("chdir failed\n");
        exit(1);
    }
    printf("chdir to /tmp succeeded\n");
    exit(0);
}
yanke@vm:~/Code/apue/ch4$ pwd
/home/yanke/Code/apue/ch4
yanke@vm:~/Code/apue/ch4$ ./a.out
chdir to /tmp succeeded
yanke@vm:~/Code/apue/ch4$ pwd
/home/yanke/Code/apue/ch4
  • 逐层上移,直到遇到根,得到当前工作目录完整的绝对路径。必须像此函数传递两个函数,一个是缓冲区地址buf(要有足够的长度容纳绝对路径再加上一个终止null字节),另一个是缓冲区的长度size(以字节为单位)。
char *getcwd(char *buf, size_t size);

参考

[1] http://blog.csdn.net/u012927281/article/details/51737312
[2] https://segmentfault.com/a/1190000004416055#articleHeader8
[3] http://www.liweifan.com/2012/05/13/linux-system-function-files-operation/

你可能感兴趣的:(apue 第4章 文件和目录)