Unix环境高级编程学习笔记(二)
第四章 文件和目录
本章将描述文件系统特征和文件性质
1、stat、fstat和lstat函数
原型:#include<sys/stat.h>
int stat(const char* restrict pathname, struct stat *restrict buf);
int fstat(int filedes, struct stat *buf);
int lstat(const char* restrict pathname, struct stat *restrict buf); 若成功返回0,出错返回-1
注:lstat与stat的不同,当命名文件是一个符号链接时,lstat返回符号链接的有关信息,而stat返回引用文件的信息。
struct stat{
mode_t st_mode;
ino_t st_ino;
dev_t st_dev;
dev_t st_rdev;
nlink_t st_nlink;
uid_t st_uid;
gid_t st_gid;
off_t st_size;
time_t st_atime;
time_t st_mtime;
time_t st_ctime;
blksize_t st_blksize;
blkcnt_t st_blocks;
};
2、文件类型:包括普通文件、目录文件、块特殊文件、字符特殊文件、FIFO、套接字、符号链接。 文件类型信息包括在stat结构的st_mode字段,可用以下宏检测文件类型:
S_ISREG():普通文件;S_ISDIR():目录文件;S_ISCHR():字符特殊文件;S_ISBLK():块特殊文件;S_ISFIFO():管道或FIFO;S_ISLNK():符号链接;S_ISSOCK():套接字。 注:头文件:<sys/stat.h> 参数是st_mode
由于POSIX.1允许实现将进程间通信对象(如消息队列和信号量等)表示为文件,所以有一些对应宏用于测是:S_TYPEISMQ():消息队列;S_TYPEISSEM():信号量;S_TYPEISSHM():共享存储对象。注:Linux没有把他们实现为文件,所以没啥用。
3、设置用户Id和组ID
与进程相关的id:实际用户id、实际组id(我们实际上是谁)、有效用户id、有效组id、附加组id(用于文件访问权限检查)、保存的设置用户id、保存的设置组id(由exec函数保存),通常:有效用户id等于实际用户id,有效组id等于实际组id。
每个文件都有所有者(st_uid)和组所有者(st_gid)。可以在st_mode中设置一个特殊标志,使“当执行这个文件时,将进程的有效用户id设置为所有者id”,这叫做设置用户id,同理有设置组id。例如:passwd程序就使用了设置用户id。。。可用S_ISUID和S_ISGID宏测试设置用户id和设置组id。
4、文件访问权限
9个访问权限位:S_IRUSR:用户读;S_IWUER:用户写;S_IXUSR:用户执行; S_IRGRP:组读;S_IWGRP:组写;S_IXGRP:组执行; S_IROTH:其他读 S_IWOTH:其他写 S_IXOTH:其他执行。(注:关于文件访问权限的意义,在之前的博客已写过,在此不再赘述。)
5、新文件和目录的所有权:新文件的用户id设置为进程的有效用户id,组id设置为:(1)进程的有效组id;(2)所在目录的组id。
6、access函数:按实际用户id和实际组id进行访问权限测试。用于验证实际用户能否访问1个文件。
原型:#include<unistd.h> int access(const char* pathname, int mode); mode:R_OK(测试读权限),W_OK(测试写权限),X_OK(测试执行权限),F_OK(测试文件是否存在) 注:这些标志可以进行或运算,来自头文件<unistd.h>
7、umask函数:为进程设置文件模式创建屏蔽字,并返回以前的值,无出错函数。
原型:#include<sys/stat.h> mode_t umask(mode_t cmask) 返回以前的文件模式创建屏蔽字,其中参数cmask是S_IRUSR等9个常量之一或按位或的结果。
说明:任何在文件模式创建屏蔽字中为1的位,在文件mode中的对应位一定被关闭。
8、chmod和fchmod函数:更改现有文件的访问权限
原型:#include<sys/stat.h>
int chmod(const char* pathname, mode_t mode);
int fchmod(int filedes, mode_t mode); 成功返回0,失败返回-1
mode可以使用的参数:1、上文的9个文件访问常量 2、S_IRWXU、S_IRWXG、S_IRWXO、S_ISUID(设置用户id)、S_ISGID(设置组id)、S_ISVTX(保存正位(粘住位))
9、粘住位:如果对一个目录设置了粘住位,则要更名和或删除该目录下的文件必须满足以下条件之一:拥有此文件;拥有此目录;是超级用户。 一般来说,/temp目录就设置了粘住位。
10、chown、fchown和lchown函数:可用于更改文件的用户id或组id
原型:#include <unistd.h>
int chown(const char* pathname, uid_t owner, gid_t group);
int fchown(int filedes, uid_t owner, gid_t group);
int lchown(const char* pathname, uid_t owner, gid_t group); 成功返回0,失败返回-1 lchown:当是符号链接时,lchown改变符号链接本身的所有者,而不是该符号链接指向的文件。
若owner或 group参数的值为-1,则对应的值不变。
11、文件长度:stat结构成员st_size表示以字节为单位的文件长度。此字段只对普通文件、目录文件和符号链接有意义。 stat的st_blksize字段:文件IO较合适的块长度 st_blocks:实际512字节数量。
du -s core : 报告该文件的st_blocks值 wc -c core:用于统计文件中的字节数 (这一段讲述了关于文件空洞问题)
12、文件截短:
原型:#include<unistd.h> int truncate(const char* pathname, off_t length);
int ftruncate(int filedes, off_t length); 成功返回0,失败返回-1
将现有的文件长度截短为length长度,若length长于原来长度,则会产生空洞。
13、文件系统:i节点记录了文件的大部分信息:链接计数(stat的st_nlink成员)、文件类型、文件访问权限等等。只有两项信息放在目录项中:文件名和i节点编号。
两个目录想指向同一个i节点叫做硬链接(nlink_t)、符号链接(S_IFLNK)
14、link、unlink、remove和rename函数
创建一个指向现有文件的链接的方法是使用link函数:#include<unistd.h> int link(const char* existingpath, const char* newpath); 成功返回0,失败返回-1
此函数创建一个新目录项newpath,它引用现有的文件exitingpath(若已存在则报错)。 硬链接
为了删除一个目录项,可以调用unlink函数:#include<unistd.h> int unlink(const char* pathname); 成功返回0,失败返回-1
如果pathname是符号链接,unlink删除符号链接,不会删除引用文件。
我们可以用remove函数解除对一个文件或目录的链接:#include<stdio.h> int remove(const char* pathname); (对文件remove等价于unlink,对目录remove等价于rmdir,这个函数是ISO C指定的)
文件或目录用rename函数更名:#include<stdio.h> int rename(const char* oldname, const char* newname); 成功返回0,失败返回-1
15、符号链接: 硬链接的不足:1)链接和文件必须位于同一文件系统中;2)只有超级用户才能创建指向目录的硬链接 当使用以名字引用文件的函数时,应当了解该函数是否处理符号链接。
16、symlink和readlink函数:
symlink函数创建一个符号链接:#include<unistd.h> int symlink(const char* actualpath, const char* sympath); 创建指向actualpath的新目录项sympath,并不要求actualpath已经存在。
因为open函数跟随符号链接,所以要有一种方法打开该链接本身:#include<unistd.h> ssize_t readlink(const char* restrict pathname, char* restrict buf, size_t bufsize);
17、文件时间:st_atime:文件数据最后访问时间; st_mtime:文件数据最后修改时间; st_ctime:i节点状态最后更改时间。
18、utime函数:更改一个文件的访问和修改时间。
原型:#include<utime> int utime(const char* pathname, const struct utimbuf *times);
struct utimbuf{
time_t actime;
time_t modtime;
} times是NULL则设置当前时间。
19、mkdir和rmdir函数:创建目录和删除目录
原型:#include<sys/stat.h> int mkdir(const char* pathname, mode_t mode);
#include<unistd.h> int rmdir(const char* pathname); 用于删除一个空目录。
20、读目录:
#include<dirent.h>
DIR *opendir(const char* pathname); 返回值:若成功返回指针,若出错返回null
struct dirent *readdir(DIR* dp); 返回值:若成功返回指针,若出错返回null
void rewinddir(DIR* dp);
int closedir(DIR* dp); 返回值:若成功返回0,若出错返回-1
long telldir(DIR *dp); 返回值:与dp关联的目录中的当前位置
void seekdir(DIR* dp, long loc);
struct dirent{
ino_t d_ino;
char d_name[NAME_MAX + 1];
DIR结构是内部结构,上述6个函数用这个内部结构保存当前正被读的目录的有关信息。
}
注意:程序清单4-7用到了函数path_alloc(),但是程序中没定义,编译无法通过。我的解决方法是自己定义一个path_alloc(),简单方便。
char*path_alloc(int* size)
{
char *p = NULL;
if(!size) return NULL;
p = malloc(256);
if(p)
*size = 256;
else
*size = 0;
return p;
} 程序清单4-7用到了opendir,readdir,closedir。
21、chdir、fchdir和getcwd函数
进程通过调用chdir和fchdir函数可以更改当前工作目录:#include<unistd.h> int chdir(const char* pathname); int fchdir(int filedes); 这两个函数分别用pathname或打开的文件描述符来指定新的当前工作目录。 注意:当前工作目录是进程的一个属性,所以chdir函数不会影响别的进程。
#include<unistd.h> char* getcwd(char* buf, size_t size); 得到完整路径名。
22、设备特殊文件:(1)、系统中与每个文件名关联的st_dev值是文件系统的设备号,只有字符特殊文件和块特殊文件才有st_rdev值。