LinuxC——2.文件属性
0.❤️API
- stat、fstat、lstat
- umast
- chmod、fchmod
- chown、fchown、lchown
- link、unlink、remove、rename、symlink、readlink
- chdir、getcwd
1.🧡Linux的7种文件类型
-
普通文件
-
-
文本文件
存放文字编码,文本编辑器打开后,进行翻译成文字
-
二进制文件(机器码)
存放执行二进制机器码,使用文本编辑器查看是乱码
对于linux而言没有区别,至于文中数据由应用程序来解决
-
-
目录文件
d
目录是一种特殊文件,专门用于管理其他文件
-
字符设备文件
c
对接字符设备驱动,读取字符设备文件,是与字符设备驱动的交互
-
块设备
b
-
字符设备
以字节为单位操作数据,比如鼠标、键盘、显示器
-
块设备
块设备数据非常大,提高读写,以1024为单位。比如电脑磁盘、移动硬盘
-
-
Pipeline
p
管道文件,用于不同程序或进程的通信(使用一个管道文件进行交互)
-
Socket
s
套接字文件
-
符号链接
l
类似快捷图标
2.🧡基础知识
🤠使用file命令可查看文件类型,如file a.txt
-
Linux可执行文件:ELF文件(Executable and Linkable Format)
-
Windows可执行文件:PE文件(protable execute)
-
Mac可执行文件:Mach-O文件(match object)
🤠strip a.out
将这个a.out进行压缩,发布版本会进行压缩
3.💛stat、lstat、fstat
三个是兄弟函数,ls命令的底层是这个
🤠1. stat
- 获取文件属性信息
数据中转过程:
应用缓存<-内核缓存<-驱动程序<-块设备
- 函数原型
int stat(const char *path, struct stat *buf);
path: 路径名 struct stat { dev_t st_dev; // 块设备号 ino_t st_ino; // 节点号 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; // 块索引 // windows下文件时间也是这三种 time_t st_atime; // 最后访问时间 time_t st_mtime; // 最后修改时间 time_t st_ctime; // 最后属性修改时间,如权限修改,文件所有者 }
制作一个my_ls
#include <stdio.h> #include <sys/types.h> #include <sys/stat.h> #include <unistd.h> #include <stdlib.h> void print_error(char* str) { perror(str); exit(-1); } int main(int argc, char **argv) { int ret = 0; if(argc != 2) print_error("argc is wrong"); struct stat s = {0}; ret = stat(argv[1], &s); if(-1 == ret) { print_error("查看属性失败"); } printf("%d %lu %d %ld %ld %ld\n", s.st_mode, s.st_nlink, s.st_gid, s.st_uid, s.st_size, s.st_atime); return 9; }
🤠2.st_mode
文件权限16位,3位设置位,其余是-rwxrwxrwx形式
使用&进行提取位,一个数&(1<<n),得出这个的真值,所以,我们可以同理可写出
/* 打印文件类型 */ char file_type = '0'; if(S_ISLNK(s.st_mode)) file_type = 'l'; // link else if(S_ISREG(s.st_mode)) file_type = '-'; // regular else if(S_ISDIR(s.st_mode)) file_type = 'd'; // dictionary else if(S_ISCHR(s.st_mode)) file_type = 'c'; // char else if(S_ISBLK(s.st_mode)) file_type = 'b'; // block else if(S_ISFIFO(s.st_mode)) file_type = 'p'; // pipe else if(S_ISSOCK(s.st_mode)) file_type = 's'; // socket putchar(file_type); char buf[10] = { '\0' }; char tmp_buf[] = "rwxrwxrwx"; /* 太复杂,采用下面的 if(s.st_mode & S_IRUSR) buf[0] = 'r'; else buf[0] = '-'; if(s.st_mode & S_IWUSR) buf[1] = 'w'; else buf[1] = '='; */ int i = 0; for(i = 0; i <= 8; i++) { if(s.st_mode & (1 << (8 - i))) buf[i] = tmp_buf[i]; else buf[i] = '-'; } printf("%s", buf); putchar('\n');
权限在linux分16位,除了3位为信息位,1位为介绍属性位,其余12位代表用户、用户组、其他的权限
chmod a=rwx file # all chmod u=rwx file # user chmod g=rwx file # group chmod o=rwx file # other chmod a+x file # +代表增加权限 chmod a-w file # -代表减少权限
🤠3.lstat
- ln -s 命令
ln -s afile bfile 创建 afile 链接 bfile
- 介绍lstat
lstat与stat基本相同,我们发现使用ls的时候,链接文件的ls -a中间显示的是原文件,目的是调用了lstat函数,我们将上述的c文件的stat函数改为lstat,发现可以顺利解析link file。
ret = lstat(argv[1], &s); // 解析运行 // 显示链接文件的状态 // 表名源码中的状态是调用了stat
🤠4.fstat
fstat与stat基本相同,相当于把path换为了文件描述符,通过fd进行查询文件属性
fd = open('xxx', O_RDWR) ret = fstat(fd, &s);
4.💚Umask 和 truncate
🤠1.umask
open创建的一个问题
文件权限掩码,我们使用open函数创建一个文件,先通过掩码进行 & 掩码,掩码默认值为 2, 所以我们使用其他用户创建777权限,仅仅能得到775。
我们通过Umask进行修改文件掩码
#include <stdio.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <unistd.h> int main() { int fd = 0; mode_t ret = 0; // 掩码改为0 ret = umask(0); printf("old mask: %d\n", ret); fd = open("./new_file.txt", O_RDWR|O_CREAT, 0777); if(-1 == fd) { perror("open fail\n"); return 1; } return 0; }
🤠2.truncate、ftruncate
截断函数,open的时候,指定O_TRUNC,如果有数据,会截断为0
truncate传的第一个参数是path,第二个是一个offsets
#include <stdio.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <unistd.h> int main() { int fd = 0; mode_t ret = 0; truncate("new_file.txt", 10); return 0; }
ftruncate进行fd的截断
fd = open("./new_file.txt", O_RDWR|O_CREAT, 0777); ftruncate(fd, 5);
5.💙空洞文件
假设开辟一定空间,不去使用。进行动态开辟空间。
我们使用truncate和ftruncate进行制作空洞文件
1.使用open创建文件 2.使用truncate进行截断8000个字符 我们发现,使用ls查看有8000,但是使用du命令看实际大小,为0,这是一个动态开辟的空间。 块字符是进行使用空洞文件进行存储
6.💜文件系统索引
1.只要是一个系统,所有索引都是使用树形结构进行管理的。
2.文件在块设备怎么存储
- 自举区
- 超级区:块设备的分配和释放
- inode节点区:空间大小相同的i节点,每个节点存储文件属性信息,每个大小512(0.5k),结构体中的st_dev
- 数据区:仅目录,链接文件、普通文件有数据,其余4个都没有数据。数据区每个分配4K字节
7.❤️link & unlink & remove & rename
int link(const char* oldpath, const char* newpath)
int unlink(const char* pathname)
进行硬链接(很easy,代码略),正确返回0,错误返回-1,errno被设置
int remove(const char* pathname)
删除文件,正确返回0,错误返回-1,errno被设置
int rename(const char* oldpath, const char* newpath)
重命名,正确返回0,错误返回-1,errno被设置
8.🧡符号连接 symlink & redline
1.硬链接,同一个文件有不同名字,指向同一个inode节点
2.符号链接(软链接)
- 2个文件,有自己的inode节点,指向文件名
3.不能给目录创建硬链接,可以创建符号链接
4.可以给符号链接创建硬连接
int symlink(const char* oldpath, const char* newpath)
创建一个符号连接,正确返回0,错误返回-1,errno被设置
int readlink(const char* path, char *buf, size_t bufsiz)
读取符号链接,正确返回0,错误返回-1,errno被设置
9.getcwd、chdir、mkdir、rmdir
int getcwd(char* buf, size_t size)
获取一个当前路径(pwd的实现)
Int chdir(char* buf)
更换到路径(类似cd命令)
int mkdir(const char* pathname, mode_t mode)
创建目录(mkdir命令)
int rmdir(const char* pathname)
删除目录(rmdir 命令)
10.opendir、readdir、chmod、fchm
DIR *opendir(const char *name)
打开目录
struct direct *readdir(Dir *dirp)
读取目录
chmod(const char* path, mode_t m)
修改权限
fchmod(int fd, mode_t m)
修改权限
struct dirent { ino_t d_ino; // 编号 off_t d_off; // 偏移 unsigned short d_reclen; // 目录项大小 unsigned char d_type; // 文件类型 char d_name[256]; // 文件名 }
来源:https://www.cnblogs.com/littlepage/p/12657559.html