libuv库学习笔记-basics_of_libuv
Basics of libuv
libuv强制使用异步和事件驱动的编程风格。它的核心工作是提供一个event-loop,还有基于I/O和其它事件通知的回调函数。libuv还提供了一些核心工具,例如定时器,非阻塞的网络支持,异步文件系统访问,子进程等。
Event loops
在事件驱动编程中,程序会关注每一个事件,并且对每一个事件的发生做出反应。libuv会负责将来自操作系统的事件收集起来,或者监视其他来源的事件。这样,用户就可以注册回调函数,回调函数会在事件发生的时候被调用。event-loop会一直保持运行状态。用伪代码描述如下:
while there are still events to process:e = get the next eventif there is a callback associated with e:call the callback
举几个事件的例子:
- 准备好被写入的文件。
- 包含准备被读取的数据的socket。
- 超时的定时器。
event-loop最终会被uv_run()
启动-当使用libuv时,最后都会调用的函数。
系统编程中最经常处理的一般是输入和输出,而不是一大堆的数据处理。问题在于传统的输入/输出函数(例如read
,fprintf
)都是阻塞式的。实际上,向文件写入数据,从网络读取数据所花的时间,对比CPU的处理速度差得太多。任务没有完成,函数是不会返回的,所以你的程序在这段时间内什么也做不了。对于需要高性能的的程序来说,这是一个主要的障碍因为其他活动和I/O操作都在保持等待。
其中一个标准的解决方案是使用多线程。每一个阻塞的I/O操作都会被分配到各个线程中(或者是使用线程池)。当某个线程一旦阻塞,处理器就可以调度处理其他需要cpu资源的线程。
但是libuv使用了另外一个解决方案,那就是异步,非阻塞风格。大多数的现代操作系统提供了基于事件通知的子系统。例如,一个正常的socket上的read
调用会发生阻塞,直到发送方把信息发送过来。但是,实际上程序可以请求操作系统监视socket事件的到来,并将这个事件通知放到事件队列中。这样,程序就可以很简单地检查事件是否到来(可能此时正在使用cpu做数值处理的运算),并及时地获取数据。说libuv是异步的,是因为程序可以在一头表达对某一事件的兴趣,并在另一头获取到数据(对于时间或是空间来说)。它是非阻塞是因为应用程序无需在请求数据后等待,可以自由地做其他的事。libuv的事件循环方式很好地与该模型匹配, 因为操作系统事件可以视为另外一种libuv事件. 非阻塞方式可以保证在其他事件到来时被尽快处理(当然还要考虑硬件的能力)。
Note
我们不需要关心I/O在后台是如何工作的,但是由于我们的计算机硬件的工作方式,线程是处理器最基本的执行单元,libuv和操作系统通常会运行后台/工作者线程, 或者采用非阻塞方式来轮流执行任务。
Bert Belder,一个libuv的核心开发者,通过一个短视频向我们解释了libuv的架构和它的后台工作方式。如果你之前没有接触过类似libuv,libev,这个视频会非常有用。视频的网址是https://youtu.be/nGn60vDSxQ4 。
包含了libuv的event-loop的更多详细信息的文档。
HELLO WORLD
让我们开始写第一个libuv程序吧!它什么都没做,只是开启了一个loop,然后很快地退出了。
helloworld/main.c
#include <stdio.h>
#include <stdlib.h>
#include <uv.h>int main() {uv_loop_t *loop = malloc(sizeof(uv_loop_t));uv_loop_init(loop);printf("Now quitting.\n");uv_run(loop, UV_RUN_DEFAULT);uv_loop_close(loop);free(loop);return 0;
}
这个程序会很快就退出了,因为没有可以很处理的事件。我们可以使用各种API函数来告诉event-loop我们要监视的事件。
从libuv的1.0版本开始,用户就可以在使用uv_loop_init
初始化loop之前,给其分配相应的内存。这就允许你植入自定义的内存管理方法。记住要使用uv_loop_close(uv_loop_t *)
关闭loop,然后再回收内存空间。在例子中,程序退出的时候会关闭loop,系统也会自动回收内存。对于长时间运行的程序来说,合理释放内存很重要。
Default loop
可以使用uv_default_loop
获取libuv提供的默认loop。如果你只需要一个loop的话,可以使用这个。
Note
nodejs中使用了默认的loop作为自己的主loop。如果你在编写nodejs的绑定,你应该注意一下。
Error handling
初始化函数或者同步执行的函数,会在执行失败后返回代表错误的负数。但是对于异步执行的函数,会在执行失败的时候,给它们的回调函数传递一个状态参数。错误信息被定义为UV_E*
常量。
你可以使用uv_strerror(int)
和uv_err_name(int)
分别获取const char *
格式的错误信息和错误名字。
I/O函数的回调函数(例如文件和socket等)会被传递一个nread
参数。如果nread
小于0,就代表出现了错误(当然,UV_EOF是读取到文件末端的错误,你要特殊处理)。
##Handles and Requests
libuv的工作建立在用户表达对特定事件的兴趣。这通常通过创造对应I/O设备,定时器,进程等的handle来实现。handle是不透明的数据结构,其中对应的类型uv_TYPE_t
中的type指定了handle的使用目的。
libuv watchers
/* Handle types. */
typedef struct uv_loop_s uv_loop_t;
typedef struct uv_handle_s uv_handle_t;
typedef struct uv_stream_s uv_stream_t;
typedef struct uv_tcp_s uv_tcp_t;
typedef struct uv_udp_s uv_udp_t;
typedef struct uv_pipe_s uv_pipe_t;
typedef struct uv_tty_s uv_tty_t;
typedef struct uv_poll_s uv_poll_t;
typedef struct uv_timer_s uv_timer_t;
typedef struct uv_prepare_s uv_prepare_t;
typedef struct uv_check_s uv_check_t;
typedef struct uv_idle_s uv_idle_t;
typedef struct uv_async_s uv_async_t;
typedef struct uv_process_s uv_process_t;
typedef struct uv_fs_event_s uv_fs_event_t;
typedef struct uv_fs_poll_s uv_fs_poll_t;
typedef struct uv_signal_s uv_signal_t;/* Request types. */
typedef struct uv_req_s uv_req_t;
typedef struct uv_getaddrinfo_s uv_getaddrinfo_t;
typedef struct uv_getnameinfo_s uv_getnameinfo_t;
typedef struct uv_shutdown_s uv_shutdown_t;
typedef struct uv_write_s uv_write_t;
typedef struct uv_connect_s uv_connect_t;
typedef struct uv_udp_send_s uv_udp_send_t;
typedef struct uv_fs_s uv_fs_t;
typedef struct uv_work_s uv_work_t;/* None of the above. */
typedef struct uv_cpu_info_s uv_cpu_info_t;
typedef struct uv_interface_address_s uv_interface_address_t;
typedef struct uv_dirent_s uv_dirent_t;
handle代表了持久性对象。在异步的操作中,相应的handle上有许多与之关联的request。request是短暂性对象(通常只维持在一个回调函数的时间),通常对映着handle上的一个I/O操作。Requests用来在初始函数和回调函数之间,传递上下文。例如uv_udp_t代表了一个udp的socket,然而,对于每一个向socket的写入的完成后,都会向回调函数传递一个uv_udp_send_t
。
handle可以通过下面的函数设置:
uv_TYPE_init(uv_loop_t *, uv_TYPE_t *)
回调函数是libuv所关注的事件发生后,所调用的函数。应用程序的特定逻辑会在回调函数中实现。例如,一个IO监视器的回调函数会接收到从文件读取到的数据,一个定时器的回调函数会在超时后被触发等等。
Idling
下面有一个使用空转handle的例子。回调函数在每一个循环中都会被调用。在Utilities这部分会讲到一些空转handle的使用场景。现在让我们使用一个空转监视器,然后来观察它的生命周期,接着看uv_run
调用会造成阻塞。当达到事先规定好的计数后,空转监视器会退出。因为uv_run
已经找不到活着的事件监视器了,所以uv_run()
也退出。
idle-basic/main.c
#include <stdio.h>
#include <uv.h>int64_t counter = 0;void wait_for_a_while(uv_idle_t* handle) {counter++;if (counter >= 10e6)uv_idle_stop(handle);
}int main() {uv_idle_t idler;uv_idle_init(uv_default_loop(), &idler);uv_idle_start(&idler, wait_for_a_while);printf("Idling...\n");uv_run(uv_default_loop(), UV_RUN_DEFAULT);uv_loop_close(uv_default_loop());return 0;
}
Storing context
在基于回调函数的编程风格中,你可能会需要在调用处和回调函数之间,传递一些上下文等特定的应用信息。所有的handle和request都有一个data
域,可以用来存储信息并传递。这是一个c语言库中很常见的模式。即使是uv_loop_t
也有一个相似的data
域。
相关文章:
libuv库学习笔记-basics_of_libuv
Basics of libuv libuv强制使用异步和事件驱动的编程风格。它的核心工作是提供一个event-loop,还有基于I/O和其它事件通知的回调函数。libuv还提供了一些核心工具,例如定时器,非阻塞的网络支持,异步文件系统访问,子进…...

