UNIX 环境高级编程 文件和目录

函数stat , fstat , fstatat , lstat

stat函数返回与此文件有关的信息结构。

fstat函数使用已打开的文件描述符(而stat则使用文件名)

fstatat函数 为一个相对于当前打开目录的路径名返回文件信息。

lstat函数返回该符号链接的有关信息,而不是该符号链接引用的文件的信息。

使用stat最多的地方可能就是 ls -l 命令。

st_mode  与 S_IFMT 进行 与 运算 在与 S_IFXXX常量相比较,来判断类型。

文件类型

1.普通文件: 无论是文本文件还是二进制文件对UNIX内核来说没有区别。

2.目录文件: 这种文件包含了其他文件的名字以及指向与这些文件有关信息的指针。

3.块特殊文件: 提供对设备带缓冲的访问。

4.字符特殊文件: 提供对设备不带缓冲的访问

5.FIFO : 用于进程通信, 称之为 管道

6.socket:用于网络通信

7.符号链接:这种文件指向另一个文件

文件类型信息包含在stat结构的 st_mode 成员中。

设置用户ID 和 设置组ID

实际用户ID :

我们实际上是谁

实际组ID     :

有效用户ID:

用于文件访问权限检查

有效组ID    :

保存的设置用户ID:

由exec函数保存

保存的设置组ID   :

实际用户ID和组ID : 表示我们究竟是谁。取自口令文件中的登陆项

有效用户ID,有效组ID ,附属组ID : 决定了我们的文件访问权限

保存的设置用户ID和设置组ID : 执行一个程序时,包含了有效用户ID和有效组ID的副本。(8.11节详细说明)

通常    有效用户ID等于实际用户ID。有效组ID等于实际组ID

每个文件有一个所有者和组所有者。 分别由 stat结构中的 st_uid 和 st_gid指定。

执行程序时,进程的 有效ID 通常就是 实际ID。

但是,可以在文件模式字(st_mode)中设置一个特殊标识,含义是“当执行此文件时,将进程的有效用户ID设置为文件所有者的用户ID(st_uid),同样可以设置组ID。

在文件模式字中的这两位,被称为 设置用户ID(set-user-id) 和 设置组ID(set-group-id) 位。

例如:

若文件的所有者是root,并且设置了该文件的设置用户ID位,那么该程序由一个进程执行时,该进程具有root权限。无论执行此进程的实际用户ID是什么都是这样。

设置用户ID 和 设置组ID 都包含在文件的 st_mode 值中。 可以分别用常量 S_ISUID 和 S_ISGID 测试。

文件访问权限

st_mode 也包含对文件的访问权限位 。

每个文件有 9 个权限位, 分成 3 类:

S_IRUSR          用户读

S_IWUSR         用户写

S_IXUSR          用户执行

S_IRGRP          组读

S_IWGRP         组写

S_IXGRP          组执行

S_IROTH          其他读

S_IWOTH         其他写

S_IXOTH          其他执行

用户指的是 文件所有者(owner) 。

  • 用名字打开文件时,对该名字包含的没一个目录,包括可能隐含的当前工作目录 都应具有执行权限。对于目录的读权限,允许我们获取该目录中所有文件名的列表。对目录的执行权限可使我们通过该目录,进而搜索该目录,寻找一个特定的文件名。
  • 对于一个文件的读权限,决定了我们能否打开现有文件进行读操作。与 open函数的 O_RDONLY 和 O_RDWR标志相关。
  • 对于一个文件的写权限,决定了我们能否打开现有文件进行写操作。与 open函数的 O_WRONLY 和 O_RDWR标志相关。
  • 为了在 open函数中对一个文件指定 O_TRUNC标志,必须对该文件具有写权限。
  • 为了在一个目录中创建一个新文件,必须对该目录具有写权限和执行权限。
  • 问了删除一个现有文件,必须对包含该文件的目录具有写权限和执行权限。对该文件本身不需要有读,写权限。
  • 如果用 7 个 exec函数中的任何一个执行某个文件,都必须对该文件具有执行权限。并且该文件必须是一个普通文件。

进程每次打开,创建或删除一个文件时,内核就进行文件访问权限测试。

