文件和目录操作的系统函数

文件和目录操作的系统函数
本节简要介绍一下文件和目录操作常用的系统函数,常用的文件操作命令如ls、cp、mv等也是基于
这些函数实现的。本节的侧重点在于讲解这些函数的工作原理,而不是如何使用它们,理解了实现
原理之后再看这些函数的用法就很简单了,请读者自己查阅Man Page了解其用法。
stat(2) 函数读取文件的inode,然后把inode中的各种文件属性填入一个struct stat 结构体传出给
调用者。stat(1)命令是基于stat 函数实现的。stat 需要根据传入的文件路径找到inode,假设一个
路径是/opt/file ,则查找的顺序是:
1. 读出inode表中第2项,也就是根目录的inode,从中找出根目录数据块的位置
2. 从根目录的数据块中找出文件名为opt 的记录,从记录中读出它的inode号
3. 读出opt 目录的inode,从中找出它的数据块的位置
4. 从opt 目录的数据块中找出文件名为file 的记录,从记录中读出它的inode号
5. 读出file 文件的inode
还有另外两个类似stat 的函数:fstat(2) 函数传入一个已打开的文件描述符,传出inode信
息,lstat(2) 函数也是传入路径传出inode信息,但是和stat 函数有一点不同,当文件是一个符号
链接时,stat(2)函数传出的是它所指向的目标文件的inode,而lstat 函数传出的就是符号链接文
件本身的inode。
access(2) 函数检查执行当前进程的用户是否有权限访问某个文件,传入文件路径和要执行的访问
操作(读/写/执行),access函数取出文件inode中的st_mode字段,比较一下访问权限,然后返
回0表示允许访问,返回-1表示错误或不允许访问。
chmod(2) 和fchmod(2) 函数改变文件的访问权限,也就是修改inode中的st_mode 字段。这两个函数的
区别类似于stat / fstat 。chmod(1) 命令是基于chmod 函数实现的。
chown(2) / fchown(2) / lchown(2) 改变文件的所有者和组,也就是修改inode中的User 和Group 字段,
只有超级用户才能正确调用这几个函数,这几个函数之间的区别类似
于stat / fstat / lstat 。chown(1) 命令是基于chown 函数实现的。
utime(2) 函数改变文件的访问时间和修改时间,也就是修改inode中的atime 和mtime 字
段。touch(1) 命令是基于utime 函数实现的。
truncate(2)和ftruncate(2) 函数把文件截断到某个长度,如果新的长度比原来的长度短,则后面的
数据被截掉了,如果新的长度比原来的长度长,则后面多出来的部分用0填充,这需要修改inode中
的Blocks索引项以及块位图中相应的bit。这两个函数的区别类似于stat / fstat 。
link(2) 函数创建硬链接,其原理是在目录的数据块中添加一条新记录,其中的inode号字段和原文
件相同。symlink(2) 函数创建一个符号链接,这需要创建一个新的inode,其中st_mode字段的文件
类型是符号链接,原文件的路径保存在inode中或者分配一个数据块来保存。ln(1) 命令是基
于link 和symlink函数实现的。
unlink(2) 函数删除一个链接。如果是符号链接则释放这个符号链接的inode和数据块,清
除inode位图和块位图中相应的位。如果是硬链接则从目录的数据块中清除一条文件名记录,如果

当前文件的硬链接数已经是 了还要删除它,就同时释放它的和数据块,清除位图中相应的位,这样就

真的删除文件了。unlink(1) 命令和rm(1) 命令是基于unlink函数实现的。

rename(2) 函数改变文件名,需要修改目录数据块中的文件名记录,如果原文件名和新文件名不在

一个目录下则需要从原目录数据块中清除一条记录然后添加到新目录的数据块中。mv(1) 命令是基