【Vuvuzela 声音去噪算法】基于流行的频谱减法技术的声音去噪算法研究(Matlab代码实现)
💥💥💞💞欢迎来到本博客❤️❤️💥💥 🏆博主优势:🌞🌞🌞博客内容尽量做到思维缜密,逻辑清晰,为了方便读者。 ⛳️座右铭&a…...

Vue + Element-ui组件上传图片报错问题解决方案
在前端开发中,我们经常需要模拟网络请求以进行单元测试或开发调试。而在模拟网络请求时,我们常常会使用到MockXMLHttpRequest对象。MockXMLHttpRequest对象是一个用于模拟XMLHttpRequest对象的工具,它提供了一种简单的方式来模拟网络请求&…...

java商城系统和php商城系统对比
java商城系统和php商城系统是两种常见的电子商务平台,它们都具有一定的优势和劣势。那么,java商城系统和php商城系统又有哪些差异呢? 一、开发难度 Java商城系统和PHP商城系统在开发难度方面存在一定的差异。Java商城系统需要使用Java语言进…...

某制造企业基于 KubeSphere 的云原生实践
背景介绍 随着业务升级改造与软件产品专案的增多,常规的物理机和虚拟机方式逐渐暴露出一些问题: 大量服务部署在虚拟机上,资源预估和硬件浪费较大;大量服务部署在虚拟机上,部署时间和难度较大,自动化程度…...

