[APUE]文件和目录(下)
一、mkdir和rmdir函数
#include <sys/types.h>
#include <sys/stat.h>
int mkdir(const char *pathname, mode_t mode);
返回值:成功0,失败-1.
mkdir函数创建一个空目录,.和..目录是自动创建的。所指定的文件存取许可权mode由进程的文件方式创建屏蔽字修改(命令行执行umask可查看) 常见的错误是指定与文件相同的mode(只指定读,写许可权)。但是对于目录来说必须设置一个执行许可权位,以允许存取该目录中的文件名。
用rmdir函数可以删除一个目录
#include <unistd.h>
int rmdir(const char *pathname);
如果调用此函数使目录的连接计数为0并且没有其他进程打开该目录,则释放此目录占用的空间。如果在连接计数为0时,有一个或多个进程打开了该目录,则在此函数返回前删除最后一个连接及.和..项。并且,在此目录中不能再创建新文件。但是在最后一个进程关闭它之前并不释放此目录(即使有进程打开该目录,它们在此目录下,也不能执行其他操作,因为使rmdir函数成功执行,该目录必须是空的)。
二、读目录
对某个目录具有存取许可权的任一用户都可读该目录,但是只有内核才能写目录(防止文件系统发生混乱)。一个目录的写许可权和执行许可权决定了能否在该目录中创建文件和删除文件,但是它们并不表示可以读写文件本身。 目录的实际格式依赖于UNIX的具体实现,早期的系统,如V7结构:每个目录项是16个字节,其中14个字节是文件名,2个字节是i节点编号数。UNIX现在包含了一套与读目录相关的例程,他们是POSIX.1的一部分。
#include <sys/types.h>
#include <dirent.h>
DIR *opendir(const char *pathname); 返回值:成功则为指针,出错则返回NULL
struct dirent *readdir(DIR *dp); 返回值:成功则为指针,若在目录尾或出错则为NULL
void rewinddir(DIR *dp); 该函数用来设置参数dir 目录流目前的读取位置为原来开头的读取位置
int closedir(DIR *dp);
返回值:成功0,失败-1
定义在头文件<dirent.h>中的dirent结构与实现有关,SVR4和4.3+BSD定义此结构至少包含下列两个成员:
struct dirent{
ino_t d_ino; /* i-node number */
char d_name[NAME_MAX+1]; /* null-terminated filename */
}
POSIX.1并没有定义d_ino,因为这是一个实现特征,POSIX.1在此结构中之定义d_name项
SVR4没有将NAME_MAX定义为一个常数--其值依赖于目录所在的文件系统。并且通常可用fpathconf函数取得。在BSD类文件系统中,NAME_MAX的常用值是255.但是文件名是以null字符结束的,所以在头文件如何定义数组d_name并无多大关系 DIR是一个内部结构类似于FILE。 opendir执行初始化操作,使第一个readdir读目录中的第一个目录项,目录中各目录项的顺序与实现有关,它们通常并不按字母顺序排列。 以下是一个简单的文件遍历程序:
三、chdir、fchdir和getcwd函数
每个进程都有一个当前工作目录,此目录是搜索所有相对路径名的起点。
#include <unistd.h>
int chdir(const char *pathname);
int fchdir(int filedes);
返回值:成功0,失败-1.
因为当前工作目录是一个进程的属性,所以它只影响调用chdir的进程本身并不影响其他进程 内核只为每个进程保存当前工作目录的i节点编号以及设备标识,并不保存该目录的完整路径名。getcwd函数可以从当前工作目录开始,找到其上一级的目录,然后读其目录项,直到该目录项中的i节点编号数与当前工作目录i节点编号数相同,这样就找到了其对应的文件,按照这种方法逐层上移直到遇到根,这样就得到了当前工作目录的绝对路径名。
#include <unistd.h>
char *getcwd(char *buf, size_t size);
返回值:成功buf,失败NULL
此函数两个参数,缓存地址buf,缓存的长度size。该缓存必须有足够的长度一容纳绝对路径名加一个null终止符,否则返回出错。
四、特殊设备文件
字符特殊文件:这种文件用于系统中某些类型的设备。
块特殊文件:这种文件典型的用于磁盘设备。系统中的所有设备或者是字符特殊文件,或者是块特殊文件。 st_dev和st_rdev这两个字段经常引起混淆
- 每个文件系统都由其主、次设备号而为人所知。设备号所用的数据类型是借本系统数据类型dev_t。
- 我们通常可以使用两个大多数实现都定义的宏:major和minor来存取主、次设备号。这就意味着我们无需关心这两个数是如何存放在dev_t对象中的。
- 系统中每个文件名的st_dev值是文件系统的设备号,该文件系统包含了该文件名和其对应的i节点。
- 只有字符特殊文件和块特殊文件才有st_rdev值。此值包含该实际设备的设备号 以下程序为每个命令行参数打印设备号,另外如果参数引用的是字符特殊文件或块特殊文件,则也打印该特殊文件的st_rdev值。
#include <sys/types>
#include <sys/stat.h>
#include <stdio.h>
int main(int argc, char *argv[])
{
int i;
struct stat buf;
for (i = 1; i< argc; i++) {
printf("%s: ", argv[i]);
if (lstat(argv[i], &buf) < 0) {
fprintf(strerr, "lstat error");
continue;
}
printf("dev= %d/%d", major(buf.st_dev), minor(buf.st_dev));
if (S_ISCHR(buf.st_mode) || I_ISBLK(buf.st_mode)) {
printf(" (%s) rdev = %d/%d", (S_ISCHR(buf.st_mode)) ? "character" : "block", major(buf.st_rdev), minor(buf.st_rdev));
}
printf("\n");
}
return 0;
}
五、sync和fsync函数
传统的UNIX实现在内核中设有缓冲存储器,大多数磁盘IO都通过缓存进行。当将数据写到文件上时,通常该数据先由内核复制到缓存中,如果该缓存尚未写满,则并不将其排入输出队列,而是等待其写满或者当内核需要重用该缓存以便存放其他磁盘块数据时,再将该缓存排入输出队列,然后待其到达队首时,才进行实际的IO操作。这种输出方式被称之为延迟写。当系统发生故障时这种延迟可能会造成文件更新内容的丢失。为了保证磁盘上实际文件系统与缓存中内容的一致性,UNIX提供了sync和fsync两个系统函数。
#include <unistd.h>
void sync(void);
int fsync(int fileds);
返回值: 成功0,失败-1
sync只是将所有修改过的块的缓存排入写队列,然后就返回,并不等待实际IO操作结束。
系统精灵进程(通常称为update)一般每隔30秒调用一次sync函数。 函数fsync只引用单个文件,它等待IO结束,然后返回。
六、文件存取许可权位小结
以下是所有文件存取许可权位: