IPC之十二:使用libdbus在D-Bus上异步发送/接收信号的实例
IPC 是 Linux 编程中一个重要的概念,IPC 有多种方式,本 IPC 系列文章的前十篇介绍了几乎所有的常用的 IPC 方法,每种方法都给出了具体实例,前面的文章里介绍了 D-Bus 的基本概念以及调用远程方法的实例,本文介绍 D-Bus 的异步处理机制,以及信号处理的基本方法,本文给出了异步处理 D-Bus 的实例,附有完整的源代码;本文实例在 Ubuntu 20.04 上编译测试通过,gcc版本号为:9.4.0;本文不适合 Linux 编程的初学者阅读。
1 D-Bus 的信号(Signal)
-
在阅读本文之前,建议阅读关于 D-Bus 的另一篇文章 《IPC之十一:使用D-Bus实现客户端向服务端请求服务的实例》
-
在文章 《IPC之十一:使用D-Bus实现客户端向服务端请求服务的实例》 中,介绍了服务端如何在 D-Bus 上提供方法调用服务以及客户端如何向服务端请求一个方法调用服务;
-
通过 D-Bus 向服务端请求方法调用服务,仅仅是 D-Bus 一半的功能,D-Bus 还支持异步的广播通信方法,这种机制称为信号(Signal),当服务端需要向大量接收者发送通知时,该机制非常有用;
-
举例来说,如果系统正在关闭、网络连接中断以及类似的系统范围内的情况,相关系统服务进程应该广播一个通知,使对这些服务有需求的进程能够及时做出反应,这样一种方式,使得接收信号的进程无需轮询服务状态;
-
D-Bus 的信号(Signal)与调用方法(Method Call)有许多类似的地方,这里简要回顾一下在上一篇文章中讨论的调用方法的概念:
- 服务程序连接 D-Bus(dbus-daemon),获得一个连接,D-Bus 随机给这个连接分配一个唯一名称;
- 为该连接绑定一个固定的名称(Bus Name),以方便客户端访问这个连接,总线名称通常以反向域名的形式命名;
- 在该连接下可以建立一个或多个对象(Object),对象名称以路径(类似文件系统路径)表示;
- 在每个对象上可以建立一个或者多个接口(Interface),接口的名称也是使用反向域名的命名方式;
- 每个接口下可以有一个或者多个方法(Method);
- 客户端需要请求服务端的某个方法时,需要知道总线名称、对象路径、接口名称以及方法名称,并将调用参数传送给服务端;
-
信号(Signal)也是建立在一个接口下,一个接口下不仅可以有一个或者多个方法,还可以有一个或者多个信号,
- 1 – 4 同上;
- 每个接口下可以有一个或者多个信号(Signal),信号的命名与调用方法一样,没有很多规则,比如:sig_demo
- 客户端想要收到某个信号时,需要向总线注册,告知总线感兴趣的信号(包括:对象路径、接口名称和信号名称),只能收到向总线注册过的信号;
-
所以其实一个接口下可以有若干个方法和信号,除此之外,接口下还可以有若干个属性(Properties),方法、信号、属性组合在一起构成一个接口;
-
本文仅讨论接口,有关属性的事情,以后的文章中再讨论;
-
发送信号通常是服务端的事情,信号通常是以广播的方式发出,而只有订阅了这个消息的客户端才能收到消息,实际上,信号也是可以点对点发送的(仅发给指定客户端),这个以后讨论,先讨论通常的广播信号;
-
服务端发送信号的步骤
- 使用
dbus_bus_get()
连接到 D-Bus,获得一个连接 DBusConnection; - 使用
dbus_message_new_signal()
为构建信号初始化一个 DBusMessge; - 使用
dbus_message_append_args()
将信号参数添加到信号的 DBusMessage 中; - 使用
dbus_connection_send()
将信号放入发送队列; - 使用
dbus_connection_flush()
将发送队列的消息全部发送出去; - 使用
dbus_message_unref()
释放信号的 DBusMessage;
- 使用
-
整个过程与在文章 《IPC之十一:使用D-Bus实现客户端向服务端请求服务的实例》 中描述的客户端向服务端请求一个服务的过程高度相似,但要简单一些:
- 请求服务时是客户端向服务端发出请求,而发送信号时是服务端发送一个广播消息;
- 请求服务时,服务端通常要在连接上绑定一个公用的名称(总线名称),客户端在请求服务时必须要指定这个总线名称,否则 D-Bus 不知道你要向哪个应用程序请求服务,发送信号时,服务端不一定需要在连接上绑定名称,因为通常客户端只需要接收信号,不需要向服务端发送任何消息;
- 请求服务时,客户端在发出请求后通常需要服务端的回复,发送信号时没有任何回复消息;
-
客户端要接收到信号,需要订阅指定的信号,D-Bus 只会把你订阅的信号推送过来;
-
使用
dbus_bus_add_match()
订阅信号:void dbus_bus_add_match(DBusConnection *connection,const char *rule,DBusError *error);
- connection 是使用
dbus_bus_get()
获得的连接; - error 已经在很多函数调用中出现过,不多说了;
- 这个 rule 参数是这个函数的灵魂,这是一个字符串,这个字符串定义了一个规则,告诉 D-Bus 我要订阅符合这个规则的信号;
- 这个规则使用
key/value
的形式描述,可以有多个key/value
对用于描述多个条件,每个key/value
对用 “,” 分隔; - 举个 rule 的例子:
"type='signal',sender='cn.whowin.dbus', path='/cn/whowin/dbus', interface='cn.whowin.dbus_iface',member='notify'"
- 在这个例子中,
type='signal'
表示消息类型为信号,sender 是发送方的总线名称,path 是发送方的对象路径,interface 是发送方的接口名称,member 是信号名称,D-Bus 会把符合这些条件的信号推送到订阅的进程中; - 在描述规则时不用把条件写的这么全,比如:
"type='signal',sender='cn.whowin.dbus',path='/cn/whowin/dbus'"
,则从cn.whowin.dbus
的对象/cn/whowin/dbus
发出的消息都可以收到; - 在这个调用中,如果 error 参数为 NULL,则调用
dbus_bus_add_match()
后会立即返回,不会产生阻塞,但是订阅不会生效,需要执行dbus_connection_flush(conn)
后订阅才会生效,而且如果发生了错误程序也是无法知晓的,所以,不建议这样做; - 目前 rule 可以使用的
key/value
对中的 key 可以为:type, sender, interface, member, path, destination; - destination 指的是目的地址,比如:
:275.6
,在广播信号中通常用不上; - 下面这段代码订阅了一个信号:
DBusError dbus_error; DBusConnection *conn;dbus_error_init(&dbus_error); conn = dbus_bus_get(DBUS_BUS_SESSION, &dbus_error);dbus_bus_add_match(conn, "type='signal',path='/cn/whowin/dbus/signal',interface='cn.whowin.dbus_iface'", &dbus_error); ......
- connection 是使用
-
如果有必要,你可以使用
dbus_bus_add_match()
订阅多个信号。
2 libdbus 的异步处理机制
-
客户端并不知道什么时候会有信号发出来,所以为了能及时收到信号必须不断轮询,像这样:
DBusConnection *conn; DBusMessage *message; ...... while (dbus_connection_read_write_dispatch(conn, -1)) {// loopmessage = dbus_connection_pop_message(conn);if (message == NULL) {usleep(10000);continue;}if (dbus_message_get_type(message) != DBUS_MESSAGE_TYPE_SIGNAL) {usleep(10000);continue;}...... }
-
函数
dbus_connection_read_write_dispatch()
在文章 《IPC之十一:使用D-Bus实现客户端向服务端请求服务的实例》 中做过介绍; -
显然,这样的编程模式并不高效,尤其是当程序不仅仅是要接收信号,还有其他工作要做时,这种程序架构就显得更加不可接受;
-
实际上,libdbus 还提供了另外一种异步接收信息的方式,像下面这样的代码:
DBusHandlerResult signal_filter(DBusConnection *connection, DBusMessage *message, void *usr_data) {DBusError dbus_error;dbus_error_init(&dbus_error);if (dbus_message_get_type(message) != DBUS_MESSAGE_TYPE_SIGNAL) {printf("Client: This is not a signal.\n");return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;}......return DBUS_HANDLER_RESULT_HANDLED; }int main() {DBusError dbus_error;DBusConnection *conn;dbus_error_init(&dbus_error);conn = dbus_bus_get(DBUS_BUS_SESSION, &dbus_error);dbus_connection_add_filter(conn, signal_filter, NULL, NULL);dbus_bus_add_match(conn, "type='signal',path='/cn/whowin/dbus/signal',interface='cn.whowin.dbus_iface'", &dbus_error);while (dbus_connection_read_write_dispatch(conn, -1)) {/* loop */......}......return; }
- 在主程序中,首先使用
dbus_connection_add_filter()
添加了一个过滤器(Filter),然后再用dbus_bus_add_match()
订阅感兴趣的信号; - 在下面的 while 循环中,并不需要去接收消息,当订阅的信号到来时,会直接调用过滤器,在过滤器里处理收到的信号即可;
- 所以,上面这段程序实际上是在函数
signal_filter()
中处理信号;
- 在主程序中,首先使用
-
函数
dbus_connection_add_filter()
原型dbus_bool_t dbus_connection_add_filter(DBusConnection *connection,DBusHandleMessageFunction function,void *user_data,DBusFreeFunction free_data_function )
- connection 为通过
dbus_bus_get()
获得的连接; - function 为过滤器调用的函数;
- user_data 为传递给 function 的参数;
- free_data_function 为释放 user_data 需要调用的函数;
- connection 为通过
-
DBusHandleMessageFunction
的定义typedef DBusHandlerResult(* DBusHandleMessageFunction)(DBusConnection *connection,DBusMessage *message,void *user_data);
- 所以在
dbus_connection_add_filter()
中,function 是一个函数指针,该函数将接收三个参数,第一个是从dbus_bus_get()
获得的连接,第二个参数是一个消息结构DBusMessage
,表示收到的消息,第三个参数是用户数据,在使用dbus_connection_add_filter()
添加过滤器时设置; - 当一个过滤器函数被调用时,收到的消息已经在 message 中了;
- 这个过滤器函数的返回值是 DBusHandlerResult,这是一个枚举类型,其值有三个:
DBUS_HANDLER_RESULT_HANDLED
、DBUS_HANDLER_RESULT_NOT_YET_HANDLED
和DBUS_HANDLER_RESULT_NEED_MEMORY
; DBUS_HANDLER_RESULT_HANDLED
表示该过滤器函数已经获得了一个有效消息并进行了处理,该消息无需再交给其他过滤器处理;DBUS_HANDLER_RESULT_NOT_YET_HANDLED
表示该过滤器没有处理该消息,如果有其他过滤器,可以把该消息交给其他过滤器处理;DBUS_HANDLER_RESULT_NEED_MEMORY
通常用不上;
- 所以在
-
调用过滤器函数是由 libdbus 实现的,应该是在调用
dbus_connection_read_write_dispatch()
时,当有可读消息时,调用过滤器函数; -
调用过滤器函数后的返回值并不会返回到应用程序中,但是对其它过滤器可能会产生影响,当系统内有多个过滤器时,当前过滤器返回
DBUS_HANDLER_RESULT_HANDLED
意味着已经处理好了这个消息,不必再使用其它过滤器处理该消息;DBUS_HANDLER_RESULT_NOT_YET_HANDLED
意味着这个消息没有在当前过滤器中被处理,如果有其它过滤器,应该尝试使用其它过滤器处理;
-
所以,过滤器函数的返回一定要正确,否则会有消息丢失;
-
如果有必要,你可以添加多个过滤器,去处理不同的消息;
-
过滤器的概念,其实也不仅仅可以用在接收信号上,也可以用在调用方法上;
-
尽管我们向 D-Bus 订阅了我们感兴趣的信号,但其实有时也会一些不符合订阅条件的信号到来,所以,在程序中还是要做一些判断,以确保收到的是我们期望的信号,如果不是,返回
DBUS_HANDLER_RESULT_NOT_YET_HANDLED
,让其它过滤器去处理。
3 使用 libdbus 异步接收信号的实例
-
源程序:dbus-signals.c(点击文件名下载源程序,建议使用UTF-8字符集)演示了使用 libdbus 对信号进行发送和接收,以及如何异步接收信号;
-
该程序是一个多进程程序,建立了一个服务端进程和三个客户端进程;
-
服务端进程在启动后发送出一个内容为 “start” 的信号,暂停 5 秒后,再发出一个内容为 “quit” 的信号,然后退出进程;
-
服务端在发送信号时,其对象路径、接口名称和信号名称均相同;
-
客户端进程订阅了服务端的信号,并添加了两个过滤器,一个用于处理内容为 “start” 的信号,另一个用于处理内容为 “quit” 的信号,这里仅是为了演示多个过滤器的工作方式;
-
客户端检查收到的信号,如果其内容为 “quit”,则退出进程;
-
编译:
gcc -Wall -g dbus-signals.c -o dbus-signals `pkg-config --libs --cflags dbus-1`
-
有关
pkg-config --libs --cflags dbus-1
可以参阅文章 《IPC之十一:使用D-Bus实现客户端向服务端请求服务的实例》 中的简要说明; -
运行:
./dbus-signals
-
运行截图:
-
程序运行后,客户端进程的两个过滤器都显示了 “Wrong object path” 的信息,这条信息是 D-Bus 为客户端连接分配了名称后发送过来的通知信号,虽然我们没有订阅,但 D-Bus 会强行推送过来;
-
这条通知信号在经过过滤器时,过滤器返回了 “DBUS_HANDLER_RESULT_NOT_YET_HANDLED”,因为这个返回值导致这个消息在经过第一个过滤器后还会再进入第二个过滤器进行处理,如果过滤器在遇到对象路径不对时返回 “DBUS_HANDLER_RESULT_HANDLED”,则这条消息不会再去第二个过滤器,读者可以尝试修改程序看看是不是这样;
-
如果你多次运行这个程序你会发现,信号总是首先到达
signal_quit()
过滤器,然后才到达signal_start()
过滤器,这是因为我们先添加的signal_quit()
过滤器,如果你改动一下程序,先添加signal_start()
过滤器,再添加signal_quit()
过滤器,你会看到信号到达的顺序也会发生变化。
欢迎订阅 『进程间通信专栏』
相关文章:
IPC之十二:使用libdbus在D-Bus上异步发送/接收信号的实例
IPC 是 Linux 编程中一个重要的概念,IPC 有多种方式,本 IPC 系列文章的前十篇介绍了几乎所有的常用的 IPC 方法,每种方法都给出了具体实例,前面的文章里介绍了 D-Bus 的基本概念以及调用远程方法的实例,本文介绍 D-Bus…...
ES6之生成器(Generator)
✨ 专栏介绍 在现代Web开发中,JavaScript已经成为了不可或缺的一部分。它不仅可以为网页增加交互性和动态性,还可以在后端开发中使用Node.js构建高效的服务器端应用程序。作为一种灵活且易学的脚本语言,JavaScript具有广泛的应用场景&#x…...
Matlab技巧[绘画逻辑分析仪产生的数据]
绘画逻辑分析仪产生的数据 逻分上抓到了ADC数字信号,一共是10Bit,12MHZ的波形: 这里用并口协议已经解析出数据: 导出csv表格数据(这个数据为补码,所以要做数据转换): 现在要把这个数据绘制成波形,用Python和表格直接绘制速度太慢了,转了一圈发现MATLAB很好用,操作方法如下:…...
Go面试题学习
1.并发安全性 Go语言中的并发安全性是什么?如何确保并发安全性? 并发安全性是指在并发编程中,多个goroutine对共享资源的访问不会导致数据竞争和不确定的结果。 使用互斥锁(Mutex):通过使用互斥锁来保护…...
SQL效率-查询条件需避免使用函数处理索引字段
一个sql效率的问题 问题 假设created_at 是date类型、是索引,那么以下2种方式有没效率差异: WHERE TO_CHAR(created_at, ‘YYYY-MM-DD’) ‘2020-02-01’WHERE created_at TO_DATE(‘2020-02-01’ , ‘YYYY-MM-DD’) DBA回复 有的,第一…...
【Spring 篇】Spring:轻松驾驭 Java 世界的利器
在 Java 开发领域,Spring 框架无疑是一颗璀璨的明星,它不仅提供了全面的企业级特性,还为开发者提供了简便而强大的开发方式。本文将深入探讨 Spring 框架的简介、配置和快速入门,带你轻松驾驭 Java 世界的利器。 Spring 简介 Sp…...
八个LOGO素材网站推荐分享
即时设计资源广场 在UI界面设计中,为了找到合适的图标icon,你有没有尝试过翻遍整个网络,找到自己想要的,却无法下载或收费使用?最后,只收集图标icon材料需要半天时间。专业设计师使用的图标icon设计材料“…...
React格式化规范
React并没有特定的格式要求,它允许开发者根据自己的喜好和项目需求来选择代码的格式化风格。然而,在React社区中有一些常见的约定和最佳实践,以下是一些常用的格式化规范和建议: 缩进:使用2个或4个空格来进行缩进&…...
如何利用Conda管理多种虚拟环境与Jupyter Notebook内核切换
写在开头 在数据科学与机器学习领域,项目之间可能存在不同的依赖关系和版本要求。为了有效管理这些差异,使用虚拟环境成为一种标准实践。本文将介绍如何利用Conda这一强大的环境管理工具,结合Jupyter Notebook,使得在不同项目之间灵活切换变得轻而易举。 2. Conda简介 2…...
博客摘录「 什么是QPS、TPS、吞吐量?- 高并发名词概念」2024年1月5日
1.什么是高并发? 高并发(High Concurrency)。通常是指系统在短时间内的大量操作。 高并发相关的常见指标有:QPS、TPS、吞吐量、并发数等。 2.QPS(Query Per Second) QPS每秒查询率,是指系统…...
PTA——逆序的三位数
程序每次读入一个正3位数,然后输出按位逆序的数字。注意:当输入的数字含有结尾的0时,输出不应带有前导的0。比如输入700,输出应该是7。 输入格式: 每个测试是一个3位的正整数。 输出格式: 输出按位逆序…...
ChatGPT怎么帮我上班的
1.解放生产力 1)标准格式,完美输出。GPT对于公文等具有一定标准格式的文件,可以进行完美仿写,随随便便以假乱真那都是小菜一碟,这对于经常要开展规范成文的人来说,简直就是个福音,只要前期调教…...
WPF 漂亮长方体、正文体简单实现方法 Path实现长方体 正方体方案 WPF快速实现长方体、正方体的方法源代码
这段XAML代码在WPF中实现了一个类似长方体视觉效果的图形 声明式绘制:通过Path、PathGeometry和PathFigure等元素组合,能够以声明方式精确描述长方体每个面的位置和形状,无需编写复杂的绘图逻辑,清晰直观。 层次结构与ZIndex控制…...
Nginx(十三) 配置文件详解 - 反向代理(超详细)
本篇文章主要讲ngx_http_proxy_module和ngx_stream_proxy_module模块下各指令的使用方法。 1. 代理请求 proxy_pass 1.1 proxy_pass 代理请求 Syntax: proxy_pass URL; Default: — Context: location, if in location, limit_except 设置代理服务器的协议和地址以…...
谷歌浏览器启用实时字幕功能
在 Chrome 中使用“实时字幕”功能 - Google Chrome帮助 在 Chrome 中使用“实时字幕”功能 从计算机上的 Chrome 浏览器中,您可以使用“实时字幕”功能自动为视频、播客、游戏、直播、视频通话或其他音频媒体生成字幕。音频和字幕均在本地处理,并会保…...
php接口优化 使用curl_multi_init批量请求
PHP使用CURL同时抓取多个URL地址 抓取多个URL地址是Web开发中常见的需求,使用PHP的curl库可以简化这个过程。本文将详细介绍如何使用PHP的curl库同时请求多个URL地址,并提供具体的代码案例和注释。 curl库介绍 curl是一个常用的开源网络传输工具&…...
联邦拜占庭共识算法的工作流程
1 前言 联邦拜占庭共识算法(Federated Byzantine Agreement,简称FBA)是一种解决分布式系统中拜占庭问题的共识算法,是拜占庭容错共识算法里的其中一种,主要应用于区块链技术中。这种算法允许系统内部的各种节点自由进…...
国家开放大学形成性考核 统一考试 学习资料参考
试卷代号:11119 机械CAD/CAM 参考试题 一、单项选择题(从所给的四个选项中,找出你认为是正确的答案,将其编号填入括号内。每小题3分,共45分) 1.下述CAD/CAM过程的概念中,属于CAM范畴的是&am…...
2401C++,实现文件服务器和聊天室
文件服务器 使用yalantinglibs,几行代码开发静态文件服务器 最近的workshop上的一个任务,就是实现一个文件服务器,只要设置下载目录之后,就可下载目录里面的文件. 看看用yalantinglibs怎么实现一个静态文件服务器的吧. coro_http::coro_http_server server(1, 9001); server.…...
【ESP-NOW 入门(ESP32 with Arduino IDE)】
ESP-NOW 入门(ESP32 with Arduino IDE) 1. 前言2. Arduino集成开发环境3. ESP-NOW 简介3.1 ESP-NOW 支持以下功能:3.2 ESP-NOW 技术还存在以下局限性:4. ESP-NOW 单向通信4.1 一个 ESP32 开发板向另一个 ESP32 开发板发送数据4.2 一个“主”ESP32 向多个 ESP32“slave”发送…...
PHP序列化总结2--常见的魔术方法
魔术方法的概念 PHP的魔术方法是一种特殊的方法,用于覆盖PHP的默认操作。它们以双下划线(__)开头,后面跟着一些特定的字符串,如__construct()、__destruct()、__get()等。这些魔术方法在对象执行特定操作时被自动调用…...
Docker 入门 ------容器互通以及Dockerfile
1. 端口映射以及容器互联 Docker 除了通过网络访问,还提供了两种很方便的功能来满足服务访问的基本需求: 允许映射容器内应用的服务端口到本地宿主主机互联机制实现多个容器间通过容器名来快速访问 1.1 容器映射实现访问容器 1.1.1 从外部访问容器应…...
AI绘图模型不会写字的难题解决了
介绍 大家好,最近有个开源项目比较有意思,解决了图像中不支持带有中文的问题。 https://github.com/tyxsspa/AnyText。 为什么不能带有中文? 数据集局限 Stable Diffusion的训练数据集以英文数据为主,没有大量包含其他语言文本的…...
vue-cli创建项目时由esLint校验导致报错或警告的问题及解决
vue-cli创建项目时由esLint校验导致报错或警告的问题及解决 一、万能办法 一、万能办法 //就是在报错的JS文件中第一行写上 /* eslint-disable */链接: https://www.yii666.com/blog/288808.html 其它的方法我遇见了再补充...
uniapp:实现手机端APP登录强制更新,从本地服务器下载新的apk更新,并使用WebSocket,实时强制在线用户更新
实现登录即更新,或实时监听更新 本文介绍的是在App打开启动的时候调用更新,点击下方链接,查看使用WebSocket实现实时通知在线用户更新。 uniapp:全局消息是推送,实现app在线更新,WebSocket,ap…...
设计循环队列——oj题622
. 个人主页:晓风飞 专栏:LeetCode刷题|数据结构|Linux 路漫漫其修远兮,吾将上下而求索 文章目录 题目要求:应该支持如下操作:示例:提示: 结构体定义队列的创建基本操作判断队列是否为空…...
阿里后端实习一面面经
阿里后端实习一面面经 项目中使用到了es,es的作用? elasticsearch是一款非常强大的开源搜索引擎,具备非常多强大功能,可以帮助我们从海量数据中快速找到需要的内容 es中的重要概念? 群集:一个或多个节点…...
element-ui组件DatePicker日期选择器移动端兼容
element-ui组件DatePicker日期选择器移动端兼容 css /** 移动端展示 **/ media screen and (max-width: 500px) {.el-picker-panel__sidebar {width: 100%;}.el-picker-panel {width: 400px!important;}.el-picker-panel__content {width: 100%;}.el-picker-panel__body{marg…...
burpsuite 爆破
靶场搭建:phpstudy的安装与靶场搭建 - junlin623 - 博客园 (cnblogs.com) 账号字典:XXTK: 一些弱口令、fuzz字典 (gitee.com) 网盘链接:https://pan.baidu.com/s/1v5pAwaTwoeCnJgkUXf3iLQ?pwd=mllm 提取码:mllm --来自百度网盘超级会员V2的分享 一、暴力破解 - 基于…...
SparkSQL基础解析(三)
1、 Spark SQL概述 1.1什么是Spark SQL Spark SQL是Spark用来处理结构化数据的一个模块,它提供了2个编程抽象:DataFrame和 DataSet,并且作为分布式SQL查询引擎的作用。 我们已经学习了Hive,它是将Hive SQL转换成MapReduce然后提…...
做夺宝网站要办理什么意思/竞价推广和信息流推广
医学统计学计算机操作教程语音编辑锁定上传视频《医学统计学计算机操作教程》是2007年科学出版社出版的图书,作者是罗家洪、郭秀花。作 者罗家洪//郭秀花ISBN9787030333711页 数304定 价39.80元出版社科学出版时间2007-8医学统计学计算机操作教程内容介绍编…...
旅游投资公司网站建设ppt模板/手机百度云电脑版入口
本文不对数据挖掘的具体数学知识做全面的总结,根据网路上前辈的理解,对其需要的数学方面的知识做一个一般性的总结。 引言 数据挖掘,是指从大量数据中获取隐含的、潜在的是有价值信息的过程,是近年来计算机领域火热的研究内容。 数…...
江津哪个网站可以做顺风车/百度宣传做网站多少钱
百度文库中有叫《Ecmall二次开发之结构图》的。还收费许高。 ecmall/eccore /ecmall.php 常量: define(START_TIME, ecm_microtime()); /* 判断请求方式 */ define(IS_POST, (strtoupper($_SERVER[REQUEST_METHOD]) POST));//判断请求方式是不是post传来的…...
化妆培训学校网站源码 下载/小红书推广怎么做
1.如何让图表的Y轴不从0开始显示:有时一系列的数据差别很小,如果从0开始显示,在Y轴上,会一堆数据都堆在某一个区间。例如期货的蜡烛图。将ViewportRangeEnabled设为true即可解决此问题。代码: Axis axis new Axis(); …...
做自媒体有哪些网站/新站seo快速排名 排名
本次每个号送 3 本一共 24 本,还包邮哦。可能你有疑问,送什么书籍呢?不用担心,这次的书籍包你满意哦,都是热门的 Java 书籍。感谢本次活动的赞助者:北京大学出版社。01 书怎么送很简单,在下面的…...
做外贸批发的网站/b2b平台运营模式
双指针 867. 链表的中间结点 可以通过 快慢指针 来完成,快指针每次走两步,慢指针每次走一步,当快指针走到尽头,慢指针就到达了链表的中点。 重点在于临界条件的处理,快指针最后有两种状态: 指向链表的最后一个元素【链表元素为奇数个】 指向空 【链表元素为偶数个】 所…...