openbmc dbus架构简析(二)

1.说明

以前看内核代码觉得难,是因为内核代码涉及到硬件原理与算法结构和层次递进的代码逻辑,现在的应用层因为业务的复杂与代码和内核的交互接口复杂,也变得有些难度了。

这篇文章是继:openbmc dbus架构简析的第二篇文章。

首先贴出来前篇文章的图,与简述内容:

* 1.inherit_fds()使用systemd机制获取到socket描述符(请先了解systemd的socket机制原理,会先接手socket服务,accept默认为no,因此需要sd_listen_fds()方法获取socket描述符)
* 2.父子进程通过socketpair(PF_UNIX, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 0, controller);中的controller[0]和controller[1]进行连接
* 3.sd_bus_set_fd(launcher->bus_controller, controller[0], controller[0]);使用该函数,实际bus->input_fd = input_fd;bus->output_fd = output_fd; 实际即为controller[0]
* 4.sd_bus_call(launcher->bus_controller, m, 0, NULL, NULL);发送socket数据实际依靠的是controller[0],而子进程使用的是controller[1],通道打通,数据可以传递给子进程
* 5.sd_bus_message_append(m, "oh","/org/bus1/DBus/Listener/0",launcher->fd_listen);把launcher->fd_listen 为总体的systemd的监听的socket描述符传递给了子进程。

在这里插入图片描述

2.代码分析

2.1 代码使用的描述符

2.1.1 dbus-broker-launch使用的描述符

根据代码,列出来使用到的描述符:

static int run(void)
---> launcher_new()
	---> r = launcher_open_log(launcher);
		---> fd = socket(PF_UNIX, SOCK_DGRAM | SOCK_CLOEXEC, 0); //新建了描述符, fd=4
	---> sd_event_default(&launcher->event);
		--->...
		---> e->epoll_fd = epoll_create1(EPOLL_CLOEXEC); //创建了描述, epoll_fd=5.
	---> r = sd_event_add_signal(launcher->event, NULL, SIGTERM, NULL, NULL);
	---> r = sd_event_add_signal(launcher->event, NULL, SIGTERM, NULL, NULL);
	---> r = sd_event_add_signal(launcher->event, NULL, SIGTERM, NULL, NULL);
		---> r = event_make_signal_data(e, sig, &d);
			---> r = signalfd(d->fd >= 0 ? d->fd : -1,...) 使用了signalfd创建的fd=6
--->  r = launcher_run(launcher);
	---> r = launcher_parse_config(launcher, &root, &nss_cache);
		---> r = dirwatch_new(&dirwatch); 
			---> dw->inotify_fd = inotify_init1(IN_CLOEXEC | IN_NONBLOCK); //新建fd=7
		---> r = config_parser_read(&parser, rootp, configfile, nss_cache, dirwatch);
			---> r = config_parser_include(parser, root, node, nss_cache, dirwatch);
				---> _c_cleanup_(c_closep) int fd = -1;
				---> r = open(node->include.file->path, O_RDONLY | O_CLOEXEC | O_NOCTTY);
				---> ... //这里 fd=8, 实际需要调用close() ?  使用_c_cleanup_语法实际关闭了
	---> r = socketpair(PF_UNIX, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 0, controller);
	---> ...返回了2个套接字 分别为 controller[0] = 8 和 controller[0] = 9

如下图,可以看到基本的文件描述符均为默认状态:
在这里插入图片描述
参考文档: https://www.jinbuguo.com/systemd/systemd.exec.html#, 描述了如下内容:

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
因此,fd=0,fd=1,fd=2的结果如下:
在这里插入图片描述
因此,对于openbmc,常见的printf,fprintf(stderr…)均会输出到journal中。

  • 对于fd=3继承于systemd,接收外部的服务。
  • 对于fd=4创建socket,用来将数据传递到:/run/systemd/journal/socket
  • 对于fd=5, 创建e->epoll_fd
  • 对于fd=6,使用signalfd()创建fd
  • 对于fd=7,使用inotify_init1()创建fd
  • 对于fd=8,fd=9,使用socketpair()创建fd,用来父子进程数据通信