测试分为4步,顺序执行:

1.若 有效ID 是 root , 则允许访问。

2.若 有效ID 等于 所有者ID 的情况下。 那么如果所有者适当的访问权限被设置则允许访问,否则拒绝。

3.若 有限组ID 或 进程的附属组ID 之一 等于文件的组ID。如果适当的访问权限被设置,则允许访问,否则拒绝。

4.若 其他用户 适当的访问权限位被设置,则允许访问, 否则拒绝。

新文件和目录的所有权

新文件的用户ID设置为进程的有效用户ID。

关于组ID,可选择下列其一作为新文件的组ID:

1.新文件的组ID可以是进程的有效组ID。

2.新文件的组ID可以是他所在目录的组ID。

函数 access 和 faccessat

内核以 进程的有效用户ID 和 有效组ID 为基础测试访问权限测试。

如果希望按 实际用户ID 和 实际组ID 来测试访问能力。既可以用 access 和 faccessat 函数来进行访问权限测试(该测试与默认的一样,但将 有效ID 改为 实际ID)

int access ( const char * pathname , int mode );

int faccessat ( int fd , const char * pathname , int mode , int flag );

若成功返回 0 , 否则返回-1

如果测试文件是否已存在,mode 为 F_OK;否则 mode 是下列常量的 按位或:

R_OK      测试读权限

W_OK     测试写权限

X_OK      测试执行权限

函数 umask

mode_t umask ( mode_t cmask )

相当于设置创建文件时,默认的权限。

cmask 由 9个访问权限位 中的 若干个 ”或“ 构成。

在文件模式创建屏蔽字中为1的位,在文件mode中的相应位会被关闭。

若希望任何用户都能读文件,则应将umask设置为0。

函数 chmod , fchmod , fchmodat

用来改变现有文件的访问权限

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

int fchmod ( int fd , mode_t mode );

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

chmod函数 在指定的文件上进行操作, fchmod对已打开的文件进行操作 , fchmodat函数的fd参数可以指定相对的目录。若pathname为绝对路径,或fd参数取值为AT_FDCWD的时候与chmod是相同的。

fchmodat函数的flag参数 可以用来改变fchmodat的行为,设置AT_SYMLINK_NOFOLLOW标志时,fchmodat 不会跟随符号链接。

为了改变一个文件的权限位 , 进程的有效ID必须等于文件的所有者ID , 除非该进程具有ROOT权限。

参数mode是如下常量按位或:

S_ISUID                    执行时设置用户ID

S_ISGID                    执行时设置组ID

S_ISVTX                    保存正文(黏着位)

S_IRWXU                    用户(所有者)读,写,执行

S_IRUSR                用户(所有者)读

S_IWUSR               用户(所有者)写

S_IXUSR                 用户(所有者)执行

S_IRWXG                    组读,写,执行

S_IRGRP                组读

S_IWGRP               组写

S_IXGRP                组执行

S_IRWXO                    其他读,写,执行

S_IROTH                其他读

S_IWOTH               其他写

S_IXOTH                其他执行

只有root才能设置黏着位。

新创建文件的组ID可能不是调用进程所属的组。也可能是父目录的组ID。人如果新文件的组ID不等于进程的有效组ID或者进程附属组ID的一个,而且进程没有root权限,那么设置组ID会被自动关闭。防止用户创建一个设置组ID文件。

黏着位

如果一个可执行程序被设置了黏着位,那么当程序第一次被执行,在终止的时候,程序正文部分的一个副本仍被保存在交换区。使得下次执行的时候能较快将其载入内存。

如果对一个目录设置了黏着位,那么只有对该目录具有写权限的用户并且满足下列条件之一,才能删除或重命名该目录下的文件:

  • 拥有此文件
  • 拥有此目录
  • 是超级用户

函数 chown , fchown , fchownat , lchown

可用于更改用户ID和组ID。如果参数owner或group中的任意一个是-1,则对应的ID不变。

int chown ( const char *pathname , uid_t owner , gid_t group)

int fchown  ( int fd , uid_t owner , gid_t group )

