lv8 嵌入式开发-网络编程开发 14

目录

[1 I/O基本概念](#1 I/O基本概念)

[1.1 IO概念](#1.1 IO概念)

[1.2 同步和异步](#1.2 同步和异步)

[1.3 阻塞和非阻塞](#1.3 阻塞和非阻塞)

[2 五种I/O模型](#2 五种I/O模型)

[2.1 阻塞IO](#2.1 阻塞IO)

[2.2 非阻塞I/O](#2.2 非阻塞I/O)

[2.3 多路复用I/O](#2.3 多路复用I/O)

[​编辑 2.4 信号驱动式I/O](#编辑 2.4 信号驱动式I/O)

​编辑

[2.5 异步I/O模型​编辑](#2.5 异步I/O模型编辑)

[3 五种I/O模型比较](#3 五种I/O模型比较)

[4 练习](#4 练习)


1 I/O基本概念

1.1 IO概念

  • I/O即数据的读取(接收)或写入(发送)操作
  • 通常用户进程中的一个完整I/O分为两个阶段

用户进程空间<-->内核空间

内核空间<-->设备空间(磁盘、网卡等)

  • I/O分为内存I/O、网络I/O和磁盘I/O三种

1.2 同步和异步

  • 对于一个线程的请求调用来讲,同步和异步的区别在于是否要等这个请求出最终结果
  • 对于多个线程而言,同步或异步就是线程间的步调是否要一致、是否要协调
  • 同步也经常用在一个线程内先后两个函数的调用上
  • 异步就是一个请求返回时一定不知道结果,还得通过其他机制来获知结果,如:主动轮询或被动通知

1.3 阻塞和非阻塞

  • 阻塞与非阻塞与等待消息通知时的状态(调用线程)有关
  • 阻塞和同步是完全不同的概念。同步是对于消息的通知机制而言,阻塞是针对等待消息通知时的状态来说的
  • 进程从创建、运行到结束总是处于下面五个状态之一:新建状态、就绪状态、运行状态、阻塞状态及死亡状态

线程在运行过程中,可能由于以下几种原因进入阻塞状态:

  • 线程通过调用sleep方式进休眠状态
  • 线程调用一个在I/O上被阻塞的操作,即该操作在输入/输出操作完成前不会返回到它的调用者
  • 线程试图得到一个锁,而该锁正被其他线程持有,于是只能进入阻塞状态,等到获取了同步锁,才能恢复执行
  • 线程在等待某个触发条件

可能阻塞套接字的Linux Sockets API调用分为以下四种

  • 输入操作
  • 输出操作
  • 接受连接accept
  • 外出连接connect

2 五种I/O模型

2.1 阻塞IO

内核也有数据缓存区,数据好了再拷贝到应用的缓存区中,包括read,accept、connect等

2.2 非阻塞I/O

示例

read

cpp 复制代码
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <errno.h>

#define FIFO_NAME "/tmp/myfifo"

int main(int argc, char *argv[]) {
    int fd, ret;
    char buf[BUFSIZ] = {};

    // 创建有名管道
    if (mkfifo(FIFO_NAME, 0666) == -1) {
        perror("mkfifo");
		exit(0);
    }

    fd = open(FIFO_NAME, O_RDONLY|O_NONBLOCK);  //非阻塞方式NONBLOCK
	if(fd < 0) {
		perror("open");
		exit(0);
	}

	while(1) {
	//	do {
			ret = read(fd, buf, BUFSIZ); 
	//	} while (ret < 0 && errno == EAGAIN);   //如果读失败了,并且原因是EAGAIN才会反复轮询读
	//	if(ret < 0){                 //如果满足EGAIN但是,但是RET<0,是其他异常情况
	//		perror("read");
	//		exit(0);
	//	}
		if(buf[0] == '#')
			break;
		printf("Read from pipe: %s\n", buf);
	}

    // 关闭管道并删除有名管道文件
    close(fd);
	unlink(FIFO_NAME);

    return 0;
}

该程序通过使用read函数从文件描述符fd中读取数据,存储在buf中。BUFSIZ是一个常量,表示可以读取的最大字节数。当读取成功时,read函数返回实际读取的字节数,如果返回值小于0,则表示读取出现错误。

程序使用循环来反复读取数据,直到遇到以#开头的字符串,才会跳出循环。在读取过程中,如果读取失败了,并且原因是EAGAIN,则会一直轮询读取,直到成功读取为止。

注释掉的部分是对异常情况的处理,如果出现其他异常情况,则会输出错误信息并退出程序。

write函数仅用来测试,无特别说明

cpp 复制代码
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>

#define FIFO_NAME "/tmp/myfifo"

int main(void) {
    int fd;
    char buf[BUFSIZ];

    // 打开有名管道并进行读写操作
    fd = open(FIFO_NAME, O_WRONLY);
	if( fd < 0 ) {
		perror("open");
		exit(0);
	}
	while(1) {
		fgets(buf, BUFSIZ, stdin);
		if (write(fd, buf, BUFSIZ) < 0 ) {
			perror("write");
			exit(0);
		}
		if(buf[0] == '#')
			break;
	}

    close(fd);
    return 0;
}

2.3 多路复用I/O

与read区别,可以监听多个文件描述符,有的可能有数据,有的可能没有数据。

如果有数据准备好的描述符,返回可读条件,如哪个文件描述符好了。

如果有数据了,调用recvfrom拷贝数据报,再进行处理。如果有多个文件描述符就执行多个这样的过程。

2.4 信号驱动式I/O

注册完就可以干别的事情了,类似异步操作。

上面4种,在数据报准备好之后拷贝数据的时候都是阻塞的,都算作是同步IO,都需要recv读一下。

2.5 异步I/O模型

内核无数据到数据报拷贝完成,这两部都是非阻塞的,应用程序干干什么就干什么。

3 五种I/O模型比较

4 练习

画出5种I/O模型调用过程

相关推荐
独行soc14 分钟前
2025年渗透测试面试题总结-2025年HW(护网面试) 33(题目+回答)
linux·科技·安全·网络安全·面试·职场和发展·护网
java龙王*32 分钟前
开放端口,开通数据库连接权限,无法连接远程数据库 解决方案
linux
bcbobo21cn1 小时前
Linux命令的命令历史
linux·histsize·histfile
岸边的风2 小时前
无需公网IP的文件交互:FileCodeBox容器化部署技术解析
网络·网络协议·tcp/ip
jingyu飞鸟2 小时前
linux系统源代码安装apache、编译隐藏版本号
linux·运维·apache
世事如云有卷舒2 小时前
Ubunt20.04搭建GitLab服务器,并借助cpolar实现公网访问
linux·服务器·gitlab
Zonda要好好学习2 小时前
Python入门Day4
java·网络·python
riverz12273 小时前
TCP backlog工作机制
服务器·网络·tcp/ip
2401_858286113 小时前
OS15.【Linux】gdb调试器的简单使用
linux·运维·服务器·开发语言·gdb
yqcoder3 小时前
1. http 有哪些版本,你是用的哪个版本,怎么查看
网络·网络协议·http