再回到开头的那副图,可以看看子进程与父进程之间,父进程通过sd-bus接口调用将fd(systemd监控转储给dbus-broker的socket)描述符传递给子进程。
在这里插入图片描述
继续分析文件:src\launch\launcher.c:

static int launcher_add_listener(Launcher *launcher, Policy *policy,...)
---> r = sd_bus_message_new_method_call(launcher->bus_controller, &m,NULL,"/org/bus1/DBus/Broker","org.bus1.DBus.Broker","AddListener");
	---> _cleanup_(sd_bus_message_unrefp) sd_bus_message *t = NULL;
	---> r = sd_bus_message_new(bus, &t, SD_BUS_MESSAGE_METHOD_CALL);
		---> t->header->endian = BUS_NATIVE_ENDIAN;
		---> t->header->type = type;
		---> t->header->version = bus->message_version;
		---> t->allow_fds = bus->can_fds || !IN_SET(bus->state, BUS_HELLO, BUS_RUNNING);
---> r = sd_bus_message_append(m, "oh","/org/bus1/DBus/Listener/0",launcher->fd_listen);
---> r = policy_export(policy, m, system_console_users, n_system_console_users);
---> r = sd_bus_call(launcher->bus_controller, m, 0, NULL, NULL);

实际上,这里面的重点是函数:sd_bus_call(),在文件:src\libsystemd\sd-bus\sd-bus.c中:

int sd_bus_call(...)
---> bus_write_message()
	---> bus_socket_write_message()
		---> if (m->n_fds > 0 && *idx == 0)
			---> mh.msg_controllen = CMSG_SPACE(sizeof(int) * m->n_fds);
			---> mh.msg_control = alloca0(mh.msg_controllen);
			---> control = CMSG_FIRSTHDR(&mh);
			---> control->cmsg_len = CMSG_LEN(sizeof(int) * m->n_fds);
			---> control->cmsg_level = SOL_SOCKET;
			---> control->cmsg_type = SCM_RIGHTS;
			---> memcpy(CMSG_DATA(control), m->fds, sizeof(int) * m->n_fds);
		---> k = sendmsg(bus->output_fd, &mh, MSG_DONTWAIT|MSG_NOSIGNAL);

使用如上调用步骤传递文件描述符给另外一个进程,使用的方法是父子进程之间通过sendmsg()调用。

继续查看函数sd_bus_message_append()的调用关系:

int sd_bus_message_append(sd_bus_message *m, const char *types, ...)
---> r = sd_bus_message_appendv(m, types, ap); //type是字符串"oh"
---> switch (*t)
	---> case SD_BUS_TYPE_UNIX_FD:
		---> uint32_t x;
		---> x = va_arg(ap, uint32_t);
		---> r = sd_bus_message_append_basic(m, *t, &x);
			---> u32 = m->n_fds;
	---> case SD_BUS_TYPE_OBJECT_PATH:
		---> const char *x;
		---> x = va_arg(ap, const char*);
		---> r = sd_bus_message_append_basic(m, *t, x);

这些sd-bus类型有必要列出来,在文件:src\systemd\sd-bus-protocol.h中:

enum {
        _SD_BUS_TYPE_INVALID         = 0,
        SD_BUS_TYPE_BYTE             = 'y',
        SD_BUS_TYPE_BOOLEAN          = 'b',
        SD_BUS_TYPE_INT16            = 'n',
        SD_BUS_TYPE_UINT16           = 'q',
        SD_BUS_TYPE_INT32            = 'i',
        SD_BUS_TYPE_UINT32           = 'u',
        SD_BUS_TYPE_INT64            = 'x',
        SD_BUS_TYPE_UINT64           = 't',
        SD_BUS_TYPE_DOUBLE           = 'd',
        SD_BUS_TYPE_STRING           = 's',
        SD_BUS_TYPE_OBJECT_PATH      = 'o',
        SD_BUS_TYPE_SIGNATURE        = 'g',
        SD_BUS_TYPE_UNIX_FD          = 'h',
        SD_BUS_TYPE_ARRAY            = 'a',
        SD_BUS_TYPE_VARIANT          = 'v',
        SD_BUS_TYPE_STRUCT           = 'r', /* not actually used in signatures */
        SD_BUS_TYPE_STRUCT_BEGIN     = '(',
        SD_BUS_TYPE_STRUCT_END       = ')',
        SD_BUS_TYPE_DICT_ENTRY       = 'e', /* not actually used in signatures */
        SD_BUS_TYPE_DICT_ENTRY_BEGIN = '{',
        SD_BUS_TYPE_DICT_ENTRY_END   = '}'
};

