UNIX高级环境编程(5)Files And Directories - 文件相关时间,目录文件相关操作

?1 File Times

每个文件会维护三个时间字段,每个字段代表的时间都不同。如下表所示:

字段说明:

  • st_mtim(the modification time)记录了文件内容最后一次被修改的时间。
  • st_ctim(the changed-status time)记录了文件的i-node最后一次被修改的时间,如修改文件权限位,修改文件所有者ID,修改关联到该文件的link数目。

i-node中的信息和文件的实际内容是分离的,所以当更新i-node时,需要更新的时st_ctim(the changed-status time),而不是st_mtim(the modification time)。

命令ls各个参数排序时使用的时间标准:

  • 参数-l和-t:使用st_mtim(the modification time of the file)
  • 参数-u:使用st_atim(the last-access time of file)
  • 参数-c:使用st_ctim(the last-change time of i-node status)

下表总结了会影响这三种时间的部分函数,在看到表之前,我们需要了解:

  • 目录就是一个文件,该文件包含了目录索引:目录包含的文件名列表和关联的i-node数目。
  • 新建,删除或者修改目录中包含的索引,会更新该目录的三种时间。
  • 例如:新建一个文件,会更新包含该文件的目录中相应的时间,和该文件的i-node。而读写文件,则只会影响该文件的时间,而不会影响包含该文件的目录。

?

2 futimens,utimensat和utimes函数

函数作用:修改文件的access time和modification time。

函数声明:

#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);

参数说明:

strcut timespec结构体中至少包含两个时间字段:time_t tv_sec(秒)和long tv_nsec(毫微秒)。

数组times包含两个时间:第一个元素是access time,第二个元素是modification time。

函数行为受参数times的取值影响,需要参考时可以自行查询。

?

同时,执行上面的一对函数也会对权限有要求,要求如下:

  • 如果times参数为非空指针,或者tv_nsec字段为UTIME_NOW,则调用该函数要求:进程的the effective user ID必须等于文件所有者的ID,或进程有对该文件的写权限,或者进程是一个管理员(super user)进程。
  • 如果times参数为非空指针,并且tv_nsec字段的取值不是UTIME_NOW和UTIME_OMIT,则调用该函数要求:进程的effective user ID必须等于该文件的所有者ID,或者进程是一个管理员进程。和上面一条不同的一点是,此种情况下仅仅对文件有写权限是不够的。
  • 如果times参数为非空指针,并且tv_nsec字段的取值为UTIME_OMIT,则没有权限检查。

函数细节:

  • 函数futimens要求文件必须打开,获取fd之后才可以修改文件相应的时间。
  • 函数futimensat则可以通过制定一个文件的绝对路径来修改文件的时间属性,而此时fd参数被忽略。
  • 同时,当目标文件为一个符号链接时,默认的函数行为是追踪该链接至真实文件,参数flag可以修改该默认行为,flag取值为AT_SYMLINK_NOFOLLOW时,修改的文件为该符号链接本身。

函数utimes通过制定一个文件路径pathname,来修改文件的相关时间。

函数声明:

#include <sys/time.h>

int utimes(const char *pathname, const struct timeval times[2]);

strcut timeval {

? ? time_t tv_sec;

? ? long tv_usec;

};

我们不能指定修改时间字段st_ctim(changed-status time),但是在调用utimes函数时,该字段被自动更新。

Example:

?例子情景:

  • 通过open的O_TRUNC选项来使得一个文件为空,长度为0;
  • 为了不修改文件的access time和modification time,先使用stat函数获取修改之前的时间;
  • 截取文件长度至0,然后使用futimes函数重置文件时间字段为修改前的时间。

Code:

#include "apue.h"

#include <fcntl.h>

?

int

main(int argc, char *argv[])

{

? ? int ? ? ? ? ? ? i, fd;

? ? struct stat ? ? statbuf;

? ? struct timespec times[2];

?

? ? for (i = 1; i < argc; i++) {

? ? ? ? if (stat(argv[i], &statbuf) < 0) {? /* fetch current times */

? ? ? ? ? ? err_ret("%s: stat error", argv[i]);

? ? ? ? ? ? continue;

? ? ? ? }

? ? ? ? if ((fd = open(argv[i], O_RDWR | O_TRUNC)) < 0) { /* truncate */

? ? ? ? ? ? err_ret("%s: open error", argv[i]);

? ? ? ? ? ? continue;

? ? ? ? }

? ? ? ? times[0] = statbuf.st_atim;

? ? ? ? times[1] = statbuf.st_mtim;

? ? ? ? if (futimens(fd, times) < 0)? ? ? ? /* reset times */

? ? ? ? ? ? err_ret("%s: futimens error", argv[i]);

? ? ? ? ? ??continue;

? ? ? ? }

? ? ? ? times[0] = statbuf.st_atim;

? ? ? ? times[1] = statbuf.st_mtim;

? ? ? ? if (futimens(fd, times) < 0)? ? ? ? /* reset times */

? ? ? ? ? ? err_ret("%s: futimens error", argv[i]);

? ? ? ? close(fd);

? ? }

