一、linux 中各种文件类型
1、普通文件( - regular file )
- 文本文件(将 0101 的数字按照一定编码,变得能让人看懂)
文件中的内容是由文本构成的,文本指的是ASCII码字符。文件里的内容本质上都是数字(不管什么文件内容本质上都是数字,因为计算机本身只有0和1),而文本文件中的数字本身应该被理解为这个数字对应的ASCII码。常见的有**.c文件、.h文件、.txt文件**等都是文本文件。文本文件的好处就是可以被人轻松读懂和编写。所以说文本文件天生为人类发明的。
- 二进制文件(编译生成的可执行文件)
二进制文件中存储的本质也是数字,只不过这些数字并不是文字的编码数字,而是真正的数字。常见的可执行文件(gcc编译生成的a.out,arm-linux-gcc编译链接生成的.bin)都是二进制文件。
总结:
从本质上看,文本文件和二进制文件并没有二进制文件。都是在一个文件里存放了数字。
区别就是理解方式不同,如果把这些数字就当作数字处理则就是二进制文件,如果把这些数字按照某种编码格式去解码成文本字符,则就是文本文件。
问:我们如何知道一个文件是文本文件还是二进制文件呢?
在Linux系统层面是不区分这两个的(譬如open、read、write等方法操作文本文件和二进制文件时一点区别都没有),所以我们无法从文件本身准确知道文件属于哪一种,我们只能本来就知道这个文件的类型然后用这个文件的用法去用他。有时候会用一些后缀名来人为的标记文件的类型。
使用文本文件时,常规用法就是用文本文件编辑器去打开它、编辑它。常见的文本文件编辑器如vim、gedit、notepad++、sourceinsight等,我们用这些文本文件编辑器去打开文件的时候,编辑器会read读出文件二进制数字内容,然后按照编码格式去解码将其还原成文字展现给我们。
问:如果用文本文件编辑器去打开一个二进制文件会如何?
这时候编辑器就以为这个二进制文件还是文本文件,然后试图去将其解码成文字,但是解码过程中很多数字并不对应有意义的文字所以成了乱码。
问:如果用二进制阅读工具去打开一个文本文件如何?
得出的就是文本文件对应的二进制的编码。
2、目录文件( d directory )
目录就是文件夹,文件夹在Linux中 也是一种文件,不过是特殊文件。
用vi打开一个文件夹就能看到,文件夹其实就是一种特殊文件,里边存的内容包括这个文件的路径还有文件夹里边的文件列表。
(kong 文件当中包含 1.txt,2.c,kong 三个文件)
但是文件夹这种文件比较特殊,本身并不适合用普通的方式来读写。Linux中是使用特殊的一些API来专门读写文件夹的。
3、设备文件(c , b)
设备文件包括:
字符设备文件(c character)
扫描二维码关注公众号,回复: 12410316 查看本文章![]()
块设备文件(b block)。
设备文件对应的是硬件设备,也就是说这个文件虽然在文件系统中存在,但是并不是真正存在于硬盘上的一个文件,而是文件系统虚拟制造出来的(叫做虚拟文件系统,如/dev /sys /proc等)
虚拟文件系统中的文件大多数不能或者说是不用直接读写的,而是用一些特殊的API产生或者使用的。
4、管道文件(p pipe)
管道在Linux中是一种通信手段,在通信的时候需要建立管道文件作为辅助。
5、套接字文件(s socket)
在上网的时候需要建立套接字文件
6、符号链接文件(l link)
类似于Windows中的快捷方式,包括硬链接、软连接等等。
二、常用文件属性获取
什么是文件属性:
利用 stat 函数可以看到,文件名,文件大小,在硬盘什么位置,文件权限等等这都是文件属性。
Blocks:占用多少个块
IO Block: 之前讲的在应用层有一个缓冲机制, 此处代表凑够 1024 个字节,进行一次 IO 读写
Device :指这个文件对应的设备,(18h/24d 可能代表设备名,或者块名那一类)
Inode : 静态文件存储在硬盘当中的标识。
Access:可访问性:
A time: 最后一次访问的时间
Modify: 表示修改文件里面的内容
Change:表示更改了一次文件的权限
1、stat、fstat、lstat API简介
文件属性信息查看的API有三个:stat、fstat、lstat,三个作用一样,参数不同,细节略有不同。
int stat(const char *path, struct stat *buf);
分析这个函数:
const char *path : 输入型参数,表示这个参数在这个函数当中不会改变。
struct stat *buf : 输出型参数,这个结构体在这个函数当中会被填充。
总结: 输入文件名(const char *path),输出文件属性(struct stat *buf)
int fstat(int fd, struct stat *buf);
int lstat(const char *path, struct stat *buf);
fstat和stat的区别是:
stat是从文件名出发得到文件属性信息结构体,而fstat是从一个已经打开的文件fd出发得到一个文件的属性信息。
所以用的时候如果文件没有打开(我们并不想打开文件操作而只是希望得到文件属性)那就用stat,
如果文件已经被打开了然后要属性那就用fstat效率会更高
stat是从磁盘去读取文件的,而fstat是从内存读取动态文件的。
lstat 和 stat/fstat的差别在于:
对于符号链接文件,stat和fstat查阅的是符号链接文件指向的文件的属性,而lstat查阅的是符号链接文件本身的属性。
2、struct stat结构体简介
struct stat
{
dev_t st_dev; /* ID of device containing file */
ino_t st_ino; /* inode number */
mode_t st_mode; /* protection */
nlink_t st_nlink; /* number of hard links */
uid_t st_uid; /* user ID of owner */
gid_t st_gid; /* group ID of owner */
dev_t st_rdev; /* device ID (if special file) */
off_t st_size; /* total size, in bytes */
blksize_t st_blksize; /* blocksize for filesystem I/O */
blkcnt_t st_blocks; /* number of 512B blocks allocated */
time_t st_atime; /* time of last access */
time_t st_mtime; /* time of last modification */
time_t st_ctime; /* time of last status change */
};
3、利用 stat 函数来,显示一些自己想要的文件属性。
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#define NAME "1.txt"
int main(void)
{
int ret = -1;
struct stat buf;
memset(&buf, 0, sizeof(buf)); // memset后buf中全是0
ret = stat(NAME, &buf); // stat后buf中有内容了
if (ret < 0)
{
perror("stat");
exit(-1);
}
// 成功获取了stat结构体,从中可以得到各种属性信息了
printf("inode = %d.\n", buf.st_ino);
printf("size = %d bytes.\n", buf.st_size);
printf("st_blksize = %d.\n", buf.st_blksize);
return 0;
}
4、利用 stat 来判断文件类型
-
文件属性中的文件类型标志在 struct stat结构体的mode_t st_mode元素中,这个元素其实是一个按位来定义的一个位标志(有点类似于ARM CPU的CPSR寄存器的模式位定义)。
-
利用 man 手册当中特定的宏,来确定这个文件到底是什么文件。譬如S_ISREG宏返回值是1表示这个文件是一个普通文件,如果文件不是普通文件则返回值是0.
理解英文: s_isreg:is regular? 都是缩写
实验代码:
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#define NAME "1.txt"
int main(void)
{
int ret = -1;
struct stat buf;
memset(&buf, 0, sizeof(buf)); // memset后buf中全是0
ret = stat(NAME, &buf); // stat后buf中有内容了
if (ret < 0)
{
perror("stat");
exit(-1);
}
// 判断这个文件属性
//int result = S_ISREG(buf.st_mode); //判断是否为普通文件,是则返回1,不是返回0
int result = S_ISDIR(buf.st_mode); //判断是否为目录文件,是则返回1,不是返回0
printf("result = %d\n", result);
return 0;
}
结果:
三、linux 当中的文件权限(以及详解)
linux 当中并没有给文件权限测试提供宏操作,而只是提供了位掩码,所以我们只能用位掩码自己来判断。
重点还是: struct stat 里面的 st_mode 元素, 这个元素本质是 unsigned int (32位数字)。
- st_mode 当中记录的文件权限位和文件类型
使用规则:(需要自己回头查找)
1、linux 当中使用 ls -l 来显示文件权限
user(属主权限): 创建这个文件的用户,在 aston 用户下创建的文件。
group(属组权限):属组(不太清楚),好像一般和属主相同。
other(其他用户权限):既不是属主 也不是属组。
2、文件操作时的权限检查规则
思考:当一个可执行文件 a.out 在执行的时候,内部程序尝试访问文件,这时可执行程序扮演什么角色?
答案:主要看 a.out 可执行程序,在哪个用户下被执行。
若在 test 用户下执行,访问的属主和属组都是 aston 的文件,那么这个 a.out 在这个文件看来就是其他用户(other)。
代码测试:
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#define NAME "1.txt"
int main(int argv,char *argv[])
{
int fd1 = -1;
fd1 = open(NAME, O_RDONLY); // O_RDONLY :表示这个有可读权限,如果没有可读权限就会打开失败
if(fd1 > 0)
{
printf("可读\n");
}
fd1 = open(NAME, O_WRONLY); // O_WRONLY :表示这个有可读权限,如果没有可写权限也会打开失败
if(fd1 > 0)
{
printf("可读\n");
}
return 0;
}
- 情况一:在 aston 用户下,执行程序访问 1.txt,这时候扮演 user 的角色,就是这个文件的属主在访问他,所以看前三个标志。(可读,可写)
- 情况二:在 aston 用户下,执行程序访问 2.txt,这时候扮演 other 的角色,就是其他用户在访问这个文件,所以看后三个标志。(只是可写)
3、access函数检查权限设置
举例: windows 系统下,有很多软件只有在管理员模式下才可以正常进行使用。然而在使用的时候也没有提醒我们要不要使用管理员模式。
缺点:在使用之前没有添加检查目前用户有没有访问权限的代码。
解决:使用 access 函数可其进行测试。
- 函数原型
int access(const char *pathname, int mode);
参数:
const char *pathname :文件名称
int mode :检测各种权限有: F_OK , R_OK , W_OK, X_OK
F_OK tests for the existence of the file.
R_OK, W_OK, and X_OK test whether the file exists and grants read, write, and execute permissions,
返回值:
On success zero is returned.
On error -1 is returned,
- 代码测试
#include <unistd.h>
#include <stdio.h>
//#define NAME "1.txt"
int main(int argc, char *argv[])
{
int ret = -1;
ret = access(argv[1],F_OK);
if(ret == 0)
printf("file exsit\n");
else
{
printf("please to creat\n");
_exit(-1);
}
ret = access(argv[1],R_OK);
if(ret == 0)
printf("file can be read\n");
else
{
printf("the file havn't read permission\n");
}
ret = access(argv[1],W_OK);
if(ret == 0)
printf("file can be write\n");
else
{
printf("the file havn't write permission\n");
}
ret = access(argv[1],X_OK);
if(ret == 0)
printf("file can be executed \n");
else
{
printf("the file havn't execute permission\n");
}
return 0;
}
4、chmod 和 chown
chowm 修改文件的 属主或者 属组
chmod 修改 user group and other 对这个文件的权限(read write execute)
注:chmod 只有 root 用户才可以使用
- chmod 既是linux命令 也是 linux的API
#include <sys/stat.h>
int chmod(const char *path, mode_t mode);
参数:
const char *path :文件名
mode_t mode :参数
S_IRUSR :set read usr ,给 usr 添加read 权限
S_IWUSR :set write usr ,给 usr 添加write 权限
案例:
chmod("1.txt",S_IRUSR | S_IWUSR ); usr or operation
补充:chmod命令(linux 命令)的使用:
- chmod函数(linux API)的使用
#include <stdio.h>
#include <sys/stat.h>
int main(int argc, char **argv)
{
int ret = -1;
if (argc != 2)
{
printf("usage: %s filename\n", argv[0]);
return -1;
}
ret = chmod(argv[1], S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IWOTH); //设置属主对文件有可读可写可执行权限
if (ret < 0) //属主所在的组对文件有可读权限,其他用户对文件有可操作权限
{
perror("chmod");
return -1;
}
return 0;
}
注:我们这里只给了 usr 用户的权限。
- chown命令(linux 命令)的使用
chown [–R] 属主名 文件名
chown [-R] 属主名:属组名 文件名
- 只修改属主
- 既修改属主,也修改属组
5、umask (文件权限掩码)
umask 是linux 系统当中维护的一个全局设置,
引入:新创建的文件默认的权限是什么?(实际操作显示这样)
利用 umask 可以看出是 0022
将 umask 修改为 0044
总结:当为 2 的时候,对应的w被屏蔽。 当为 4 的时候,对应的 r 位被屏蔽。
然后分别改为 0077(全部不允许) 和 0000(全部允许) 的时候。
为了安全考虑,一般文件不具备执行权限,只有编译生成的可执行文件才具有可执行权限。
四、读取目录文件
1、Opendir 和 readfir函数
- 函数模型
DIR *opendir(const char *name);
const char *name: 要打开的目录文件
DIR * : 返回的文件指针
struct dirent *readdir(DIR *dirp);
DIR *dirp: 就是使用 opendir 函数返回的指针
返回一个结构体指针
struct dirent {
ino_t d_ino; /* inode number */
off_t d_off; /* not an offset; see NOTES */
unsigned short d_reclen; /* length of this record */ (目录长度)
unsigned char d_type; /* type of file; not supported
by all filesystem types */ (文件类型)
char d_name[256]; /* filename */ (文件名字)
}
1、opendir打开一个目录后得到一个DIR类型的指针给readdir使用
2、readdir函数调用一次就会返回一个struct dirent类型的指针,这个指针指向一个结构体变量,这个结构体变量里面记录了一个目录项(所谓目录项就是目录中的一个子文件)。
注:一个文件夹下有 8 个文件,我们每调用一次只返回一个该文件下的子文件信息,所以我们一共需要调用 8次。
readdir函数内部户记住哪个目录项已经被读过了哪个还没读,所以多次调用后不会重复返回已经返回过的目录项。
当readdir函数返回NULL时就表示目录中所有的目录项已经读完了。
- 代码实践
#include <stdio.h>
#include <sys/types.h>
#include <dirent.h>
int main(int argc, char **argv)
{
DIR *pDir = NULL;
struct dirent * pEnt = NULL;
unsigned int cnt = 0;
if (argc != 2)
{
printf("usage: %s dirname\n", argv[0]);
return -1;
}
pDir = opendir(argv[1]);
if (NULL == pDir)
{
perror("opendir");
return -1;
}
while (1)
{
pEnt = readdir(pDir);
if(pEnt != NULL)
{
// 还有子文件,在此处理子文件
printf("name:[%s] ,", pEnt->d_name);
cnt++;
if (pEnt->d_type == DT_REG)
{
printf("是普通文件\n");
}
else
{
printf("不是普通文件\n");
}
}
else
{
break;
}
};
printf("总文件数为:%d\n", cnt);
return 0;
}
2、可重入函数引入
(1)有些函数是可重入的有些是不可重入的,至于具体用法读者自行去百度查阅。
(2)readdir函数和我们前面接触的一些函数是不同的,首先readdir函数直接返回了一个结构体变量指针,因为readdir内部申请了内存并且给我们返回了地址。多次调用readdir其实readir内部并不会重复申请内存而是使用第一次调用readdir时分配的那个内存。这个设计方法是readdir不可重入的关键。
(3)readdir在多次调用时是有关联的,这个关联也标明readdir函数是不可重入的。
(4)库函数中有一些函数当年刚开始提供时都是不可重入的,后来意识到这种方式不安全,所以重新封装了C库,提供了对应的可重复版本(一般是不可重入版本函数名_r)