文件的相关操作
一、文件同步
1、在写入数据时,内存到磁盘之间有一块缓冲区,这种机制降低了磁盘读写次数、提高了读写效率
2、但是这种机制带来的后果是磁盘的数据与实际写入的数据不匹配,系统提供了三个系统函数可以让缓冲区的数据立即写入到磁盘
void sync(void);
功能:立即把缓冲区中的数据同步到磁盘
注意:并不会等待数据同步结束才返回,而是提交要同步的数据到写入队列中,就返回
int fsync(int fd);
功能:把指定文件的内容同步到磁盘
注意:会等到完全写入磁盘后才返回
int fdatasync(int fd);
功能:把指定文件的内容同步到磁盘,只同步文件内容不同步文件属性
二、文件属性
int stat(const char *pathname, struct stat *buf);
功能:根据文件的路径获取文件属性
buf:存储文件属性的结构体 是一个输出型参数
int fstat(int fd, struct stat *buf);
功能:根据文件描述获取文件属性
int lstat(const char *pathname, struct stat *buf)
功能:根据文件的路径获取软链接文件属性
struct stat {
dev_t st_dev; // 设备ID
ino_t st_ino; // inode节点号
mode_t st_mode; // 文件类型和权限
nlink_t st_nlink; // 硬链接数
uid_t st_uid; // 用户ID
gid_t st_gid; // 组ID
dev_t st_rdev; // 特殊设备ID号
off_t st_size; // 总字节数
blksize_t st_blksize; // IO块字节数
blkcnt_t st_blocks; // 占用512Bblock块数量
struct timespec st_atim; // 最后访问时间
struct timespec st_mtim; // 最后内容修改时间
struct timespec st_ctim; // 最后状态修改时间
};
st_mode:
文件类型:
S_IFMT 0170000 获取文件类型的掩码
S_IFSOCK 0140000 socket文件
S_IFLNK 0120000 软链接文件
S_IFREG 0100000 普通文件
S_IFBLK 0060000 块设备文件
S_IFDIR 0040000 目录文件
S_IFCHR 0020000 字符设备文件
S_IFIFO 0010000 管道文件
上面类型判断在POSIX中定义了以下函数进行类型判断
S_ISREG(st_mode) 是普通文件
S_ISDIR(m) 是目录文件
S_ISCHR(m) 字符设备文件
S_ISBLK(m) 块设备文件
S_ISFIFO(m) 管道文件
S_ISLNK(m) 软链接文件
S_ISSOCK(m) socket文件
st_mode包含的权限信息:
S_IRWXU 00700 用户自己的权限掩码
S_IRUSR 00400 读
S_IWUSR 00200 写
S_IXUSR 00100 执行
S_IRWXG 00070 测试组权限的掩码
S_IRGRP 00040 读
S_IWGRP 00020 写
S_IXGRP 00010 执行
S_IRWXO 00007 测试其他用户权限的掩码
S_IROTH 00004 读
S_IWOTH 00002 写
S_IXOTH 00001 执行
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <pwd.h>
#include <grp.h>
#include <time.h>
// 显示文件类型
void file_type(mode_t mode)
{
switch(mode & S_IFMT)
{
case S_IFSOCK: printf("s"); break;
case S_IFLNK: printf("l"); break;
case S_IFREG: printf("-"); break;
case S_IFBLK: printf("b"); break;
case S_IFDIR: printf("d"); break;
case S_IFCHR: printf("c"); break;
case S_IFIFO: printf("p"); break;
}
}
// 显示文件权限
void file_mode(mode_t mode)
{
printf("%c",mode & S_IRUSR ?'r':'-');
printf("%c",mode & S_IWUSR ?'w':'-');
printf("%c",mode & S_IXUSR ?'x':'-');
printf("%c",mode & S_IRGRP ?'r':'-');
printf("%c",mode & S_IWGRP ?'w':'-');
printf("%c",mode & S_IXGRP ?'x':'-');
printf("%c",mode & S_IROTH ?'r':'-');
printf("%c",mode & S_IWOTH ?'w':'-');
printf("%c ",mode & S_IXOTH ?'x':'-');
}
// 显示用户名
void user_name(uid_t uid)
{
struct passwd* pw = getpwuid(uid);
printf("%s ",pw->pw_name);
}
// 显示组名
void group_name(gid_t gid)
{
struct group* gp = getgrgid(gid);
printf("%s ",gp->gr_name);
}
// 获取最后访问时间
void access_time(time_t time)
{
struct tm* t = localtime(&time);
printf("%d月 %d %d:%d ",
t->tm_mon+1,t->tm_mday,t->tm_hour,t->tm_min);
}
void list_file_stat(const char* path)
{
// 获取文件属性
struct stat buf;
if(stat(path,&buf))
{
perror("stat");
return;
}
// 显示文件类型
file_type(buf.st_mode);
// 显示文件权限
file_mode(buf.st_mode);
// 显示目录层数
printf("%d ",S_ISDIR(buf.st_mode)?2:1);
// 显示用户名
user_name(buf.st_uid);
// 显示组名
group_name(buf.st_gid);
// 显示字节数
printf("%lu ",buf.st_size);
// 显示最后访问时间
access_time(buf.st_atim.tv_sec);
// 显示文件名
printf("%s\n",path);
}
int main(int argc,const char* argv[])
{
list_file_stat("01stat.c");
}
三、文件的权限
int access(const char *pathname, int mode);
功能:测试当前用户对文件的权限
pathname:文件的路径
mode:想要测试的权限
F_OK 文件是否存在
R_OK 读权限
W_OK 写权限
X_OK 执行权限
返回值:存在返回0,不存在返回-1
#include <stdio.h>
#include <unistd.h>
int main(int argc,const char* argv[])
{
printf("%d\n",access("01stat.c",F_OK));
printf("%d\n",access("01stat.c",R_OK));
printf("%d\n",access("01stat.c",W_OK));
printf("%d\n",access("01stat.c",X_OK));
}
int chmod(const char *pathname, mode_t mode);
功能:根据路径修改文件权限
mode:由三位八进制数组成的权限码
0644 普通文件
0755 目录文件/可执行文件
#include <stdio.h>
#include <sys/stat.h>
int main(int argc,const char* argv[])
{
printf("%d\n",chmod("01stat.c",0777));
}
int fchmod(int fd, mode_t mode);
功能:根据文件描述符修改文件权限
四、权限屏蔽码
如果我们不想让新创建的文件拥有某种权限,则可以设置权限过滤,记录在权限屏蔽码中
权限屏蔽码对chmod命令和函数是无效的
通过命令 umask 查看当前终端的权限屏蔽码
1、通过 umask 0xxx 设置当前终端的权限屏蔽码
2、函数修改
mode_t umask(mode_t mask);
mask:想要设置的权限屏蔽码
返回值:旧的权限屏蔽码
注意:通过命令、函数修改权限屏蔽码,只会在当前终端生效,如果关闭后会恢复为默认的
五、修改文件的大小
int truncate(const char *path, off_t length);
功能:根据文件路径修改文件长度
length:想要修改的字节数
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
int main(int argc,const char* argv[])
{
printf("%d\n",truncate("test.txt",30));
}
int ftruncate(int fd, off_t length);
功能:根据文件描述符修改文件长度
length:想要修改的字节数
练习1:实现一个函数,可以删除文件的[n,m)个字节
int cut_file(const char* path,size_t n,size_t m);
// 读写打开文件
// 从m读到buf
// 从buf写到n
// 更新m n 继续读写
// 读完 相当于 从m挪到n 把后面删除
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
int cut_file(const char* path,size_t n,size_t m)
{
int fd = open(path,O_RDWR);
if(0 > fd)
{
perror("open");
return -1;
}
// 计算原文件字节数
int len = lseek(fd,0,SEEK_END);
if(m > len) m = len;
if(n >= m) return -1;
// 计算出删除后文件字节数
len -= (m-n);
// 从m开始数据向前移动
char buf[256] = {};
lseek(fd,m,SEEK_SET);
int ret = 0;
while(ret = read(fd,buf,sizeof(buf)))
{
// 写入到文件的n位置
lseek(fd,n,SEEK_SET);
write(fd,buf,ret);
// 更新m n
m += ret;
n += ret;
lseek(fd,m,SEEK_SET);
}
// 删除末尾多余数据
ftruncate(fd,len);
close(fd);
}
int main(int argc,const char* argv[])
{
cut_file("test.txt",3,80);
}
六、文件删除和重命名
int remove(const char *pathname);
功能:标准库中的删除文件、空目录的函数
底层调用了unlink
#include <stdio.h>
int main(int argc,const char* argv[])
{
printf("%d\n",remove("dir"));
}
int unlink(const char *pathname);
功能:删除文件
int rename(const char *oldpath,const char *newpath);
功能:重命名、移动文件
#include <stdio.h>
int main(int argc,const char* argv[])
{
printf("%d\n",rename("tt.c","./dir/t.c"));
}
七、链接文件
Linux文件系统会有两个主要的分区:
inode信息块:默认128B,记录文件权限、大小、所有者、修改时间等
block数据块:默认4k,记录了文件名和数据信息
每个文件必须拥有唯一一个inode以及若干个block块,读写文件需要借助目录的block中记录的文件名和inode号找到该文件的inode,
通过inode读取block
什么是软硬链接文件?
硬链接:硬链接文件没有自己的inode和block,只是在不同目录下复制了一份源文件的inode信息,通过inode信息访问源文件的block
软链接:软链接会建立自己的新的inode和block,软链接的block存储的是源文件的inode信息、文件名
区别:
1、删除源文件,只是删除了源文件的inode信息,硬链接不受影响,而软链接无法访问
2、对于一个文件而言,硬链接数删除为0时,文件才被真正的删除
3、当修改硬链接文件的内容,源文件也会被修改
4、硬链接不能链接目录,软链接可以
5、硬链接不能跨文件系统、软链接可以
int link(const char *oldpath,const char *newpath);
功能:创建硬链接文件
int symlink(const char *target,const char *linkpath);
功能:创建软链接文件
ssize_t readlink(const char *pathname, char *buf, size_t bufsiz);
功能:读取软链接文件链接路径
#include <stdio.h>
#include <unistd.h>
int main(int argc,const char* argv[])
{
//printf("%d\n",link("/home/ubuntu/随机.c","li.c"));
// printf("%d\n",symlink("/home/ubuntu/随机.c","syli.c"));
char buf[256] = {};
readlink("syli.c",buf,sizeof(buf));
printf("%s\n",buf);
}
八、目录操作
int mkdir(const char *pathname, mode_t mode);
功能:创建目录
mode:权限,必须要执行权限才能访问目录
int rmdir(const char *pathname);
功能:删除空目录
char *getcwd(char *buf, size_t size);
功能:获取当前工作目录,相当于pwd
buf:存储结果的内存
size:buf的大小
返回值:buf的地址 方便链式调用
int chdir(const char *path);
功能:根据路径字符串修改工作路径
int fchdir(int fd);
功能:根据文件描述符修改工作路径
DIR *opendir(const char *name);
功能:打开目录文件,返回目录流结构体指针
DIR *fdopendir(int fd);
功能:打开目录文件,返回目录流结构体指针
struct dirent *readdir(DIR *dirp);
功能:从目录流中读取一条记录信息,该条信息记录了目录中一个文件的信息
struct dirent {
ino_t d_ino; // inode号
off_t d_off; // 下一条信息的偏移量
unsigned short d_reclen; //当前信息的长度
unsigned char d_type; // 文件的类型
char d_name[256]; // 文件名
};
#include <stdio.h>
#include <dirent.h>
int main(int argc,const char* argv[])
{
DIR* dir = opendir(".");
if(NULL == dir)
{
perror("opendir");
return -1;
}
for(struct dirent* d=readdir(dir); d; d=readdir(dir))
{
if('.' != d->d_name[0])
printf("%s\n",d->d_name);
}
}