? ? exit(0);

}

运行结果:(由于我用的mac os不支持的原因,并没有编译成功该例,所以直接用书上的结果)

从结果中可以看到,last-modification time和last-access time没有改变,而changed-status time发生了改变。

?

3 mkdir、mkdirat和rmdir函数

mkdir和mkdirat函数创建一个新的空的目录,rmdir函数用来删除目录。

函数声明:

#include <sys/stat.h>

int mkdir(const char* pathname, mode_t mode);

int mkdirat(int fd, const char* pathname, mode_t mode);

参数mode取值为基于在前面一篇提到过的文件创建掩码(file creation mask of process)。

?rmdir函数用来删除一个空的目录,空目录中只含有两个记录(dot和dot-dot)。

函数声明:

#include <sys/stat.h>

int rmdir(const char* pathname);

上面的三个函数,调用成功返回0,失败返回-1.

?

4 读取目录文件(reading directories)

对于目录文件,只要有相应的权限,任何人都可以读取目录文件的内容。但是为了保证系统正常工作,只有内核可以对目录文件执行写操作。

在前面的章节中,我们了解到,目录文件的写权限位和执行权限位决定了我们是否可以在该目录下创建和删除文件,但是它们并不能允许我们写目录文件本身。

读文件操作依赖于系统实现。相关函数声明如下:

#include <dirent.h>

DIR *opendir(const char* pathname);

DIR *fdopendir(int fd); ? // return : pointer if OK, NULL on error

struct dirent *readdir(DIR *dp); ? ?// return : pointer if OK, at end of directory or error

void rewinddir(DIR *dp);

int closed(DIR *dp); ? ?// return : 0 if OK, -1 on error

long telluride(DIR *dp); ? ?// return : current location in directory associated with dp

void seekdir(DIR *dp, long loc);

细节说明:

  • 结构体struct dirent定义在头文件<dirent.h>中,依赖于具体的实现,但是至少包含两个成员:ino_t d_ino(inode number)和char d_name[](null-terminated filename)。
  • 结构体DIR会在后面的章节详细讨论。
  • opendir和fdopendir返回一个指向DIR结构体的指针,该指针会用在另外五个函数中。
  • readdir返回目录的第一条记录,当readdir使用fdopendir返回的指针时,读取的记录取决于传给fdopendir的参数fd的偏移量。

?

5 chdir、fchdir和getcwd函数

每个进程都有一个工作目录(current working directory),工作目录是进程的一个属性。

函数声明:

#include <unistd.h>

int chdir(const char* pathname);

int fchdir(int fd);

?比较简单,不做赘述。

如果我们希望获取当前工作目录的完整信息(绝对路径),无法直接从内核获取,因为内核只是维护了一个指向该目录的指针。

如果我们要获取绝对路径,需要通过dot-dot进入上级目录,读取该级目录的信息,获取目录名,然后再依次访问上级目录一直到根目录,最后拼出绝对路径。

函数getcwd就是实现了这个功能。

函数声明:

#include <unistd.h>

char* getcwd(char *buf, size_t size);

需要注意的一点是,这里的buf需要足够大,size为它的大小。buf需要容纳绝对路径加上一个字节的null终止符。

?Example:

函数功能:修改当前工作目录(chdir)并获取该工作目录绝对路径(getcwd)

Code:

#include "apue.h"

?

int

main(void)

{

? ? char? ? *ptr;

? ? size_t? ? ? size;

?

? ? if (chdir("/usr/") < 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);

}

切换工作目录到/usr/并打印工作目录。

?

6 文件权限位总结

如下表所示

??

7 小结

这一章包括了三篇内容,主要围绕stat函数,了解了:

  • stat结构体中的各个成员;
  • UNIX文件和目录的各个属性;
  • 操作文件和目录的常用函数;
  • UNIX文件系统的组织结构。

下一章我们将会学习标准IO库。?

?

参考资料:

《Advanced Programming in the UNIX Envinronment 3rd》

?

时间: 2024-08-24 04:35:45

UNIX高级环境编程(5)Files And Directories - 文件相关时间,目录文件相关操作的相关文章

《unix高级环境编程》终端 I/O——终端 IO 基本概述

终端基本概念 终端 IO 是一种字符型设备,终端特殊设备文件一般有以下几种: 串行端口终端:是使用计算机串行端口连接的设备,计算机把每个串行端口都看作是一个字符设备.串行端口所对应的设备名称 /dev/ttySn(n表示从0开始的整数): 伪终端:是成对的逻辑终端设备,例如 /dev/ptyp3 和/ dev/ttyp3(在设备文件系统中分别是 /dev/pty/m3 和/ dev/pty/s3 ),它们与实际物理设备并不直接相关: 控制终端:是当前进程的控制终端的设备特殊文件 /dev/tty

UNIX高级环境编程(4)Files And Directories - umask、chmod、文件系统组织结构和链接