在文件:src\broker\controller.c中,函数:controller_init()调用:

int controller_init(Controller *c, Broker *broker, int controller_fd)
---> r = connection_init_server(&controller->connection,
                                   &broker->dispatcher,controller_dispatch_connection,
                                   broker->bus.user,"0123456789abcdef",controller_fd);

其中,函数controller_dispatch_connection()定义:

static int controller_dispatch_connection(DispatchFile *file) 
---> r = connection_dispatch(&controller->connection, dispatch_file_events(file));
---> r = connection_dequeue(&controller->connection, &m);
	---> r = socket_dequeue(&connection->socket, &message);
		---> r = message_new_incoming(&message, socket->in.header);
---> r = message_parse_metadata(m);
	---> r = message_parse_header(message, &message->metadata);
		---> switch (field)
			---> case DBUS_MESSAGE_FIELD_PATH:
			---> case DBUS_MESSAGE_FIELD_INTERFACE:
			---> case DBUS_MESSAGE_FIELD_MEMBER:
			---> case DBUS_MESSAGE_FIELD_REPLY_SERIAL:
			---> case DBUS_MESSAGE_FIELD_UNIX_FDS:
				---> c_dvar_read(&v, "<u>)", c_dvar_type_u, &metadata->fields.unix_fds);
	---> r = message_parse_body(message, &message->metadata);
