《apue》读书笔记 第四章 文件和目录(4)

第四章 文件和目录

20.读目录

回顾:对于一个目录,r权限表示在该目录下可以执行ls命令,即查看目录内容;w权限表示用户可以在该目录下创建、修改、删除文件;x目录表示用户能否进入该目录,即能不能cd该目录。

书中这样说:

“对某个目录具有访问权限的任一用户都可读该目录,但是只有内核才能写目录(保持文件系统一致性)。【一个目录的写权限位和执行权限位决定了在该目录中能否创建新文件以及删除文件,但是它们并不表示能否写目录本身。】”

这一段不是很能理解!目前也查不到相关资料,暂记录在此~

许多实现不允许应用程序使用read函数访问目录内容,这样就将目录的具体实现格式细节和应用程序隔离开来。

 #include <dirent.h>
 DIR *opendir(const char *pathname);
 返回:如果成功返回目录指针,如果错误返回空。
 struct dirent *readdir(DIR *dp);
 返回:如果成功返回目录指针,如果到达目录结尾或者错误返回空。
 void rewinddir(DIR *dp);
 int closedir(DIR *dp);
 返回:如果成功返回0,如果错误返回-1
 long telldir(DIR *dp);
 返回:和dp关联的目录的当前位置。
 void seekdir(DIR *dp, long loc);

dirent结构在文件中定义,是依赖于具体实现(也就是系统实现)的。系统实现中,定义的dirent结构至少包含以下成员:

 struct dirent {
 ino_t d_ino;                  /* i-node number */
 char  d_name[NAME_MAX + 1];   /* null-terminated filename */
 }

d_ino并不是POSIX.1定义的,因为它是一个与实现相关的特性。但是,它在POSIX.1的XSI中定义了。POSIX.1只定义了这个结构的d_name成员。

DIR结构是一个内部结构,它由这6个函数用来维护正被读取的目录的有关信息。其作用类似于由标准I/O库维护的FILE结构。

opendir执行初始化操作,使第一个readdir读取目录中的第一个目录项。目录中各目录项的顺序与实现有关。它们通常并不按字母顺序排列。

21.chdir,fchdir和getcwd函数

每个进程都有一个当前工作目录,此目录是所有相对路径名(不以斜线开始的路径名)的搜索起点。当用户登录到UNIX系统时,其当前工作目录通常是密码文件(/etc/passwd )中该用户登录项的第6个字段所指定的目录(用户的起始目录)。【当前工作目录是进程的属性,而起始目录则是登录名的一个属性】。

进程调用chdir或fchdir函数可以更改当前工作目录。它们的声明如下:

 #include <unistd.h>
 int chdir(const char *pathname);
 int fchdir(int filedes);
 两者返回:如果成功返回0,如果错误返回-1

chdir通过文件路径名指定,而fchdir通过文件描述符号来指定。

使用chdir函数的例子:

 #include "apue.h"
 int main(void)
 {
  if (chdir("/tmp") < 0)
   err_sys("chdir failed");
  printf("chdir to /tmp succeeded\n");
  exit(0);
 }

如果我们编译以上代码,生成mycd可执行程序,并且运行,那么结果如下:

 $ pwd
 /usr/lib
 $ mycd
 chdir to /tmp succeeded
 $ pwd
 /usr/lib

××因为当前工作目录是进程的属性,所以子进程(mycd)调用chdir不会影响父进程(运行mycd程序的shell)的当前工作路径,这也是为什么cd命令(切换路径命令)是shell的内建命令而不是一个独立的程序的原因!

因为内核保持有当前工作目录的信息,所以我们应能取其当前值。可是,内核为每个进程只保存其当前工作目录的i节点编号以及设备标识,并不保存该目录的完整路径名。如果想要获取当前路径,那么我们需要从当前工作目录开始,找到其上一级的目录,然后读其目录项,直到该目录项中的i节点编号数与工作目录i节点编号数相同,这样地就找到了其对应的文件,然后按照这种方法,逐层上移,直到遇到根,这样就得到了当前工作目录的绝对路径名。其实,函数getcwd就提供了这种功能,其声明如下:

 #include <unistd.h>
 char *getcwd(char *buf, size_t size);
 返回:如果成功返回buf,如果错误返回NULL

使用getcwd函数的例子:

 #include "apue.h"
 int  main(void)
 {
  char    *ptr;
  int     size;
  if (chdir("/usr/spool/uucppublic") < 0)
   err_sys("chdir failed");
  ptr = path_alloc(&size); /* our own function */
  if (getcwd(ptr, size) == NULL)
   err_sys("getcwd failed");
  printf("cwd = %s\n", ptr);
  exit(0);
 }

运行上述代码,得到如下结果:

 $ ./a.out
 cwd = /var/spool/uucppublic
 $ ls -l /usr/spool
 lrwxrwxrwx 1 root 12 Jan 31 07:57 /usr/spool -> ../var/spool

注意,chdir会处理符号连接(即使是getcwd调用了它),但是当getcwd沿目录树上溯遇到/var/spool目录时,它并不了解该目录由符号连接/usr/spool所指向。

在实际应用中,如果我们需要更换目录,处理完之后再返回来,那么我们可以在更换目录前用getcwd保存当前工作目录,返回时调用chdir即可。

也有一种便捷的方法:更换目录时用open打开当前目录,保存文件描述符,返回时将描述符传递给fchdir即可。

22.特殊设备文件

和设备文件相关的stat成员是dev_t st_dev和dev_t st_rdev。

*每个文件系统通过主从设备号被获知,这两者属于基本系统类型dev_t(一般是一个整数类型的typedef)。major号标志驱动或者有时候标志和哪个外设板子相通信;minor用来标志特定的从设备。例如一个磁盘通常有多个文件系统,那么在这个磁盘上面的文件系统具有共同的major号,但是具有不同的minor号。

*为了访问major号和minor号,一般都有两个宏来达到这个目的:major和minor.我们不用考虑major号和minor号是怎么存放在dev_t中的。在不同系统中dev_t的位是不同的,那位存放什么也不同,具体参见文档。

*在系统中一个文件的st_dev值是包含这个文件的文件系统的device number.

*只有字符设备文件和块设备文件具有s_rdev值,这个值包含device number和实际的设备。

*对于一个文件,其st_dev的major和minor是这个文件所在的文件系统的major和minor.

*对于一个字符文件,会有st_dev和st_rdev.其中st_dev是这个字符设备特殊文件的文件节点所在的文件系统的major和minor(虚拟文件系统),而st_rdev是这个字符设备文件的实际设备的major和minor号码。

你可能感兴趣的:(linux,目录,文件系统)