该系列文章总纲链接:专题分纲目录 LinuxC 系统编程​​​​​​​


本章节思维导图如下所示(思维导图会持续迭代):

第一层:

Linux C 系统编程(03) 文件与I/O 文件目录_#include

第二层:

Linux C 系统编程(03) 文件与I/O 文件目录_linux_02


目录操作:目录文件本质上是一个文件,但是实际上其内容又与普通文件不同。目录文件是一种特殊的文件,其内容存储着目录下所有文件的目录项。用户可以像普通文件一样读写目录,不过需要使用一组特殊的系统调用。

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。

注意:

  1. 一个目录总要包含两个默认的目录项,即.(当前目录)和..(上一级目录)。
  2. 在linux下,新创建的子目录会自动继承父目录的设置组ID位,而不是创建进程的组ID。

2.2 删除一个目录 rmdir

linux下使用rmdir函数来创建一个目录。rmdir函数的接口:

#include <unistd.h>
int rmdir(const char *pathname);
参数pathname     :要删除的目录的路径。
函数成功执行返回0,失败返回-1。

注意:

  1. rmdir函数只能删除一个空目录,如果目录非空,则删除出错。(这里的空目录表示目录项只含有.和..文件)
  2. 如果目录的权限中没有写的权限,那么删除目录也会发生错误。

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”)命令。