本篇主要介绍文件和文件系统中常用的一些函数,文件系统的组织结构和硬链接.符号链接. 通过对这些知识的了解,可以对Linux文件系统有更为全面的了解. ? 1 umask函数 之前我们已经了解了每个文件与权限相关的9个位(bit),我们现在来了解一下当每个进程创建文件时默认会设置该文件的文件权限(the file mode creation mask). umask函数设置该进程默认创建文件的权限掩码(the file mode creation mask),并且返回之前的权限掩码值. #incl

UNIX高级环境编程(3)Files And Directories - stat函数,文件类型,和各种ID

在前面的两篇,我们了解了IO操作的一些基本操作函数,包括open.read和write. 在本篇我们来学习一下文件系统的其他特性和一个文件的属性,涉及的函数功能包括: 查看文件的所有属性: 改变文件所有者: 改变文件权限: 操作文件夹. 我们还会了解一些文件系统相关的数据结构和符号链接(symbolic link). 1 函数stat.fstat.fstatat.lsat函数 #include <sys/stat.h> int stat(const char *restrict pathnam

UNIX高级环境编程(2)FIle I/O - 原子操作、共享文件描述符和I/O控制函数

引言: 本篇通过对open函数的讨论,引入原子操作,多进程通信(共享文件描述符)和内核相关的数据结构. 还会讨论集中常见的文件IO控制函数,包括: dup和dup2 sync,fsync和fdatasync fcntl ioctl /dev/fd ? 一.文件共享 这里所说的文件共享主要指的是进程间共享打开的文件. 这一节主要讨论文件在进程间共享的理论基础和数据结构,不涉及具体的技术实现,不同的系统可能会有不同的实现. 每一个打开的文件,涉及内核中的三种数据结构,这三种数据结构也是文件在进程间共

UNIX高级环境编程(17)文件系统 &lt; 雨后 &gt;

来点绿色放松一下眼睛吧 :) 文件系统是对文件和目录的组织集合. 一 设备文件 设备文件和系统的某个设备相对应. 设备驱动程序 处理设备的所有IO请求. 提供了一致的API接口,对应于系统调用的open, close, read, write, mmap以及ioctl,屏蔽了底层设备的差异. 设备的类型 字符型设备:基于每个字符来处理请求,例如终端和鼠标.键盘. 块设备:按数据块处理请求,通常数据块是512字节的整数倍.例如硬盘和磁带. 设备ID 每个设备文件都有主.辅设备文件ID.主文件ID标

UNIX高级环境编程(13)信号 - 概念、signal函数、可重入函数

信号就是软中断. 信号提供了异步处理事件的一种方式.例如,用户在终端按下结束进程键,使一个进程提前终止. ? 1 信号的概念 每一个信号都有一个名字,它们的名字都以SIG打头.例如,每当进程调用了abort函数时,都会产生一个SIGABRT信号. 每一个信号对应一个正整数,定义在头文件<signal.h>中. 没有信号对应整数0,kill函数使用信号编号0表示一种特殊情况,所以信号编号0又叫做空信号(null signal). 下面的各种情况会产生一个信号: 当用户在终端按下特定的键时,会产生

UNIX高级环境编程(1)FIle I/O

引言: Unix系统中主要的文件操作包括: open read write lseek close unbuffered IO和standard I/O相对应,后面的章节我们会讨论这两者的区别. 在讨论open函数的时候,会引入原子操作,多进程通信(共享文件描述符)和内核相关的数据结构. 一,文件描述符 对应内核来说,每一个打开的文件都对应一个非负整数. 有三个特殊的文件描述符: 0表示标准输入 1表示标准输出 2表示标准错误输出 对于较新的内核来说(Linux3.2.0,Solaris10等)

UNIX高级环境编程(9)进程控制(Process Control)- fork,vfork,僵尸进程,wait和waitpid

本章包含内容有: 创建新进程 程序执行(program execution) 进程终止(process termination) 进程的各种ID ? 1 进程标识符(Process Identifiers) 每个进程都有一个唯一的标识符,进程ID(process ID). 进程的ID是可重用的,如果一个进程被终止,那么它的进程ID会被系统回收,但是会延迟使用,防止该进程ID标识的新进程被误认为是以前的进程. 三个特殊ID的进程: Process ID 0:调度者进程,内核进程. Process

UNIX高级环境编程(15)进程和内存分配 &lt; 故宫角楼 &gt;

故宫角楼是很多摄影爱好者常去的地方,夕阳余辉下的故宫角楼平静而安详. ? 首先,了解一下进程的基本概念,进程在内存中布局和内容. 此外,还需要知道运行时是如何为动态数据结构(如链表和二叉树)分配额外内存的. 一 进程 1 进程和程序 进程:是一个可执行程序的实例. 程序:包含一系列信息的文件,这些信息描述了如何在运行时创建一个进程.包含如下信息: 二进制格式标识:如最常见的ELF格式. 机器语言指令:对程序算法进行编码. 程序入口地址:标识程序开始执行时的起始指令位置. 数据:程序文件包含的变量