---> r = controller_dbus_dispatch(controller, m);
	---> switch (message->header->type) {
		---> case DBUS_MESSAGE_TYPE_METHOD_CALL:
			---> r = controller_dispatch_object(controller,
               message_read_serial(message),message->metadata.fields.interface,
               message->metadata.fields.member,message->metadata.fields.path,
               message->metadata.fields.signature,message);
               ---> if (strcmp(path, "/org/bus1/DBus/Broker") == 0)
               		---> controller_dispatch_controller(controller, serial, member, path, signature, message);
               			---> controller_method_add_listener( ... )
               				---> listener_fd = fdlist_get(fds, fd_index);
               				---> r = controller_add_listener(controller, &listener, path, listener_fd, policy);
               					---> listener_init_with_fd(...)
               						---> listener->bus = bus;
               						---> dispatch_file_init(.., listener_dispatch,... )					

这里面的判断:message->header->type来自于src\launch\launcher.csd_bus_message_new_method_call( )

最后,调用到函数listener_dispatch():

static int listener_dispatch(DispatchFile *file)
---> fd = accept4(listener->socket_fd, NULL, NULL, SOCK_CLOEXEC | SOCK_NONBLOCK);
2.1.2 socket takeover

需要首先查阅该篇文章: Zero Downtime Release: Disruption-free Load Balancing of a Multi-Billion User Website.

可参考文件src\util\log.c中的函数log_fd_send():

static int log_fd_send(int destination_fd, int payload_fd)
---> control.cmsg.cmsg_level = SOL_SOCKET;
---> control.cmsg.cmsg_type = SCM_RIGHTS;
---> l = sendmsg(destination_fd, &msg, MSG_NOSIGNAL);
2.1.3 描述符附录

简单看一下应用程序使用的描述符:

在这里插入图片描述

root@evb-ast2500:~# ps | grep -i "dbus"
  191 messageb  5836 S    /usr/bin/dbus-broker-launch --scope system --audit
  192 messageb  2756 S    dbus-broker --log 4 --controller 9 --machine-id c47c0d3d042848a1908818ca62f0644e --max-bytes 536870912 --max-fds 4096 --max-matches 16384 --audit
  344 root      2952 S    grep -i dbus
root@evb-ast2500:~#
root@evb-ast2500:~# ls -al /proc/191/fd
dr-x------    2 root     root            13 Feb 27 17:54 .
dr-xr-xr-x    8 messageb messageb         0 Feb 27 17:54 ..
lr-x------    1 root     root            64 Feb 27 17:54 0 -> /dev/null
lrwx------    1 root     root            64 Feb 27 17:54 1 -> socket:[2242]
lrwx------    1 root     root            64 Feb 27 17:55 10 -> anon_inode:[pidfd]
lrwx------    1 root     root            64 Feb 27 17:55 11 -> anon_inode:[timerfd]
lrwx------    1 root     root            64 Feb 27 17:55 12 -> /memfd:dbus-broker-log (deleted)
lrwx------    1 root     root            64 Feb 27 17:54 2 -> socket:[2242]
lrwx------    1 root     root            64 Feb 27 17:54 3 -> socket:[2210]
lrwx------    1 root     root            64 Feb 27 17:54 4 -> socket:[2246]
lrwx------    1 root     root            64 Feb 27 17:55 5 -> anon_inode:[eventpoll]
lrwx------    1 root     root            64 Feb 27 17:55 6 -> anon_inode:[signalfd]
lr-x------    1 root     root            64 Feb 27 17:55 7 -> anon_inode:inotify
lrwx------    1 root     root            64 Feb 27 17:55 8 -> socket:[2254]
lrwx------    1 root     root            64 Feb 27 17:54 9 -> socket:[2303]
root@evb-ast2500:~#

2.2 bus的建立

2.2.1 launcher bus

在文件:src\launch\launcher.c中调用如下函数launcher_new():

int launcher_new(Launcher **launcherp, int fd_listen, bool audit, const char *configfile, bool user_scope)
---> r = sd_bus_new(&launcher->bus_controller);

另外函数:launcher_run()调用如下:

int launcher_run(Launcher *launcher)
---> r = sd_bus_set_fd(launcher->bus_controller, controller[0], controller[0]);
---> r = sd_bus_start(launcher->bus_controller);
	---> bus_set_state(bus, BUS_OPENING);
	---> if (bus->input_fd >= 0)  
		---> r = bus_start_fd(bus);
		---> return bus_send_hello(bus);//直接返回,并未调用"hello"

由于开启的服务是:
在这里插入图片描述

在函数:launcher_connect()中:

static int launcher_connect(Launcher *launcher)
---> r = sd_bus_open_system(&launcher->bus_regular);
	---> sd_bus_open_system_with_description(ret, NULL);
		---> r = sd_bus_new(&b);
		---> r = bus_set_address_system(b);
		---> b->bus_client = true;
		---> b->is_local = true;
		--->  r = sd_bus_start(b);
			---> bus_set_state(bus, BUS_OPENING);
			---> r = bus_start_address(bus);
			---> return bus_send_hello(bus);
				---> r = sd_bus_message_new_method_call(
                        bus, &m,"org.freedesktop.DBus","/org/freedesktop/DBus",
                        "org.freedesktop.DBus","Hello");
                ---> sd_bus_call_async(bus, NULL, m, hello_callback, NULL, 0);

设置系统默认的bus:"unix:path=/run/dbus/system_bus_socket".
在如下调用关系中:

static int listener_dispatch(DispatchFile *file)
---> ...
---> r = peer_dispatch(&peer->connection.socket_file);
	---> r = peer_dispatch_connection(peer, dispatch_file_events(file) & interest[i]);
		---> r = connection_dispatch(&peer->connection, events);
		---> r = connection_dequeue(&peer->connection, &m);
		---> r = message_parse_metadata(m);
		---> r = driver_dispatch(peer, m);
			---> r = driver_dispatch_internal(peer, message);
				---> if (string_equal(message->metadata.fields.destination, "org.freedesktop.DBus"))
					---> driver_dispatch_interface(peer,...) 	
						---> static const DriverInterface interfaces[] = {
						--->  { "org.freedesktop.DBus", driver_methods },
						---> { "org.freedesktop.DBus.Monitoring", monitoring_methods },
						---> ...	
						---> }

其中,函数:driver_methods():

static const DriverMethod driver_methods[] = {
        { "Hello",  false,  NULL,  driver_method_hello, ...}

static int driver_method_hello(Peer *peer, const char *path, CDVar *in_v, uint32_t serial, CDVar *out_v)
---> peer_register(peer);
	---> peer->registered = true;
---> unique_name = address_to_string(&(Address)ADDRESS_INIT_ID(peer->id)); //获取unique name
---> c_dvar_write(out_v, "(s)", unique_name); //将unique name返回
---> r = driver_send_reply(peer, out_v, serial);
	---> driver_send_reply_with_fds(peer, var, serial, NULL, 0);
		---> r = message_new_outgoing(&message, data, n_data);
		---> r = driver_send_unicast(peer, message);
---> r = driver_name_owner_changed(peer->bus, &peer->name_owner_changed_matches, NULL, NULL, peer);
	---> r = driver_notify_name_owner_changed(bus, matches, name, old_owner_str, new_owner_str);
		---> .fields = {
			---> .path = "/org/freedesktop/DBus",
			---> .interface = "org.freedesktop.DBus",
			---> .member = "NameOwnerChanged",
		---> }

在这里插入图片描述

3.分析一下权威代码

3.1 LogControl

可以查看网站:https://www.freedesktop.org/software/systemd/man/latest/org.freedesktop.LogControl1.html 或者systemd的代码: man\logcontrol-example.c.

注意:此处是建立一个server

int main(int argc, char **argv)
---> r = sd_bus_default(&bus);
	---> b->bus_client = true;
---> r = sd_bus_add_object_vtable(bus, NULL,
        "/org/freedesktop/LogControl1","org.freedesktop.LogControl1",vtable,&o);
---> r = sd_bus_request_name(bus, "org.freedesktop.Example", 0);
	---> r = sd_bus_call_method(
                        bus,"org.freedesktop.DBus","/org/freedesktop/DBus",
                        "org.freedesktop.DBus","RequestName",NULL,&reply,"su",name,param);
    ---> r = sd_bus_message_read(reply, "u", &ret);
---> for()
	---> {
		---> r = sd_bus_wait(bus, UINT64_MAX);
			---> r = bus_poll(bus, false, timeout_usec);
		---> r = sd_bus_process(bus, NULL);
			---> bus_process_internal(bus, ret);
	---> }

另外,systemd的代码:src\busctl\busctl.c是客户端代码,查看如下命令:

 * $ busctl --user set-property org.freedesktop.Example \
 *                              /org/freedesktop/LogControl1 \
 *                              org.freedesktop.LogControl1 \
 *                              LogLevel \
 *                              "s" debug

代码调用如下:

static int run(int argc, char *argv[])
---> busctl_main(argc, argv);
	---> r = acquire_bus(false, &bus);
		---> r = sd_bus_new(&bus);
		---> sd_bus_set_description(bus, "busctl");
		---> r = sd_bus_set_bus_client(bus, true);
			bus->bus_client = b;
		---> r = sd_bus_set_watch_bind(bus, arg_watch_bind);
		---> r = sd_bus_start(bus);
			---> bus_send_hello(bus);
	---> r = sd_bus_message_new_method_call(bus, &m, argv[1], argv[2],
                                           "org.freedesktop.DBus.Properties", "Set");
    ---> r = sd_bus_message_append(m, "ss", argv[3], argv[4]);
    ---> r = sd_bus_message_open_container(m, 'v', argv[5]);
    ---> r = message_append_cmdline(m, argv[5], &passed_fdset, &p);
    ---> r = sd_bus_message_close_container(m);
    ---> r = sd_bus_call(bus, m, arg_timeout, &error, NULL);

4.TBD…

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/929080.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Qt复习学习

https://www.bilibili.com/video/BV1Jp4y167R9/?spm_id_from333.999.0.0&vd_sourceb3723521e243814388688d813c9d475f https://subingwen.cn/qt/qt-primer/#1-4-Qt%E6%A1%88%E4%BE%8B https://subingwen.cn/qt/ https://download.qt.io/archive/qt/1.1Qt的特点 1.2QT中的…

9.13[debug]

这个错误表明 Git 尝试通过 HTTPS 协议连接到 Gitee 上的仓库时&#xff0c;实际上却尝试连接到了本地的 127.0.0.1&#xff08;即 localhost&#xff09;的 7890 端口&#xff0c;这通常是因为 HTTP 代理配置错误或全局 Git 配置中的代理设置不正确 如果这些命令返回了代理设…

第2章:CSS基本语法 --[CSS零基础入门]

CSS&#xff08;层叠样式表&#xff0c;Cascading Style Sheets&#xff09;是用来描述HTML或XML&#xff08;包括各种XML&#xff1a;SVG, MathML 或 XHTML&#xff09;等文档的外观和格式的语言。以下是CSS的基本语法&#xff1a; 1.选择器 1.元素选择器 元素选择器是基于…

12月2日星期一今日早报简报微语报早读

12月2日星期一&#xff0c;农历十一月初二&#xff0c;早报#微语早读。 1、公安部&#xff1a;全国机动车所有人12月2日起均可申领电子行驶证&#xff1b; 2、2025年国考笔试开考&#xff1a;参考率约为86.7%&#xff0c;约65人录1人&#xff1b; 3、今日头条、拼多多等9款A…

JDK17 下载与安装

下载安装包 针对不同的操作系统, 需要下载对应版本的 JDK. 如果电脑是 Windows32 位的, 建议重装系统, 重装成 64 位的操作系统. 因为 Java 从 9 版本开始, 就已经不提供 32 位版本的安装包了. 官网下载 官网下载链接 需要登录 Oracle 账号才能下载. 账号: 2872336204qq.c…

2021数学分析【南昌大学】

2021 数学分析 求极限 lim ⁡ n → ∞ 1 n ( n + 1 ) ( n + 2 ) ⋯ ( n + n ) n \lim_{n \to \infty} \frac{1}{n} \sqrt [n]{(n+1)(n+2) \cdots (n+n)} n→∞lim​n1​n(n+1)(n+2)⋯(n+n) ​ lim ⁡ n → ∞ 1 n ( n + 1 ) ( n + 2 ) ⋯ ( n + n ) n = lim ⁡ n → ∞ ( n + …

Android 消息队列之MQTT的使用:物联网通讯,HTTP太重了,使用MQTT;断网重连、注册、订阅、发送数据和接受数据,实现双向通讯。

目录&#xff1a; 问题MQTT是什么以及为什么使用如何使用&#xff1a;第一阶段、基础功能如何使用&#xff1a;第二阶段、增加断网重连如何使用&#xff1a;第三阶段、封装 一、问题 在开发的时候&#xff0c;我们一般都使用Http和后台进行通讯&#xff0c;比如我们是开发物联…

node.js基础学习-express框架-静态资源中间件express.static(十一)

前言 在 Node.js 应用中&#xff0c;静态资源是指那些不需要服务器动态处理&#xff0c;直接发送给客户端的文件。常见的静态资源包括 HTML 文件、CSS 样式表、JavaScript 脚本、图片&#xff08;如 JPEG、PNG 等&#xff09;、字体文件和音频、视频文件等。这些文件在服务器端…

全面解析 Transformer:改变深度学习格局的神经网络架构

目录 一、什么是 Transformer&#xff1f; 二、Transformer 的结构解析 1. 编码器&#xff08;Encoder&#xff09; 2. 解码器&#xff08;Decoder&#xff09; 3. Transformer 模型结构图 三、核心技术&#xff1a;注意力机制与多头注意力 1. 注意力机制 2. 多头注意力&…

LobeChat-46.6k星!顶级AI工具集,一键部署,界面美观易用,ApiSmart 是你肉身体验学习LLM 最好IDEA 工具

LobeChat LobeChat的开源&#xff0c;把AI功能集合到一起&#xff0c;真的太爽了。 我第一次发现LobeChat的时候&#xff0c;就是看到那炫酷的页面&#xff0c;这么强的前端真的是在秀肌肉啊&#xff01; 看下它的官网&#xff0c;整个网站的动效简直闪瞎我&#xff01; GitH…

AC+AP漫游实验

实验拓扑 实验要求 1.AP1服务vlan10&#xff0c;AP2服务vlan20&#xff0c;实现三层漫游 2.AP1与AP2为不同AP组&#xff0c;直接转发 实验步骤 1.配置VLAN放行相关流量 交换机与AP接口为trunk口并修改PVID为30 2.配置相关业务使得ap上线 3.配置vap上线&#xff0c;AP可用…

浅谈CI持续集成

1.什么是持续集成 持续集成&#xff08;Continuous Integration&#xff09;&#xff08;CI&#xff09;是一种软件开发实践&#xff0c;团队成员频繁地将他们的工作成果集成到一起(通常每人每天至少提交一次&#xff0c;这样每天就会有多次集成)&#xff0c;并且在每次提交后…

JUnit介绍:单元测试

1、什么是单元测试 单元测试是针对最小的功能单元编写测试代码&#xff08;Java 程序最小的功能单元是方法&#xff09;单元测试就是针对单个Java方法的测试。 2、为什么要使用单元测试 确保单个方法运行正常&#xff1b; 如果修改了代码&#xff0c;只需要确保其对应的单元…

Active RIS-Aided ISAC Systems: Beamforming Design and Performance Analysis

文章目录 II. SYSTEM MODELC. Active RIS Model III. PROBLEM FORMULATIONA. Radar Performance MetricC. Optimize Φ V. PERFORMANCE ANALYSIS OF THE RADAR SINR IN ACTIVE RIS-AIDED SENSING SYSTEMSA. Simplified System SettingB. Power Scaling Law AnalysisC. Active R…

python之Django连接数据库

文章目录 连接Mysql数据库安装Mysql驱动配置数据库信息明确连接驱动定义模型在模型下的models.py中定义表对象在settings.py 中找到INSTALLED_APPS添加创建的模型 测试testdb.py中写增删改查操作urls.py添加请求路径启动项目进行测试 连接Mysql数据库 安装Mysql驱动 pip inst…

网页端五子棋对战(四)---玩家匹配实现上线下线处理

文章目录 1.游戏大厅用户匹配1.1请求和响应1.2设计匹配页面1.3获取玩家信息1.4玩家信息的样式设置1.5初始化我们的websocket1.6点击按钮和客户端交互1.7点击按钮和服务器端交互 2.服务器端实现匹配功能框架2.1方法重写2.2借用session 3.处理上线下线3.1什么是上线下线3.2实现用…

matlab finv()函数解释 F分布 和 逆累积分布函数 卡方分布

1.Earths flattening 翻译并解释含义 "Earths flattening" 翻译为中文是“地球的扁率”。 含义解释&#xff1a; 地球的扁率是指地球形状偏离完美球形的程度。地球并非一个完美的球体&#xff0c;而是一个扁球体&#xff0c;即在两极略微扁平&#xff0c;赤道略微…

qt QSettings详解

1、概述 QSettings是Qt框架中用于应用程序配置和持久化数据的一个类。它提供了一种便捷的方式来存储和读取应用程序的设置&#xff0c;如窗口大小、位置、用户偏好等。QSettings支持多种存储格式&#xff0c;包括INI文件、Windows注册表&#xff08;仅限Windows平台&#xff0…

Web 毕设篇-适合小白、初级入门练手的 Spring Boot Web 毕业设计项目:智行无忧停车场管理系统(前后端源码 + 数据库 sql 脚本)

&#x1f525;博客主页&#xff1a; 【小扳_-CSDN博客】 ❤感谢大家点赞&#x1f44d;收藏⭐评论✍ 文章目录 1.0 项目介绍 1.1 项目功能 2.0 用户登录功能 3.0 首页界面 4.0 车辆信息管理功能 5.0 停车位管理功能 6.0 入场登记管理功能 7.0 预约管理功能 8.0 收费规则功能 9.0…

openssl使用哈希算法生成随机密钥

文章目录 一、openssl中随机数函数**OpenSSL 随机数函数概览**1. **核心随机数函数** **常用函数详解**1. RAND_bytes2. RAND_priv_bytes3. RAND_seed 和 RAND_add4. RAND_status **随机数生成器的熵池****常见用例****注意事项** 二、使用哈希算法生成随机的密钥 一、openssl中…