Electron 学习_BrowserWindow
BrowserWindow创建并控制浏览器窗口(主进程) 条件:在 app 模块 emitted ready 事件之前,您不能使用此模块。 1.在加载页面时,渲染进程第一次完成绘制时,如果窗口还没有被显示,渲染进程会发出 ready-to-show 事件 。 在…...

Docker学习笔记,包含docker安装、常用命令、dockerfile、docker-compose等等
😀😀😀创作不易,各位看官点赞收藏. 文章目录 Docker 学习笔记1、容器2、Docker 安装3、Docker 常用命令4、Docker 镜像5、自定义镜像5.1、镜像推送到阿里云5.2、镜像私有库 6、数据卷7、Docker 软件安装8、Docker File8.1、常见保…...
解决 “Module build failed (from ./node_modules/babel-loader/lib/index.js)“ 错误的方法
系列文章目录 文章目录 系列文章目录前言一、错误原因:二、解决方法:三、注意事项:总结 前言 在前端项目开发中,如果使用了 Babel 来转译 ES6 语法,有时会遇到错误信息 “Module build failed (from ./node_modules/b…...
go学习 6、方法
6、方法 面向对象编程(OOP),封装、组合。 6.1 方法声明 在函数声明时,在其名字之前放上一个变量,即是一个方法。这个附加的参数会将该函数附加到这种类型上,即相当于为这种类型定义了一个独占的方法。 …...

MySQL Windows版本下载及安装时默认路径的修改
提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 文章目录 前言一、MySQL 下载二、默认路径修改1、安装前准备【非常重要】2、启动安装程序总结1、MySQL下载2、MySQL默认路径修改前言 MySQL 被Oracle收购后,各种操作规范及约束也相应的跟着来了,这不,只…...

第3章 配置与服务
1 CoreCms.Net.Configuration.AppSettingsHelper using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Configuration.Json; namespace CoreCms.Net.Configuration { /// <summary> /// 【应用设置助手--类】 /// <remarks> /// 摘要&#x…...

Arcgis之 KML/KMZ文件转shp
一般我们在Goole Earth上勾画的区域导出后都为KML或者KMZ格式的,但无法在arcgis等软件上直接应用,故需进行一定的转换 1.打开ArcMap,选择ArcToolbox->Conversion Tools->From KML->KML To Layer 得到如下结果(由于本KML…...

python绘制3D条形图
文章目录 数据导入三维条形图bar3d 数据导入 尽管在matplotlib支持在一个坐标系中绘制多组条形图,效果如下 其中,蓝色表示中国,橘色表示美国,绿色表示欧盟。从这个图就可以非常直观地看出,三者自2018到2022年的GDP变化…...
计算从曲线的起点到param指定的点的曲线段的长度
以下方法只能用于继承于AcDbCurve的类型 主要使用两个接口 派生类中此函数的实现应返回, 并将endParam设置为曲线端点的参数。 如果成功则返回Acad::eOk。 默认情况下, 该函数返回Acad::eNotImplemented。 virtual Acad::ErrorStatus getEndParam(double&endParam) cons…...

POLARDB IMCI 白皮书 云原生HTAP 数据库系统 一 数据压缩和打包处理与数据更新
开头还是介绍一下群,如果感兴趣polardb ,mongodb ,mysql ,postgresql ,redis 等有问题,有需求都可以加群群内有各大数据库行业大咖,CTO,可以解决你的问题。加群请联系 liuaustin3 ,在新加的朋友会分到2群(共…...
linux----源码安装如何加入到系统服务中(systemclt)
将自己源码安装的软件加入到系统服务中。例如nginx,mysql 就以nginx为例,源码安装,加入到系统服务中 使用yum安装nginx,自动会加入到系统服务 16-Linux系统服务 - 刘清政 - 博客园 (cnblogs.com) 第一步: 源码安装好nginx之后࿰…...
Unity 使用UnityWebRequest 读取存档 (IOS只能这样做)
打IOS包的时候发现的,不能使用正常的IO流读取,不然会读取不到数据,只能使用UnityWebRequest 读取 代码如下 public IEnumerator ReadArchive(Action<bool, string> ac, string filepath ""){UnityWebRequest request Unit…...
Caused by: org.springframework.beans.factory.
问题解决:Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name IUserRepository defined in app.test4.OpportunityMatching.IUserRepository defined in EnableJpaRepositories declared on JpaRepositoriesRegistrar.Enable…...
【docker 安装】 与【docker-compose 安装】
不同的操作系统需要不同的docker安装文件:具体下载位置: Docker: https://download.docker.com/linux/static/stable/ docekr-compose:https://github.com/docker/compose/releases 1. 验证客户机器是否有docker 和docker-compose docker -…...

意外:WPS编程新工具,不用编程,excel用户:可以不用VBA啦
来来来,拓宽一下视野! 别总以为excel和WPS只能用VBA编程,也别总是想着ACCESS这些老生常谈的工具。其实对于电子表格高级用户来讲,不会VBA,不用ACCESS,也一样可以解决复杂问题或者高级应用。 尤其是WPS用户…...
java_网络服务相关_gateway_nacos_feign区别联系
1. spring-cloud-starter-gateway 作用:作为微服务架构的网关,统一入口,处理所有外部请求。 核心能力: 路由转发(基于路径、服务名等)过滤器(鉴权、限流、日志、Header 处理)支持负…...
MySQL 隔离级别:脏读、幻读及不可重复读的原理与示例
一、MySQL 隔离级别 MySQL 提供了四种隔离级别,用于控制事务之间的并发访问以及数据的可见性,不同隔离级别对脏读、幻读、不可重复读这几种并发数据问题有着不同的处理方式,具体如下: 隔离级别脏读不可重复读幻读性能特点及锁机制读未提交(READ UNCOMMITTED)允许出现允许…...

基于Flask实现的医疗保险欺诈识别监测模型
基于Flask实现的医疗保险欺诈识别监测模型 项目截图 项目简介 社会医疗保险是国家通过立法形式强制实施,由雇主和个人按一定比例缴纳保险费,建立社会医疗保险基金,支付雇员医疗费用的一种医疗保险制度, 它是促进社会文明和进步的…...

让AI看见世界:MCP协议与服务器的工作原理
让AI看见世界:MCP协议与服务器的工作原理 MCP(Model Context Protocol)是一种创新的通信协议,旨在让大型语言模型能够安全、高效地与外部资源进行交互。在AI技术快速发展的今天,MCP正成为连接AI与现实世界的重要桥梁。…...

Android15默认授权浮窗权限
我们经常有那种需求,客户需要定制的apk集成在ROM中,并且默认授予其【显示在其他应用的上层】权限,也就是我们常说的浮窗权限,那么我们就可以通过以下方法在wms、ams等系统服务的systemReady()方法中调用即可实现预置应用默认授权浮…...

sipsak:SIP瑞士军刀!全参数详细教程!Kali Linux教程!
简介 sipsak 是一个面向会话初始协议 (SIP) 应用程序开发人员和管理员的小型命令行工具。它可以用于对 SIP 应用程序和设备进行一些简单的测试。 sipsak 是一款 SIP 压力和诊断实用程序。它通过 sip-uri 向服务器发送 SIP 请求,并检查收到的响应。它以以下模式之一…...
AGain DB和倍数增益的关系
我在设置一款索尼CMOS芯片时,Again增益0db变化为6DB,画面的变化只有2倍DN的增益,比如10变为20。 这与dB和线性增益的关系以及传感器处理流程有关。以下是具体原因分析: 1. dB与线性增益的换算关系 6dB对应的理论线性增益应为&…...
c# 局部函数 定义、功能与示例
C# 局部函数:定义、功能与示例 1. 定义与功能 局部函数(Local Function)是嵌套在另一个方法内部的私有方法,仅在包含它的方法内可见。 • 作用:封装仅用于当前方法的逻辑,避免污染类作用域,提升…...
比较数据迁移后MySQL数据库和ClickHouse数据仓库中的表
设计一个MySQL数据库和Clickhouse数据仓库的表数据比较的详细程序流程,两张表是相同的结构,都有整型主键id字段,需要每次从数据库分批取得2000条数据,用于比较,比较操作的同时可以再取2000条数据,等上一次比较完成之后,开始比较,直到比较完所有的数据。比较操作需要比较…...
C/Python/Go示例 | Socket Programing与RPC
Socket Programming介绍 Computer networking这个领域围绕着两台电脑或者同一台电脑内的不同进程之间的数据传输和信息交流,会涉及到许多有意思的话题,诸如怎么确保对方能收到信息,怎么应对数据丢失、被污染或者顺序混乱,怎么提高…...