知识点4【文件的阻塞特性】
文件描述符 默认为 阻塞 的
比如:我们读取文件数据的时候,如果文件缓冲区没有数据,就需要等待数据的到来,这就是阻塞
当然写入的时候,如果发现缓冲区是满的,也需要等待刷新缓冲区,才可写入,这也是阻塞
注意:阻塞和非阻塞都是对文件而言的,并不是read和write的属性
下面 我来介绍两种设置文件非阻塞的方法:
- 通过open函数再打开文件的时候,设置文件为非阻塞
注意:文件描述符 事先不存在 才使用open的方法
案例1:open打开文件,默认为阻塞特性
这里补充一个知识点,当我们需要 打开终端时,终端的目录是 /dev/tty,下面我们在Linux中查看一下
好了现在我们实现从终端中读数据
代码演示
- 带有阻塞特性
int main(int argc, char const *argv[])
{
//打开文件
int fd = open("/dev/tty",O_RDONLY | O_NONBLOCK);
if(fd < 0)
{
perror("open");
return 0;
}
//读取文件到数组
printf("非阻塞特性展示\n");
printf("请输入字符数据\n");
char buf[128] = "";
read(fd,buf,sizeof(buf));
printf("buf = %s\n",buf);
//关闭文件
close(fd);
return 0;
}
可以看到有一个等待的过程
- 非阻塞特性
仅展示主要代码
int fd = open("/dev/tty",O_RDONLY | O_NONBLOCK);
- 使用fcntl函数在文件打开后设置文件为非阻塞
文件描述符 事先存在
fcntl函数介绍
int fcntl(int fd,int cmd,…/*arg*/)
功能介绍
改变已打开文件描述符的文件性质,针对文件描述符提供控制
参数
fd:文件描述符
cmd:操作方式
arg:cmd不同,arg会不同
返回值
成功:不同的cmd,会有不同
失败:-1
cmd:
fcntl函数有5种功能:
1) 复制一个现有的描述符(cmd=F_DUPFD)
2) 获得/设置文件描述符标记(cmd=F_GETFD或F_SETFD)
3) 获得/设置文件状态标记(cmd=F_GETFL或F_SETFL)
4) 获得/设置异步I/O所有权(cmd=F_GETOWN或F_SETOWN)
5) 获得/设置记录锁(cmd=F_GETLK, F_SETLK或F_SETLKW)
这里我们设置阻塞特性主要使用 cmd=F_GETFL或F_SETFL
但这里我提一下 文件状态标记和文件描述符标记是不同的,大家可以自行使用ChatGPT搜索区别,若仍有疑问可以评论留言
设置一个存在的文件描述符的阻塞特性的步骤
- fcntl先得到的文件描述符的状态标记
- 修改文件的状态标记
- 将修改后的状态标记应用到文件描述符上
代码演示
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
int main(int argc, char const *argv[])
{
//提取文件状态标记
int flag = fcntl(0,F_GETFL);
//处理文件状态标记
flag = flag | O_NONBLOCK;
//应用文件状态标记
fcntl(0,F_SETFL,flag);
//阻塞特性验证
char buf[128] = "";
printf("请输入数据\n");
read(0,buf,sizeof(buf));
printf("buf = %s\n",buf);
return 0;
}
代码运行结果
知识点5【获取文件状态】
int stat(const char *path,struct stat *buf);
int lstat(const char *path,struct stat *buf);
思想补充
我们能知道如果要想要函数内部修改函数外部的值,参数为指针类型
我们反过来也许知道
如果函数参数是指针类型,我们就需要知道这个函数需要是对其进行赋值操作的
stat和lstat的区别
概念复习
这里帮大家复习一个概念
链接方式分为软链接和硬链接
软连接:类似于快捷方式,操作其链接文件数据,源文件数据也会改变,但是如果源文件被删除,链接文件不能正常使用
软链接:类似于快捷方式,操作其链接文件数据,源文件数据也会改变,但是如果源文件被删除,链接文件不能正常使用
硬链接:类似于文件的拷贝(不是简单的拷贝,有链接),操作其链接文件数据,源文件数据也会改变,但是如果源文件被删除,链接文件能正常使用
区别
当我们查看链接文件的文件信息的时候
stat:会获得源文件的文件信息
lstat:会获得链接文件的文件信息
查看源文件的文件信息的时候,没有区别,最好使用stat
这两个函数的参数,返回值都一样
函数介绍
参数
path:文件的路径及文件名
buf:保存文件信息的结构体
返回值
成功:0
失败:-1
案例1:获取文件的属性、大小
这里主要介绍两种文件模式的判断方式
- 使用宏,这里 的都是宏,我们只需要使用宏函数可以直接判断
- 使用按位与的操作
if((s.st_mode & S_IRWXU) == S_IRWXU)
注意:这里的()必须加,优先级问题
代码演示
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
int main(int argc, char const *argv[])
{
struct stat s;
stat("./text.txt",&s);
//方式一判断文件类型
if(S_ISDIR(s.st_mode))
{
printf("text是一个目录\n");
}
//方式二判断文件类型
else if((s.st_mode & S_IFREG) == S_IFREG)
{
printf("text是一个普通文件\n");
}
//文件权限 有上面的man 2 stat 可知 判断只能使用 方式二
if((s.st_mode & S_IRUSR) == S_IRUSR)
{
printf("text文件所有者可读\n");
}
if((s.st_mode & S_IWUSR) == S_IWUSR)
{
printf("text文件所有者可写\n");
}
if((s.st_mode & S_IXUSR) == S_IXUSR)
{
printf("text文件所有者可执行\n");
}
return 0;
}
代码运行结果
知识点6【文件目录操作函数】(重点)
常用文件目录操作函数:opendir readdir closedir 下面详细介绍
- 得到文件目录的句柄 opendir
句柄
句柄就是结构体指针
句柄我们在文件操作中也用到过,比如我们得到的FILE*就是一个文件句柄。FILE * 是一个结构体指针,结构体中存储的是文件信息
在文件目录的介绍中,我们先函数介绍功能,然后通过一个整体的项目带大家了解其功能
函数介绍
DIR *opendir(const char *name)
功能
打开一个目录
参数
name:目录名
返回值
成功:返回指向该目录的 结构体的指针(目录句柄)
失败:NULL
- 读取目录readdir
函数介绍
struct dirent *readdir(DIR *dirp)
功能介绍
读取目录,调用一次只能读取一个文件
参数
dirp:opendir的返回值
返回值
成功:目录结构体指针
失败:NULL
struct dirent 结构体介绍
d_type相关数据
- 关闭目录closedir
函数介绍
int close(DIR *dirp)
功能介绍
读取目录,调用一次只能读取一个文件
参数
dirp:opendir的返回值
返回值
成功:0
失败:1
代码演示
#include <sys/types.h>
#include <dirent.h>
#include <stdio.h>
#include <string.h>
void readDir(char *name);
int main(int argc, char const *argv[])
{
readDir("..");
return 0;
}
/**
* 读取一个目录中的内容
*
* @param const char *name 目录名
* @return 添加成功返回 1,否则返回 0
*/
void readDir(char *name)
{
//打开一个文件夹,并判断打开文件是否有效
DIR *dirp = opendir(name);
//读取文件夹 循环
struct dirent *read_dir;
while(read_dir = readdir(dirp))
{
if((read_dir->d_type & DT_REG) == DT_REG)
{
printf("%s是一个普通文件\n",read_dir->d_name);
}
else if((read_dir->d_type & DT_DIR) == DT_DIR)
{
//测试时 发现文件夹中有.. 和 . 文件夹,因此去掉
if (strcmp(read_dir->d_name, ".") == 0 || strcmp(read_dir->d_name, "..") == 0) {
continue; // 跳过本次循环
}
//处理递归目标目录
char dir_name[512] = "";
sprintf(dir_name,"%s/%s",name,read_dir->d_name);
//printf("dir_name = %s\n",dir_name);
printf("\n%s是一个文件夹,它的内部文件为:\n",read_dir->d_name);
readDir(dir_name);
}
}
//关闭目录
closedir(dirp);
}
结束
代码重在练习!
代码重在练习!
代码重在练习!
今天的分享就到此结束了,希望对你有所帮助,如果你喜欢我的分享,请点赞收藏夹关注,谢谢大家!!!