int fchownat ( int fd , const char *pathname , uid_t owner , gid_t group , int flag )

int lchown ( const char *pathname , uid_t owner , gid_t group )

在符号链接的情况下, lchown 和 设置了 AT_SYMLINK_NOFOLLOW标志的fchownat 更改符号链接本身的所有者。

fchown函数改变fd参数指向的打开文件的所有者。

文件长度

stat结构成员 st_size 表示 以字节为单位的文件的长度。只对普通文件,目录文件,符号链接有意义。

对于普通文件,长度可为0

对于目录文件,文件长度一般是一个数(如16或512)的倍数。

对于符号链接,文件长度就是文件名中的实际字节数。

文件的空洞

空洞是由所设置的偏移量超过文件尾端,并写入了某些数据后造成的。

对于没有写过的字节位置,read函数读到的字节是0。

如果使用实用程序(如cat)复制这个文件,那么所有空洞将被填满。其中所有书记数据字节皆填写为0。

文件截断

为了截断文件可以调用函数 truncate 和 ftruncate

int truncate ( const char *pathname , off_t length );

int ftruncate ( const char *pathname , off_t length);

这两个函数将一个现有文件长度截断为length。如果文件长度大于length,则超过length以外的数据就不能再访问。如果以前的长度小于length,文件长度将增加。

文件系统

书113页

函数 link , linkat , unlink , unlinkat , remove

创建一个指向现有文件的链接的方法是使用 link 函数 或 linkat 函数。

int link (const char *existingpath , const char *newpath )

int linkat ( int efd , const char *existingpath , int nfd , const char *newpath , int flag )

这两个函数创建一个新目录项newpath,它引用现有文件existingpath。如果 newpath已存在,则返回出错。只创建newpath中的最后一个分量,路径中的其他部分应当已经存在。

对于linkat函数,现有文件是通过efd和existingpath参数指定的,新的路径名通过nfd和newpath参数指定。

当有文件是符号链接时,有flag来控制linkat函数指向 现有符号链接的链接 还是 创建指向现有符号链接所指向的文件的链接(AT_SYMLINK_FOLLOW )

创建新目录项和增加链接计数应当是一个原子操作。

为了删除一个现有的目录项,可以调用unlink函数

int unlink ( const char *pathname )

int unlinkat ( int fd , const char *pathname , int flag)

这两个函数删除目录项 , 并将由 pathname 所引用文件的链接计数减1

只有当链接计数为0,该文件的内容才可被删除,如果有进程打开了该文件,其内容也不能删除。

如果 flag 参数设置为 AT_REMOVEDIR 。 函数可以类似于 rmdir 一样删除目录。

也可以用 remove 函数解除一个文件或目录的链接。 对于文件 remove的功能与unlink功能相同。对于目录 remove 与 rmdir 相同

int remove ( const char *pathname )

函数 rename 和 renameat

文件或目录可以用rename函数或者renameat函数进行重命名

根据oldname是指文件,目录还是符号链接。有几种情况需要加以说明。

  1. 如果oldname指的是一个文件,那么为该文件或符号链接重命名。在这种情况下,如果newname已存在,则它不能则它不能引用一个目录。如果newname不是一个目录,则先将该目录项删除然后将oldname重名为newname。对于包含oldname和newname的目录,调用进程需要具有写权限。
  2. 如果oldname指的是一个目录,那么为该目录重命名。如果newname已存在,则它必须引用一个目录,而且该目录应当是空目录(该目录中只有 . 和 ..)。如果newname存在(空目录),则先将其删除,然后将oldname重命名为newname。另外,当为一个目录重命名时,newname 不能包含 oldname作为其路径前缀。
  3. 如果oldname或newname引用符号链接,则处理的是符号链接本身,而不是他所引用的文件。
  4. 不能对 . 和 .. 重命名
  1. 如果oldname 和 newname 引用同一个文件。则函数不做任何更改而成功返回。

符号链接

符号链接是对一个文件的间接指针,与硬链接有所不同,硬链接直接指向文件的i节点。

  • 硬链接通常要求链接和文件位于同一文件系统中。
  • 只有ROOT才能创建指向目录的硬链接