于rename函数实现的,因此在同一分区的不同目录中移动文件并不需要复制和删除文件的inode和
数据块,只需要一个改名操作,即使要移动整个目录,这个目录下有很多子目录和文件也要随着一
起移动,移动操作也只是对顶级目录的改名操作,很快就能完成。但是,如果在不同的分区之间移
动文件就必须复制和删除inode和数据块,如果要移动整个目录,所有子目录和文件都要复制删
除,这就很慢了。
readlink(2)函数读取一个符号链接所指向的目标路径,其原理是从符号链接的inode或数据块中读
出保存的数据,这就是目标路径。
mkdir(2) 函数创建新的目录,要做的操作是在它的父目录数据块中添加一条记录,然后分配新
的inode和数据块,inode的st_mode字段的文件类型是目录,在数据块中填两个记录,分别
是.和..,由于..表示父目录,因此父目录的硬链接数要加1。mkdir(1) 命令是基于mkdir 函数实现
的。
rmdir(2) 函数删除一个目录,这个目录必须是空的(只包含.和.. )才能删除,要做的操作是释放
它的inode和数据块,清除inode位图和块位图中相应的位,清除父目录数据块中的记录,父目录的
硬链接数要减1。rmdir(1) 命令是基于rmdir 函数实现的。
opendir(3) / readdir(3) / closedir(3)用于遍历目录数据块中的记录。opendir 打开一个目录,返回一
个DIR *指针代表这个目录,它是一个类似FILE *指针的句柄,closedir 用于关闭这个句柄,把DIR
*指针传给readdir 读取目录数据块中的记录,每次返回一个指向struct dirent的指针,反复读就可
以遍历所有记录,所有记录遍历完之后readdir返回NULL 。结构体struct dirent的定义如下:
struct dirent {
ino_t
d_ino;
off_t
d_off;
unsigned short d_reclen;
unsigned char d_type;
char
d_name[256];
};
/* inode number */
/* offset to the next dirent */
/* length of this record */
/* type of file */
/* filename */
这些字段和图 29.6 “根目录的数据块”基本一致。这里的文件名d_name被库函数处理过,已经在结尾
加了'\0',而图 29.6 “根目录的数据块”中的文件名字段不保证是以'\0'结尾的,需要根据前面的文件
名长度字段确定文件名到哪里结束。
下面这个例子出自[K&R],作用是递归地打印出一个目录下的所有子目录和文件,类似ls -R。
例 29.1. 递归列出目录中的文件列表
#include
#include
#include
#include
#include
#include
<sys/types.h>
<sys/stat.h>
<unistd.h>
<dirent.h>
<stdio.h>
<string.h>
#define MAX_PATH 1024

/* dirwalk: apply fcn to all files in dir */
void dirwalk(char *dir, void (*fcn)(char *))
{
char name[MAX_PATH];
struct dirent *dp;
DIR *dfd;
if ((dfd = opendir(dir)) == NULL) {
fprintf(stderr, "dirwalk: can't open
%s\n", dir);
return;
}
while ((dp = readdir(dfd)) != NULL) {
if (strcmp(dp->d_name, ".") == 0
|| strcmp(dp->d_name, "..") == 0)
continue;
/* skip self and
parent */
if (strlen(dir)+strlen(dp->d_name)+2 >
sizeof(name))
fprintf(stderr, "dirwalk: name %s
%s too long\n",
dir, dp->d_name);
else {
sprintf(name, "%s/%s", dir, dp-
>d_name);
(*fcn)(name);
}
}
closedir(dfd);
}
/* fsize: print the size and name of file "name" */
void fsize(char *name)
{
struct stat stbuf;
if (stat(name, &stbuf) == -1) {
fprintf(stderr, "fsize: can't access
%s\n", name);
return;
}
if ((stbuf.st_mode & S_IFMT) == S_IFDIR)
dirwalk(name, fsize);
printf("%8ld %s\n", stbuf.st_size, name);
}
int main(int argc, char **argv)
{
if (argc == 1) /* default: current directory */
fsize(".");
else
while (--argc > 0)
fsize(*++argv);
return 0;
}
然而这个程序还是不如ls -R健壮,它有可能死循环,思考一下什么情况会导致死循环。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值