🔥博客主页: 我要成为C++领域大神
🎥系列专栏:【C++核心编程】 【计算机网络】 【Linux编程】 【操作系统】
❤️感谢大家点赞👍收藏⭐评论✍️
本博客致力于分享知识,欢迎大家共同学习和交流。
从常规文件读取不会阻塞;无论读取多少数据,read
函数都会在有限的时间内返回。
从终端设备或网络读取可能会阻塞。例如,如果从终端输入的数据缺少换行符,则调用终端设备的 read
函数会阻塞。如果网络上没有接收到数据包,则调用网络的 read
函数会阻塞。至于阻塞的持续时间是不确定的。如果没有数据到达,进程将会无限期地保持阻塞状态。
“阻塞”:当进程调用一个阻塞的系统函数时,它会进入睡眠(sleep)状态,此时内核会调度其他进程运行,直到等待的事件发生(比如在网络上收到数据包,或者等待指定的sleep睡眠时间结束),进程才会继续执行。睡眠状态的相对状态是运行(Running)状态。在 Linux 内核中,运行状态的进程分为两种情况:
- 主动调度执行:CPU 正在执行进程的指令,读写其地址空间,并在通用寄存器中保存中间结果。
- 就绪状态:进程随时可以执行,但当前 CPU 正在执行另一个进程。因此,进程在就绪队列中等待内核调度。系统中可能同时有多个就绪的进程。内核的调度算法基于优先级和时间片,动态调整每个进程的优先级和时间片,以确保公平执行,并响应用户交互。
下面我们将用read,write,fcntl函数实现阻塞和非阻塞读取终端
read
1、功能:read
函数用于从已打开的设备或文件中读取数据。
2、头文件:read
函数的声明位于 <unistd.h>
头文件中。
3、函数定义:
ssize_t read(int fd, void *buf, size_t count);
这是 read
函数的函数原型,它接受三个参数:文件描述符 fd
、存储读取数据的缓冲区地址 buf
和要读取的字节数 count
。
4、返回值:read
函数的返回值是 ssize_t
类型,表示成功读取的字节数。如果出错,则返回 -1,并设置全局变量 errno
指示错误类型。如果在调用 read
前已经到达文件末尾,则返回 0。
5、参数说明:
fd
:要读取的文件的文件描述符,它是通过 open
等函数打开文件时返回的。
buf
:要将读取的数据存储到哪个缓冲区中,这个缓冲区通常是一个指向数据的指针。
count
:请求读取的字节数,读取到的数据将会保存到缓冲区 buf
中,并且文件的当前读写位置会向后移动相应的字节数。
write
1、功能:write
函数用于从打开的设备或文件中写入数据。
2、头文件:write
函数的声明位于 <unistd.h>
头文件中。
3、函数定义:
ssize_t write(int fd, const void *buf, size_t count);
这是 write
函数的函数原型,它接受三个参数:文件描述符 fd
、要写入的数据缓冲区地址 buf
和要写入的字节数 count
。
4、返回值:write
函数的返回值是 ssize_t
类型,表示成功写入的字节数。如果出错,则返回 -1,并设置全局变量 errno
指示错误类型。如果在调用 write
之前已经到达文件末尾,则 write
返回 0。
5、参数说明:
fd
:要写入的文件的文件描述符,它是通过 open
等函数打开文件时返回的。
buf
:存储要写入内容的缓冲区的地址,这个缓冲区可以是任何内存中的位置,通常是一个指向数据的指针。
count
:请求写入的字节数,将要写入的数据保存在缓冲区 buf
中,并移动文件的当前读写位置。
fcntl
1、功能:fcntl
函数用于对文件描述符进行控制操作,包括复制描述符、获取/设置文件描述符标志以及获取/设置文件状态标志等。
2、头文件:fcntl
函数的声明位于 <fcntl.h>
头文件中。
3、函数定义:
int fcntl(int fd, int cmd, ... /* arg */ );
fcntl
函数接受三个参数:文件描述符 fd
、控制命令 cmd
以及可选的参数 arg
。
当函数有两个参数时,是获取权限;
当函数有三个参数时,是设置权限
4、返回值:fcntl
函数的返回值根据不同的命令而异。一般情况下,成功执行时返回值取决于命令的具体含义,失败时返回 -1,并设置全局变量 errno
指示错误类型。
5、参数说明:
fd
:要操作的文件的文件描述符。
cmd
:控制命令,用于指定要执行的操作。常见的命令包括:
F_DUPFD
:复制文件描述符。
F_GETFL
:获取文件描述符的标志。
F_SETFL
:设置文件描述符的标志。
F_GETFD
:获取文件状态标志。
F_SETFD
:设置文件状态标志。等等,还有其他命令用于进行不同的操作。
arg
:可选参数,用于传递给指定命令的参数。具体参数类型和含义取决于命令。
函数实现:
阻塞读取:
阻塞读取不需要使用fcntl函数改变标准输入符权限,因为本身标准输入本身就是阻塞的。
将标准输入读取到的数据写入到标准输出即可。
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
int main()
{
char buf[10] = "";
int nLength = read(STDIN_FILENO, buf, sizeof(buf));
write(STDOUT_FILENO, buf, nLength);
return 0;
}
阻塞读取标准输入,当读取到标准输入按下回车后,程序结束的速度很快
非阻塞读取:
非阻塞读取需要使用fcntl函数改变标准输入符权限为非阻塞。
1、fcntl获取标准输入权限
2、设置标准输入权限为非阻塞
3、读取标准输入
4、写入到标准输出中去
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
int main()
{
// 获取当前标准输入权限
int flags = fcntl(STDIN_FILENO, F_GETFL);
// 改变当前标准输入权限为非阻塞
flags |= O_NONBLOCK;
char buf[10] = "";
int nLength;
while (1)
{
nLength = read(STDIN_FILENO, buf, sizeof(buf)); // 读取标准输入
if (nLength == -1)
{
if (errno == EAGAIN)
{ // 没有读取到数据
sleep(1); // 等待一秒,再重新读
continue;
}
else
{ // 文件读取失败
perror("read fail");
return -1;
}
}
else
{
break; // 读取成功,退出循环
}
}
write(STDOUT_FILENO, buf, nLength); // 写入到标准输出中去
return 0;
}
非阻塞读取标准输入,当读取到标准输入按下回车后,有时程序退出比较慢,因为可能出去非阻塞的sleep时间。