符号链接已经它指向和中对象并无任何文件系统的限制。任何用户都可以创建指向目录的符号链接。符号链接一般用于将一个文件或整个目录结构移至系统中另一位置。

当使用以名字引用文件的函数时,应当了解该函数是否处理符号链接。

创建和读取符号链接

可以用 symlink 或 symlinkat 函数创建一个符号链接。

int symlink ( const char * actualpath , const char *sympath ) ;

int symlinkat ( const char *actualpath , int fd , const char *sympath) ;

函数创建一个指向 actualpath 的新目录项 sympath。创建符号链接时,不要求 actualpath已存在。并且不需要位于同一文件系统。

由于open函数跟随符号链接,使用 readlink 和 readlinkat 函数 可以打开该链接本身。

ssize_t readlink ( const char *restrict pathname , char *restrict buf , size_t bufsize ) ;

ssize_t readlinkat ( const char *restrict pathname , int fd , char * restrict buf , size_t bufsize );

这两个函数组合了open,read,close的所有操作。如果成功则返回读入buf的字节数。

文件时间

在 stat 结构中,对每个文件维护了 3 个 时间字段。


字段


说明


例子


ls 选项


st_atim


文件数据最后的访问时间


read


-u


st_stim


文件数据的最后修改时间


write


默认


st_ctim


i节点状态的最后更改时间


chown,chmod


-c

函数 futimens , utimensat 和 utimes

可以用来修改一个文件的访问时间和修改时间。

int futimens ( int fd , const struct timespec times[2] ) ;

int utimensat ( int fd , const char *path, const struct timespec times[2] , int flag );

times数组的第一个元素包含访问时间,第二个元素包含修改时间。

时间戳可按下列4种方式之一进行指定:

函数 mkdir, mkdirat , rmdir

mkdir 和 mkdirat 用来创建目录 , rmdir 用来删除目录

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

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

int rmdir ( const char *pathname );

读目录

DIR *opendir ( const char *pathname );

DIR *fdopendir ( int fd );

struct dirent *readdir ( DIR *dp );

void rewinddir ( DIR *dp )

int closedir (DIR *dp )

long telldur (DIR *dp )

void seekdir ( DIR *dp , long loc )

在头文件 <dirent.h> 中的dirent结构与实现有关。

至少包含两个成员:

ino_t d_ino;     //i-node number

char d_name[];

函数 chdir , fchdir , getcwd

每个进程都有一个当前工作目录,此目录是所有相对路径名的起点。

int chdir ( const char *pathname );

int fchdir ( int fd );

两个函数分别用pathname 和 打开文件描述符 指定新的当前工作目录。

当前工作目录是进程的一个属性,所以它只影响调用 chdir 的进程本身。

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

该缓冲区必须有足够的长度容纳绝对路径名加上终止null字节。

时间: 2024-12-28 12:05:15

UNIX 环境高级编程 文件和目录的相关文章

unix环境高级编程——文件i/o

一.文件描述符 对于内核,通过文件描述符来管理文件.什么是文件描述符? 在unix中,用open或者create建立几个文件时候,内核向进程返回一个整数,用来记录此文件. 以后对文件进行操作的时候,就用此文件描述符做引用. 二.open函数 open函数用于建立一个文件,函数返回文件描述符. [cpp] view plaincopy #include <fcntl.h> int open(const char *pathname, int flag); int open(const char 

UNIX环境高级编程 文件I/O

