该系列文章总纲链接:专题分纲目录 LinuxC 系统编程
本章节思维导图如下所示(思维导图会持续迭代):
第一层:
第二层:
目录操作:目录文件本质上是一个文件,但是实际上其内容又与普通文件不同。目录文件是一种特殊的文件,其内容存储着目录下所有文件的目录项。用户可以像普通文件一样读写目录,不过需要使用一组特殊的系统调用。
1 目录权限
目录文件的访问权限和普通文件是一样的,对于目录权限而言有:
S_IRUSR :0400:用户-读 S_IWUSR :0200:用户-写 S_IXUSR :0100:用户-执行 S_IRGRP :0040:用户组-读 S_IWGRP :0020:用户组-写 S_IXGRP :0010:用户组-执行 S_IROTH :0004:其他用户-读 S_IWOTH :0002:其他用户-写 S_IXOTH :0001:其他用户-执行
其中读、写、可执行分别表示:
- 读 :表示可以列出该目录所有的文件
- 写 :表示可以创建文件、删除文件
- 可执行 :表示可以打开目录,路径有效,一般都要有这个权限
测试目录文件:S_ISDIR(stat结构体变量.st_mode);为0,表示是目录文件;为非0,表示非目录文件。
设置用户ID位对目录没有意义,但是设置用户组ID对目录却有很大的意义。因为如果设置用户组ID被设置,则在该目录下创建的任何目录和文件的组ID都将设置为该目录的组ID。
2 目录操作
2.1 创建一个目录 mkdir
linux下使用mkdir函数来创建一个目录。mkdir函数的接口:
#include <sys/stat.h> #include <sys/types.h> int mkdir(const char *pathname, mode_t mode); 参数pathname :要创建的目录的路径。 参数mode :目录的操作权限(一般情况下,一定要加上可执行的权限)。 函数成功执行返回0,失败返回-1。
注意:
- 一个目录总要包含两个默认的目录项,即.(当前目录)和..(上一级目录)。
- 在linux下,新创建的子目录会自动继承父目录的设置组ID位,而不是创建进程的组ID。
2.2 删除一个目录 rmdir
linux下使用rmdir函数来创建一个目录。rmdir函数的接口:
#include <unistd.h> int rmdir(const char *pathname); 参数pathname :要删除的目录的路径。 函数成功执行返回0,失败返回-1。
注意:
- rmdir函数只能删除一个空目录,如果目录非空,则删除出错。(这里的空目录表示目录项只含有.和..文件)
- 如果目录的权限中没有写的权限,那么删除目录也会发生错误。
2.3 目录项结构 struct dirent
用户不可以直接对目录进行写的操作,这个操作完全是由内核来完成的,因此linux下不提供写目录的函数接口。一个目录文件中存着目录中所有文件的目录项,对目录的读写实际上就是操作这些目录项。目录项使用dirent结构表示,该结构体定义在dirent.h文件中:
struct dirent { #ifndef __USE_FILE_OFFSET64 /*用于64位的文件系统*/ __ino_t d_ino; /* inode number 索引节点号 */ off_t d_off; /*在目录文件中的偏移 */ #else __ino64_t d_ino; /* inode number 索引节点号 */ off_64t d_off; /*在目录文件中的偏移 */ #endif unsigned short int d_reclen; /*文件名长 */ unsigned char d_type; /*文件类型 */ char d_name [NAME_MAX+1]; /*文件名,最长255字符 */ };
2.4 打开与关闭目录 opendir closedir
linux下使用opendir函数来创建一个目录。opendir函数的接口:
#include <sys/types.h> #include <dirent.h> DIR *opendir(const char *name); 参数name :要打开的目录的路径。 open返回的是一个DIR结构的指针,该结构类似于FILE指针,只不过用来保存所打开目录的信息。
linux下使用closedir函数来创建一个目录。closedir函数的接口:
#include <sys/types.h> #include <dirent.h> int closedir(DIR *dirp); 参数dirp:要关闭的目录的DIR结构指针。 函数成功执行返回0,失败返回-1。
opendir和closedir是配套使用的,如果只打开目录而没有关闭,那么该进程就会因为打开的文件数过多而不能打开任何文件。这里的DIR结构体定义如下:
struct __dirstream { void *__fd; /* `struct hurd_fd' pointer for descriptor. */ char *__data; /* Directory block. */ int __entry_data; /* Entry number `__data' corresponds to. */ char *__ptr; /* Current pointer into the block. */ int __entry_ptr; /* Entry number `__ptr' corresponds to. */ size_t __allocation; /* Space allocated for the block. */ size_t __size; /* Total valid data in the block. */ __libc_lock_define (, __lock) /* Mutex lock for this structure. */ }; typedef struct __dirstream DIR;
2.5 读目录
在进行读目录项操作的时候,会改变目录文件的当前读写位置。linux下使用readdir函数来读取目录中的内容。
readdir函数的原型: #include <dirent.h> struct dirent *readdir(DIR *dirp); 参数dirp:要读取的目录的DIR结构指针。 函数返回一个dirent类型的指针,指针中存放着每个文件的目录项,如果目录到达结尾或者发生错误,readdir函数返回NULL。 如果要判断readdir是因为目录到达结尾还是发生错误,需要通过errno变量来判断。
2.6 得到目录文件的读写位置
对目录的读写操作会影响到目录文件的当前位置,同样,目录文件也可以像普通文件一样被定位。linux下使用telldir函数得到当前目录文件的读写位置。telldir函数的原型:
#include <dirent.h> long telldir(DIR *dirp); 参数dirp:需要得到读写位置的目录的DIR结构指针。 函数执行成功返回目录文件当前的读写位置,失败返回-1。
2.7 定位目录
目录文件可以随意更改文件当前的读写位置,在linux下使用seekdir函数来实现文件定位的功能。seekdir函数的原型:
#include <dirent.h> void seekdir(DIR *dirp, long offset); 参数dirp :需要定位的目录的DIR结构指针。 参数offset :要定位的偏移值。(如果该值超过了有效的目标文件的范围,seekdir函数将会出错) 函数无返回值。
注意: 打开一个目录文件,一个目录项占用12个字节,向后偏移24个字节就可以跳过.文件和..文件两个目录项。
2.8 回卷目录文件
若想重新读取文件目录中的内容又想部关闭目录文件,可以使用回卷函数rewinddir,使得目录文件的读写位置回到文件的起始处。rewinddir函数的原型:
#include <sys/types.h> #include <dirent.h> void rewinddir(DIR *dirp); 参数dirp:需要回卷的目录的DIR结构指针。 函数无返回值。
3 进程的工作目录
每个进程都有一个工作目录,如果程序使用了相对路径,进程以当前目录为起点搜索相对路径。shell解释器解释的每一个命令实际上都是一个程序,这些程序大部分存储在/bin文件夹下,但是里面没有cd这个程序,也就是说,cd的实现方法和其他命令不同其他的命令可以独立于shell而存在,但是cd命令不可以。
注意:因为shell会调用fork函数创建一个子进程来执行命令,如果在子进程中调用chdir函数改变工作目录不会影响到父进程shell。
3.1 改变工程的工作目录
linux下使用chdir函数实现改变进程的工作目录。chdir函数的原型:
#include <unistd.h> int chdir(const char *path); int fchdir(int fd); 参数path:打算改变的新工作目录的路径名。 参数fd:新路径名的文件描述符 函数执行成功返回0,失败返回-1。
程序执行后相当于cd命令,但是只对本身进程以及自己创建的子进程有效,对父进程无效。
fchdir 用来将当前目录改为参数fd(文件描述符)指定的目录。
3.2 得到进程的当前工作目录
使用pwd可以查看当前的工作目录,在linux下使用getcwd函数获取当前进程的工作目录。getcwd函数的原型:
#include <unistd.h> char *getcwd(char *buf, size_t size); 参数buf :存储当前路径名的缓冲区,属于值结果参数。 参数size:缓冲区的大小(路径名+‘\0’的长度),一般不要定义太小。 函数执行成功则返回缓冲区的地址,失败返回NULL。
3.3 子进程工作目录对父进程的影响
子进程中改变工作目录不会对父进程造成影响,但是会对自己的子进程造成影响,自己的子进程会继承父进程的工作目录。对于cd命令,它是直接实现在shell中的,因此如果在本身的进程中想要改变工作目录不要使用system(“cd”)命令。