大多数文件I/O 只需要用到 5个函数 :    open , read , write , lseek , close 本章描述的都是不带缓冲的I/O(read write 都调用内核中的一个系统调用) 文件描述符 对于内核而言,所有文件都通过文件描述符引用. 文件描述符是一个非负整数 打开或创建一个新文件时,内核都将返回一个文件描述符 文件描述符的范围是 0~OPEN_MAX-1 函数 open 和 openat 调用 open 或 openat 可以打开或创建一个文件 int open(

unix环境高级编程——文件操作

一.阻塞io操作和非阻塞io操作 对于阻塞io操作:当cpu得不到需要的文件数据的时候,则将自己挂起,直到有了数据,才能执行下面的操作. 非阻塞io:  当cpu得不到文件时候,其一直在轮询cpu,直达得到数据. 二.文件描述符 对于内核而言,所有打开的文件,都用文件描述符来引用.所有的文件操作都是由内核态完成的,当一个用户的进程创建一个文件时候,内核会给其返回一个文件描述符. 而当用户进程,对文件进行各类读写操作的时候,也是给内核传入文件描述符.这样,就可以对其文件进行操作了. 三.open函

Unix 环境高级编程-----文件操作函数

1. open()   O_RONLY O_WONLY O_RDWR 返回值为 文件描述符fd 2. creat()   创建新文件,这个函数的产生是因为最开始open函数没有O_CREAT 功能,所以单独开发的该函数 注:不是create,没有e 3. read()  读取fd对应的文件内容 4. write() 写信息到fd对应的文件中 5. lseek()  设置当前文件偏移量 SEEK_SET, SEEK_CUR, SEEK_END 6. close()  关闭fd对应的描述符 小结:

Unix环境高级编程

Advanced Programming in the UNIX Environment Second Edition Unix 环境高级编程 第二版 目录: 第一章:UNIX基础知识 第二章:UNIX标准化及实现 第三章:文件I/O 第四章:文件和目录 第五章:标准I/O库 第六章:系统数据文件盒信息 第七章:进程环境 第八章:进程控制 第九章:进程关系 第十章:信号 第十一章:线程 第十二章:线程控制 第十三章:守护线程 第十四章:高级I/O 第十五章:进程间通信 第十六章:网络IPC:套接

UNIX环境高级编程---标准I/O库

前言:我想大家学习C语言接触过的第一个函数应该是printf,但是我们真正理解它了吗?最近看Linux以及网络编程这块,我觉得I/O这块很难理解.以前从来没认识到Unix I/O和C标准库I/O函数压根不是一码事.Unix I/O也叫低级I/O,也叫Unbuffered I/O,是操作系统内核部分,也是系统调用:而C标准I/O函数相对也成Buffered I/O,高级I/O,一般是为了效率考虑对这些系统调用的封装.以前使用getchar()经常为输入完后的回车而出错.那是不理解标准I/O实现时的

《Unix环境高级编程》读书笔记 第3章-文件I/O

1. 引言 Unix系统的大多数文件I/O只需用到5个函数:open.read.write.lseek以及close 本章描述的函数经常被称为不带缓冲的I/O.术语不带缓冲指的是在用户的进程中对其不会自动缓冲,每个read和write都调用内核中的一个系统调用.但是,所有磁盘I/O都要经过内核的块缓存区(也称为内核的缓冲区高速缓存).唯一例外的是对原始磁盘设备的I/O. 2. 文件描述符 对于内核而言,所有打开的文件都通过文件描述符引用.文件描述符是一个非负整数,其变化范围是0~OPEN_MAX

UNIX环境高级编程笔记之文件I/O

一.看图说话 一图胜过千言,看图! 二.唠一唠 在写之前,先唠几句,<UNIX环境高级编程>,简称APUE,这本书简直是本神书,像我这种小白,基本上每看完一章都是“哇”这种很吃惊的表情.其实大概三年前,那会大三,我就买了这本书,也看过一些,但好像没有留下什么印象,今天再看,依然觉得像新的一样.很大的原因我想是一直以来都在用windows(用windows做开发为什么学不到真正的技术,我想大家都懂的),当然知识结构不完整,学习能力这些就不说了.所以,对于那些致力于想在Linux下做开发的人来说,

unix环境高级编程(第三版)中apue.h文件的配置问题

最近刚开始学习unix环境高级编程(第三版),其中有个作者自己写的apue.h文件,在这归总下相应的配置方法,希望对有需要的朋友们有所帮助 首先http://www.apuebook.com/code3e.html 上去下载相应的压缩包,注意自己书的版本. 下载完成之后,鉴于大多数朋友学习linux都是基于虚拟机的,所以顺便附上虚拟机与本地主机传输文件的方式 首先下载SSH Secure Shell 这个工具,然后直接点击quick connect, 弹出如下界面,输入虚拟机的ip地址,和登录用