PipeWire 音频设计与实现分析三——日志子系统
日志子系统
PipeWire 的日志子系统的设计分为多个层次。PipeWire 用 struct spa_log
对象描述日志组件,用 struct spa_log_methods
对象描述日志组件打印各层级日志的多个方法。PipeWire 为日志子系统添加了 topic 机制,不同文件中的日志按功能以不同的 topic 来打印,以方便针对 topic 的日志分类处理。PipeWire 用 struct spa_log_topic
对象描述日志 topic。日志组件的接口名为SPA_TYPE_INTERFACE_Log
Spa:Pointer:Interface:Log。这些结构定义 (位于 pipewire/spa/include/spa/support/log.h) 如下:
#define SPA_LOG_TOPIC_DEFAULT NULLenum spa_log_level {SPA_LOG_LEVEL_NONE = 0,SPA_LOG_LEVEL_ERROR,SPA_LOG_LEVEL_WARN,SPA_LOG_LEVEL_INFO,SPA_LOG_LEVEL_DEBUG,SPA_LOG_LEVEL_TRACE,
};/*** The Log interface*/
#define SPA_TYPE_INTERFACE_Log SPA_TYPE_INFO_INTERFACE_BASE "Log"struct spa_log {/** the version of this log. This can be used to expand this* structure in the future */
#define SPA_VERSION_LOG 0struct spa_interface iface;/*** Logging level, everything above this level is not logged*/enum spa_log_level level;
};/*** \struct spa_log_topic** Identifier for a topic. Topics are string-based filters that logically* group messages together. An implementation may decide to filter different* topics on different levels, for example the "protocol" topic may require* debug level TRACE while the "core" topic defaults to debug level INFO.** spa_log_topics require a spa_log_methods version of 1 or higher.*/
struct spa_log_topic {
#define SPA_VERSION_LOG_TOPIC 0/** the version of this topic. This can be used to expand this* structure in the future */uint32_t version;/** The string identifier for the topic */const char *topic;/** Logging level set for this topic */enum spa_log_level level;/** False if this topic follows the \ref spa_log level */bool has_custom_level;
};struct spa_log_methods {
#define SPA_VERSION_LOG_METHODS 1uint32_t version;/*** Log a message with the given log level.** \note If compiled with this header, this function is only called* for implementations of version 0. For versions 1 and above, see* logt() instead.** \param log a spa_log* \param level a spa_log_level* \param file the file name* \param line the line number* \param func the function name* \param fmt printf style format* \param ... format arguments*/void (*log) (void *object,enum spa_log_level level,const char *file,int line,const char *func,const char *fmt, ...) SPA_PRINTF_FUNC(6, 7);/*** Log a message with the given log level.** \note If compiled with this header, this function is only called* for implementations of version 0. For versions 1 and above, see* logtv() instead.** \param log a spa_log* \param level a spa_log_level* \param file the file name* \param line the line number* \param func the function name* \param fmt printf style format* \param args format arguments*/void (*logv) (void *object,enum spa_log_level level,const char *file,int line,const char *func,const char *fmt,va_list args) SPA_PRINTF_FUNC(6, 0);/*** Log a message with the given log level for the given topic.** \note Callers that do not use topic-based logging (version 0), the \a* topic is NULL** \param log a spa_log* \param level a spa_log_level* \param topic the topic for this message, may be NULL* \param file the file name* \param line the line number* \param func the function name* \param fmt printf style format* \param ... format arguments** \since 1*/void (*logt) (void *object,enum spa_log_level level,const struct spa_log_topic *topic,const char *file,int line,const char *func,const char *fmt, ...) SPA_PRINTF_FUNC(7, 8);/*** Log a message with the given log level for the given topic.** \note For callers that do not use topic-based logging (version 0),* the \a topic is NULL** \param log a spa_log* \param level a spa_log_level* \param topic the topic for this message, may be NULL* \param file the file name* \param line the line number* \param func the function name* \param fmt printf style format* \param args format arguments** \since 1*/void (*logtv) (void *object,enum spa_log_level level,const struct spa_log_topic *topic,const char *file,int line,const char *func,const char *fmt,va_list args) SPA_PRINTF_FUNC(7, 0);/*** Initializes a \ref spa_log_topic to the correct logging level.** \since 1*/void (*topic_init) (void *object, struct spa_log_topic *topic);
};
PipeWire 的 SPA 日志系统提供了许多宏,来以不同的日志等级和 topic,向 spa_log
打印日志,这些宏定义 (位于 pipewire/spa/include/spa/support/log.h) 如下:
/* Unused, left for backwards compat */
#define spa_log_level_enabled(l,lev) ((l) && (l)->level >= (lev))#define spa_log_level_topic_enabled(l,topic,lev) \
({ \struct spa_log *_log = l; \enum spa_log_level _lev = _log ? _log->level : SPA_LOG_LEVEL_NONE; \struct spa_log_topic *_t = (struct spa_log_topic *)topic; \if (_t && _t->has_custom_level) \_lev = _t->level; \_lev >= lev; \
})/* Transparently calls to version 0 log if v1 is not supported */
#define spa_log_logt(l,lev,topic,...) \
({ \struct spa_log *_l = l; \struct spa_interface *_if = &_l->iface; \if (SPA_UNLIKELY(spa_log_level_topic_enabled(_l, topic, lev))) { \if (!spa_interface_call(_if, \struct spa_log_methods, logt, 1, \lev, topic, \__VA_ARGS__)) \spa_interface_call(_if, \struct spa_log_methods, log, 0, \lev, __VA_ARGS__); \} \
})/* Transparently calls to version 0 logv if v1 is not supported */
#define spa_log_logtv(l,lev,topic,...) \
({ \struct spa_log *_l = l; \struct spa_interface *_if = &_l->iface; \if (SPA_UNLIKELY(spa_log_level_topic_enabled(_l, topic, lev))) { \if (!spa_interface_call(_if, \struct spa_log_methods, logtv, 1, \lev, topic, \__VA_ARGS__)) \spa_interface_call(_if, \struct spa_log_methods, logv, 0, \lev, __VA_ARGS__); \} \
})#define spa_log_log(l,lev,...) \spa_log_logt(l,lev,SPA_LOG_TOPIC_DEFAULT,__VA_ARGS__)#define spa_log_logv(l,lev,...) \spa_log_logtv(l,lev,SPA_LOG_TOPIC_DEFAULT,__VA_ARGS__)#define spa_log_error(l,...) spa_log_log(l,SPA_LOG_LEVEL_ERROR,__FILE__,__LINE__,__func__,__VA_ARGS__)
#define spa_log_warn(l,...) spa_log_log(l,SPA_LOG_LEVEL_WARN,__FILE__,__LINE__,__func__,__VA_ARGS__)
#define spa_log_info(l,...) spa_log_log(l,SPA_LOG_LEVEL_INFO,__FILE__,__LINE__,__func__,__VA_ARGS__)
#define spa_log_debug(l,...) spa_log_log(l,SPA_LOG_LEVEL_DEBUG,__FILE__,__LINE__,__func__,__VA_ARGS__)
#define spa_log_trace(l,...) spa_log_log(l,SPA_LOG_LEVEL_TRACE,__FILE__,__LINE__,__func__,__VA_ARGS__)#define spa_logt_error(l,t,...) spa_log_logt(l,SPA_LOG_LEVEL_ERROR,t,__FILE__,__LINE__,__func__,__VA_ARGS__)
#define spa_logt_warn(l,t,...) spa_log_logt(l,SPA_LOG_LEVEL_WARN,t,__FILE__,__LINE__,__func__,__VA_ARGS__)
#define spa_logt_info(l,t,...) spa_log_logt(l,SPA_LOG_LEVEL_INFO,t,__FILE__,__LINE__,__func__,__VA_ARGS__)
#define spa_logt_debug(l,t,...) spa_log_logt(l,SPA_LOG_LEVEL_DEBUG,t,__FILE__,__LINE__,__func__,__VA_ARGS__)
#define spa_logt_trace(l,t,...) spa_log_logt(l,SPA_LOG_LEVEL_TRACE,t,__FILE__,__LINE__,__func__,__VA_ARGS__)#ifndef FASTPATH
#define spa_log_trace_fp(l,...) spa_log_log(l,SPA_LOG_LEVEL_TRACE,__FILE__,__LINE__,__func__,__VA_ARGS__)
#else
#define spa_log_trace_fp(l,...)
#endif
这些宏最终通过 struct spa_log_methods
的方法打印日志。
PipeWire 的代码通常通过另一组日志宏 (也称为 pipewire 日志宏) 打印日志,如 pw_log_info()
和 pw_log_warn()
,这组宏定义 (位于 pipewire/src/pipewire/log.h) 如下:
/** Log a message for a topic */
void
pw_log_logt(enum spa_log_level level,const struct spa_log_topic *topic,const char *file,int line, const char *func,const char *fmt, ...) SPA_PRINTF_FUNC(6, 7);/** Log a message for a topic */
void
pw_log_logtv(enum spa_log_level level,const struct spa_log_topic *topic,const char *file,int line, const char *func,const char *fmt, va_list args) SPA_PRINTF_FUNC(6, 0);/** Log a message for the default topic */
void
pw_log_log(enum spa_log_level level,const char *file,int line, const char *func,const char *fmt, ...) SPA_PRINTF_FUNC(5, 6);/** Log a message for the default topic */
void
pw_log_logv(enum spa_log_level level,const char *file,int line, const char *func,const char *fmt, va_list args) SPA_PRINTF_FUNC(5, 0);. . . . . .
/** Check if a loglevel is enabled */
#define pw_log_level_enabled(lev) (pw_log_level >= (lev))
#define pw_log_topic_enabled(lev,t) ((t) && (t)->has_custom_level ? (t)->level >= (lev) : pw_log_level_enabled((lev)))#define pw_logtv(lev,topic,fmt,ap) \
({ \if (SPA_UNLIKELY(pw_log_topic_enabled(lev,topic))) \pw_log_logtv(lev,topic,__FILE__,__LINE__,__func__,fmt,ap); \
})#define pw_logt(lev,topic,...) \
({ \if (SPA_UNLIKELY(pw_log_topic_enabled(lev,topic))) \pw_log_logt(lev,topic,__FILE__,__LINE__,__func__,__VA_ARGS__); \
})#define pw_log(lev,...) pw_logt(lev,PW_LOG_TOPIC_DEFAULT,__VA_ARGS__)#define pw_log_error(...) pw_log(SPA_LOG_LEVEL_ERROR,__VA_ARGS__)
#define pw_log_warn(...) pw_log(SPA_LOG_LEVEL_WARN,__VA_ARGS__)
#define pw_log_info(...) pw_log(SPA_LOG_LEVEL_INFO,__VA_ARGS__)
#define pw_log_debug(...) pw_log(SPA_LOG_LEVEL_DEBUG,__VA_ARGS__)
#define pw_log_trace(...) pw_log(SPA_LOG_LEVEL_TRACE,__VA_ARGS__)#define pw_logt_error(t,...) pw_logt(SPA_LOG_LEVEL_ERROR,t,__VA_ARGS__)
#define pw_logt_warn(t,...) pw_logt(SPA_LOG_LEVEL_WARN,t,__VA_ARGS__)
#define pw_logt_info(t,...) pw_logt(SPA_LOG_LEVEL_INFO,t,__VA_ARGS__)
#define pw_logt_debug(t,...) pw_logt(SPA_LOG_LEVEL_DEBUG,t,__VA_ARGS__)
#define pw_logt_trace(t,...) pw_logt(SPA_LOG_LEVEL_TRACE,t,__VA_ARGS__)#ifndef FASTPATH
#define pw_log_trace_fp(...) pw_log(SPA_LOG_LEVEL_TRACE,__VA_ARGS__)
#else
#define pw_log_trace_fp(...)
#endif
这组宏是对一组日志函数的封装。这组日志函数定义 (位于 pipewire/src/pipewire/log.c) 如下:
SPA_LOG_IMPL(default_log);#define DEFAULT_LOG_LEVEL SPA_LOG_LEVEL_WARNSPA_EXPORT
enum spa_log_level pw_log_level = DEFAULT_LOG_LEVEL;static struct spa_log *global_log = &default_log.log;. . . . . .
/** Log a message for the given topic* \param level the log level* \param topic the topic* \param file the file this message originated from* \param line the line number* \param func the function* \param fmt the printf style format* \param ... printf style arguments to log**/
SPA_EXPORT
void
pw_log_logt(enum spa_log_level level,const struct spa_log_topic *topic,const char *file,int line,const char *func,const char *fmt, ...)
{if (SPA_UNLIKELY(pw_log_topic_enabled(level, topic))) {va_list args;va_start(args, fmt);spa_log_logtv(global_log, level, topic, file, line, func, fmt, args);va_end(args);}
}/** Log a message for the given topic with va_list* \param level the log level* \param topic the topic* \param file the file this message originated from* \param line the line number* \param func the function* \param fmt the printf style format* \param args a va_list of arguments**/
SPA_EXPORT
void
pw_log_logtv(enum spa_log_level level,const struct spa_log_topic *topic,const char *file,int line,const char *func,const char *fmt,va_list args)
{spa_log_logtv(global_log, level, topic, file, line, func, fmt, args);
}/** Log a message for the default topic with va_list* \param level the log level* \param file the file this message originated from* \param line the line number* \param func the function* \param fmt the printf style format* \param args a va_list of arguments**/
SPA_EXPORT
void
pw_log_logv(enum spa_log_level level,const char *file,int line,const char *func,const char *fmt,va_list args)
{pw_log_logtv(level, PW_LOG_TOPIC_DEFAULT, file, line, func, fmt, args);
}/** Log a message for the default topic* \param level the log level* \param file the file this message originated from* \param line the line number* \param func the function* \param fmt the printf style format* \param ... printf style arguments to log**/
SPA_EXPORT
void
pw_log_log(enum spa_log_level level,const char *file,int line,const char *func,const char *fmt, ...)
{va_list args;va_start(args, fmt);pw_log_logtv(level, PW_LOG_TOPIC_DEFAULT, file, line, func, fmt, args);va_end(args);
}
这组日志函数,主要封装了对 SPA 日志宏的调用。当然在调用 SPA 日志宏之前,会先检查要打印的日志的日志等级,按照日志等级设置是否需要打印。此外,日志组件实现 spa_log
取全局日志组件。
PipeWire 代码通过 pipewire 日志宏或 pipewire 日志函数打印日志。
PipeWire 的日志组件实现有多个,默认的日志组件实现 (位于 pipewire/spa/include/spa/support/log-impl.h) 如下:
static inline SPA_PRINTF_FUNC(7, 0) void spa_log_impl_logtv(void *object,enum spa_log_level level,const struct spa_log_topic *topic,const char *file,int line,const char *func,const char *fmt,va_list args)
{static const char * const levels[] = { "-", "E", "W", "I", "D", "T" };const char *basename = strrchr(file, '/');char text[512], location[1024];char topicstr[32] = {0};if (basename)basename += 1; /* skip '/' */elsebasename = file; /* use whole string if no '/' is found */if (topic && topic->topic)snprintf(topicstr, sizeof(topicstr), " %s ", topic->topic);vsnprintf(text, sizeof(text), fmt, args);snprintf(location, sizeof(location), "[%s]%s[%s:%i %s()] %s\n",levels[level],topicstr,basename, line, func, text);fputs(location, stderr);
}static inline SPA_PRINTF_FUNC(7,8) void spa_log_impl_logt(void *object,enum spa_log_level level,const struct spa_log_topic *topic,const char *file,int line,const char *func,const char *fmt, ...)
{va_list args;va_start(args, fmt);spa_log_impl_logtv(object, level, topic, file, line, func, fmt, args);va_end(args);
}static inline SPA_PRINTF_FUNC(6, 0) void spa_log_impl_logv(void *object,enum spa_log_level level,const char *file,int line,const char *func,const char *fmt,va_list args)
{spa_log_impl_logtv(object, level, NULL, file, line, func, fmt, args);
}static inline SPA_PRINTF_FUNC(6,7) void spa_log_impl_log(void *object,enum spa_log_level level,const char *file,int line,const char *func,const char *fmt, ...)
{va_list args;va_start(args, fmt);spa_log_impl_logv(object, level, file, line, func, fmt, args);va_end(args);
}static inline void spa_log_impl_topic_init(void *object, struct spa_log_topic *topic)
{/* noop */
}#define SPA_LOG_IMPL_DEFINE(name) \
struct { \struct spa_log log; \struct spa_log_methods methods; \
} name#define SPA_LOG_IMPL_INIT(name) \{ { { SPA_TYPE_INTERFACE_Log, SPA_VERSION_LOG, \SPA_CALLBACKS_INIT(&name.methods, &name) }, \SPA_LOG_LEVEL_INFO, }, \{ SPA_VERSION_LOG_METHODS, \spa_log_impl_log, \spa_log_impl_logv, \spa_log_impl_logt, \spa_log_impl_logtv, \} }#define SPA_LOG_IMPL(name) \SPA_LOG_IMPL_DEFINE(name) = SPA_LOG_IMPL_INIT(name)
日志组件实现定义 struct spa_log_methods
对象及其各个方法,以及 struct spa_log
对象。日志组件的使用者通过 struct spa_log
对象的 iface.cb.funcs
字段获得它实现的 struct spa_log_methods
对象,获得它提供的各个打印日志的方法。
这个默认实现将日志打印到标准输出。日志组件实现更常见的是从支持库插件或 journal 插件加载。
PipeWire 通过一个全局对象维护默认使用的日志组件实现,并提供一些接口来操作 (位于 pipewire/src/pipewire/log.c) 该对象,如:
SPA_EXPORT
void pw_log_set(struct spa_log *log)
{global_log = log ? log : &default_log.log;global_log->level = pw_log_level;
}bool pw_log_is_default(void)
{return global_log == &default_log.log;
}/** Get the global log interface* \return the global log*/
SPA_EXPORT
struct spa_log *pw_log_get(void)
{return global_log;
}/** Set the global log level* \param level the new log level*/
SPA_EXPORT
void pw_log_set_level(enum spa_log_level level)
{pw_log_level = level;global_log->level = level;
}
PipeWire 还提供接口初始化 (位于 pipewire/src/pipewire/log.c) 日志 topic,如:
SPA_EXPORT
struct spa_log_topic *PW_LOG_TOPIC_DEFAULT;PW_LOG_TOPIC_STATIC(log_topic, "pw.log"); /* log topic for this file here */
PW_LOG_TOPIC(log_buffers, "pw.buffers");
PW_LOG_TOPIC(log_client, "pw.client");
PW_LOG_TOPIC(log_conf, "pw.conf");
PW_LOG_TOPIC(log_context, "pw.context");
PW_LOG_TOPIC(log_core, "pw.core");
PW_LOG_TOPIC(log_data_loop, "pw.data-loop");
PW_LOG_TOPIC(log_device, "pw.device");
PW_LOG_TOPIC(log_factory, "pw.factory");
PW_LOG_TOPIC(log_filter, "pw.filter");
PW_LOG_TOPIC(log_global, "pw.global");
PW_LOG_TOPIC(log_link, "pw.link");
PW_LOG_TOPIC(log_loop, "pw.loop");
PW_LOG_TOPIC(log_main_loop, "pw.main-loop");
PW_LOG_TOPIC(log_mem, "pw.mem");
PW_LOG_TOPIC(log_metadata, "pw.metadata");
PW_LOG_TOPIC(log_module, "pw.module");
PW_LOG_TOPIC(log_node, "pw.node");
PW_LOG_TOPIC(log_port, "pw.port");
PW_LOG_TOPIC(log_properties, "pw.props");
PW_LOG_TOPIC(log_protocol, "pw.protocol");
PW_LOG_TOPIC(log_proxy, "pw.proxy");
PW_LOG_TOPIC(log_resource, "pw.resource");
PW_LOG_TOPIC(log_stream, "pw.stream");
PW_LOG_TOPIC(log_thread_loop, "pw.thread-loop");
PW_LOG_TOPIC(log_work_queue, "pw.work-queue");PW_LOG_TOPIC(PW_LOG_TOPIC_DEFAULT, "default");. . . . . .void
pw_log_init(void)
{PW_LOG_TOPIC_INIT(PW_LOG_TOPIC_DEFAULT);PW_LOG_TOPIC_INIT(log_buffers);PW_LOG_TOPIC_INIT(log_client);PW_LOG_TOPIC_INIT(log_conf);PW_LOG_TOPIC_INIT(log_context);PW_LOG_TOPIC_INIT(log_core);PW_LOG_TOPIC_INIT(log_data_loop);PW_LOG_TOPIC_INIT(log_device);PW_LOG_TOPIC_INIT(log_factory);PW_LOG_TOPIC_INIT(log_filter);PW_LOG_TOPIC_INIT(log_global);PW_LOG_TOPIC_INIT(log_link);PW_LOG_TOPIC_INIT(log_loop);PW_LOG_TOPIC_INIT(log_main_loop);PW_LOG_TOPIC_INIT(log_mem);PW_LOG_TOPIC_INIT(log_metadata);PW_LOG_TOPIC_INIT(log_module);PW_LOG_TOPIC_INIT(log_node);PW_LOG_TOPIC_INIT(log_port);PW_LOG_TOPIC_INIT(log_properties);PW_LOG_TOPIC_INIT(log_protocol);PW_LOG_TOPIC_INIT(log_proxy);PW_LOG_TOPIC_INIT(log_resource);PW_LOG_TOPIC_INIT(log_stream);PW_LOG_TOPIC_INIT(log_thread_loop);PW_LOG_TOPIC_INIT(log_topic);PW_LOG_TOPIC_INIT(log_work_queue);
}
各个需要打印日志的源文件,定义 PW_LOG_TOPIC_DEFAULT
宏选择所要用的日志 topic,如:
PW_LOG_TOPIC_EXTERN(log_context);
#define PW_LOG_TOPIC_DEFAULT log_context
则 PipeWire 打印日志的方法,分为如下四层:
- PipeWire 日志打印宏。许多代码会直接用它们打印日志。封装 PipeWire 日志打印函数。
- PipeWire 日志打印函数。部分代码会直接用它们打印日志。封装 SPA 日志打印宏,通过全局日志打印组件打印日志。
- SPA 日志打印宏。插件代码会直接用它们打印日志。通过日志打印组件实现打印日志。
- 日志打印组件实现。
全局日志打印组件管理:通过一个全局对象维护,提供接口来设置和获取。
日志打印组件实现:有一个内置的实现,但大多通过插件实现,如支持库插件和 journal 插件。
相关文章:
PipeWire 音频设计与实现分析三——日志子系统
日志子系统 PipeWire 的日志子系统的设计分为多个层次。PipeWire 用 struct spa_log 对象描述日志组件,用 struct spa_log_methods 对象描述日志组件打印各层级日志的多个方法。PipeWire 为日志子系统添加了 topic 机制,不同文件中的日志按功能以不同的…...
playwright解决重复登录问题,通过pytest夹具自动读取storage_state用户状态信息
playwright解决重复登录问题,通过pytest夹具自动读取storage_state用户状态信息 conftest.py文件下封装两个夹具夹具一:将storage_state登录状态导出为json文件夹具二:重写夹具browser_context_args,添加storage_state登录状态登录…...
Codeforces Round 1014 (Div. 2)(A-D)
题目链接:Dashboard - Codeforces Round 1014 (Div. 2) - Codeforces A. Kamilka and the Sheep 思路 最大值-最小值 代码 void solve(){int n;cin>>n;vi a(n10);int mx0;int miinf;for(int i1;i<n;i){cin>>a[i];mimin(mi,a[i]);mxmax(mx,a[i])…...
vulhub靶场—— Tomcat8
目录 一、漏洞描述 二、靶场搭建 三、漏洞复现 1、弱密码 2、文件上传 一、漏洞描述 环境描述: Tomcat 支持后台部署 war 文件,可以直接将 webshell 部署到 web 目录下。tomcat 默认的管理页面 manager 使用 basic 认证用户名和密码登录࿰…...
c# ftp上传下载 帮助类
工作中FTP的上传和下载还是很常用的。如下载打标数据,上传打标结果等。 这个类常用方法都有了:上传,下载,判断文件夹是否存在,创建文件夹,获取当前目录下文件列表(不包括文件夹) ,获取当前目录下文件列表(不包括文件夹) ,获取FTP文件列表(包括文件夹), 获取当前目…...
Redis 哈希表结构详解
Redis 哈希表结构详解 相关链接 redis中 hashtable的 sizemask理解 一、核心结构体定义与作用 Redis 的哈希表实现基于 链表法解决冲突,并采用 渐进式 rehash 策略。其核心结构体包括 dictEntry、dictht 和 dict,三者协作实现高效的键值对存储。 二、结…...
接口等幂处理
介绍 ✅ 什么是等幂(Idempotency)? 等幂 无论这个操作被执行多少次,结果都是一样的,不会因为多次执行而产生副作用。 通俗一点说:“点一次和点一百次,效果是一样的。” ✅ 在接口中࿰…...
华为配置篇-BGP实验
BGP 一、简述二、常用命令总结三、实验 一、简述 二、常用命令总结 display bgp peer #查看 BGP 对等体 display bgp routing-table #查看 BGP 路由表#在R1上通过 network 命令发布路由 [R1]bgp 64513 [R1-bgp] network 10.1.1.1 24#在R2上将路由的下一跳地址修改为自身 [R2]…...
【Tauri2】008——简单说说配置文件
前言 配置文件,即tauri.conf.json Configuration Files | Taurihttps://tauri.app/zh-cn/develop/configuration-files/这个文件的作用 该文件由 Tauri 运行时和 Tauri CLI 使用。你可以定义构建设置(例如在 tauri build 或 tauri dev 启动前运行的命令…...
Java学习笔记1——编程基础
一、整数类型变量 注意:每个字符型常量占两个字节 二、自动类型转换和强制类型转换 三、算术运算符 四、赋值运算符 五、比较运算符 六、逻辑运算符 七、运算符的优先级 运算符的优先级可以通过以下口诀来记忆: 括号优先,单目次之&am…...
CMD/DOS和批处理入门知识汇总
0、前言: 在工作中,有时候需要涉及到window系统更底层的一些东西,所以需要学习一些cmd指令和dos命令,来完成高效批处理任务,或者自动化办公。还有想要对系统中文件管理有更细致的认识,便于请理磁盘文件。后…...
Visual Studio 2019 Qt QML 项目环境搭建常见问题处理方法
在 Visual Studio 2019 运行 Qt/QML 项目比直接使用QtCreator环境麻烦,主要是有qmake 的一些配置项不能在 Visual Studio中设置。下面整理一些常见问题的处理方法,供参考: 搭建VS Qt 环境,在Visual Studios 2019下面安装 Qt Vis…...
Python-Django入手
18.1 建立项目 18.1.1 制定规范 - 定义项目目标:明确应用的核心功能 - 创建项目文档:用README.md记录技术栈和开发流程 - 规划目录结构:建议遵循Django官方推荐的项目布局 18.1.2 建立虚拟环境 在命令行执行: python -m ven…...
SakuraCat(2)Endpoint
Endpoint 功能概述 监听指定端口(默认是 8080)的客户端连接。接受客户端连接后,为每个连接创建一个新的线程进行处理。使用 Processor 类来处理客户端的请求和响应。 package com.SakuraCat.connector.protocolHandler;import com.SakuraC…...
19914 最小生成树2
19914 最小生成树2 ⭐️难度:中等 🌟考点:最小生成树 📖 📚 import java.util.*;public class Main {static class Edge{int u,v,w;Edge(int u,int v,int w){this.u u;this.v v;this.w w;}}static ArrayList<…...
SSE服务器主动推送至浏览器客户端,让你不再需要websocket
Server-Sent Events(SSE)是一种服务器向客户端推送实时更新的技术,基于HTTP协议。客户端通过EventSource API来接收事件流,而服务器则保持一个长连接,持续发送数据。这与传统的请求-响应模式不同,允许服务器…...
C++中的搜索算法实现
C中的搜索算法实现 在编程中,搜索算法是解决各种问题的基础工具之一。C作为一种功能强大的编程语言,提供了多种实现搜索算法的方式。本文将详细介绍两种常见的搜索算法:线性搜索和二分搜索,并通过代码示例展示它们的实现。 一、…...
OSI 七层模型和四层模型(TCP/IP 模型)
文章目录 前言一、OSI 七层模型二、TCP/IP 四层模型三、运行协议及设备1. OSI 七层模型2. TCP/IP 四层模型3. 运行协议4. 各类设备的作用 总结 前言 OSI 七层模型和四层模型(TCP/IP 模型)是两种常见的网络协议分层架构,它们的主要区别如下&a…...
将代理连接到 Elasticsearch 使用模型上下文协议
作者:来自 Elastic Jedr Blaszyk 及 Joe McElroy 让我们使用 Model Context Protocol 服务器 与 你的 数据 在 Elasticsearch 中聊天。 如果与你的数据交互像与同事聊天一样轻松,会怎样?想象一下,你只需简单地问:“显…...
前端调试实践与案例场景
前端调试实践与案例场景 前端开发中,调试是一项必不可少的技能。以下是一些常见的前端调试实践和相应的案例场景: 1. 浏览器开发者工具 案例场景:布局问题 用户报告在移动设备上页面布局错乱。使用 Chrome DevTools 的设备模拟功能和 Ele…...
安卓的布局方式
一、RelativeLayout 相对布局 特点:每个组件相对其他的某一个组件进行定位。 (一)主要属性 1、设置和父组件的对齐: alignParentTop : 设置为true,代表和父布局顶部对齐。 其他对齐只需要改变后面的Top为 Left、Right 或者Bottom&…...
计算机网络面经(一)
以下为个人总结,图源大部分会来自网络和JavaGuide 网络分层模型 OSI七层模型 各层的常见协议 应用层 用户接口 HTTP, FTP, SMTP, DNS表示层 数据格式转换 SSL/TLS, JSON, JPEG会话层 会话管理 NetBIOS, RPC, SSH传输层 端到端通信 TCP, UDP, QUIC网络层 路由寻址…...
k8s日志管理
k8s日志管理 k8s查看日志查看集群中不是完全运行状态的pod查看deployment日志查看service日志进入pod的容器内查看日志 管理k8s组件日志kubectl logs查看日志原理 管理k8s应用日志收集k8s日志思路收集标准输出收集容器中日志文件 k8s查看节点状态失败k8s部署prometheus监控 k8s…...
Netty源码—10.Netty工具之时间轮二
大纲 1.什么是时间轮 2.HashedWheelTimer是什么 3.HashedWheelTimer的使用 4.HashedWheelTimer的运行流程 5.HashedWheelTimer的核心字段 6.HashedWheelTimer的构造方法 7.HashedWheelTimer添加任务和执行任务 8.HashedWheelTimer的完整源码 9.HashedWheelTimer的总结…...
Baklib激活企业知识管理新动能
Baklib核心技术架构解析 Baklib的底层架构以模块化设计为核心,融合知识中台的核心理念,通过分布式存储引擎与智能语义分析系统构建三层技术体系。数据层采用多源异构数据接入协议,支持文档、音视频、代码片段等非结构化数据的实时解析与分类…...
CSP-J/S冲奖第21天:插入排序
一、插入排序概念 1.1 生活中的类比 • 扑克牌排序:就像整理手中的扑克牌,每次将一张牌插入到已排好序的牌中合适位置 • 动态演示: 初始序列:[5, 2, 4, 6, 1, 3] 排序过程: → [2, 5, 4, 6, 1, 3] → [2, 4, 5, 6, …...
Jest系列二之基础实践
Jest基础实践 官方文档地址:https://jest.nodejs.cn/docs 生命周期 在 Jest 中,生命周期方法大致分为两类:下面所罗列的生命周期方法,也是全局方法,不需要引入,直接就可以使用。 重复性的生命周期方法&…...
Scikit-learn全攻略:从入门到工业级应用
Scikit-learn全攻略:从入门到工业级应用 引言:Scikit-learn在机器学习生态系统中的核心地位 Scikit-learn作为Python最受欢迎的机器学习库,已成为数据科学家的标准工具集。根据2023年Kaggle调查报告,超过83%的数据专业人士在日常工作中使用Scikit-learn。本文将系统性地介…...
基于Python的图书馆信息管理系统研发
标题:基于Python的图书馆信息管理系统研发 内容:1.摘要 在数字化信息快速发展的背景下,传统图书馆管理方式效率低下,难以满足日益增长的信息管理需求。本研究旨在研发一款基于Python的图书馆信息管理系统,以提高图书馆信息管理的效率和准确性…...
Pytorch学习笔记(十七)Image and Video - Adversarial Example Generation
这篇博客瞄准的是 pytorch 官方教程中 Image and Video 章节的 Adversarial Example Generation 部分。 官网链接:https://pytorch.org/tutorials/beginner/fgsm_tutorial.html 完整网盘链接: https://pan.baidu.com/s/1L9PVZ-KRDGVER-AJnXOvlQ?pwdaa2m 提取码: …...
基于Arm GNU Toolchain编译生成的.elf转hex/bin文件格式方法
基于Arm GNU Toolchain编译生成的.elf转hex/bin文件格式方法 已经弃用的版本(Version 10.3-2021.10):gcc-arm-none-eabi:https://developer.arm.com/downloads/-/gnu-rmArm GNU Toolchain当前版本:https://developer.a…...
Ubuntu系统Docker安装失败
问题: 1. 删除错误的 Docker 源 sudo rm -rf /etc/apt/sources.list.d/docker.list sudo rm -rf /etc/apt/keyrings/docker.gpg 2. 重新添加 Docker 官方 GPG 密钥 sudo mkdir -p /etc/apt/keyrings curl -fsSL https://download.docker.com/linux/ubuntu/gpg | …...
鸿蒙学习手册(HarmonyOSNext_API16)_数据持久化②:键值型数据库
概述 键值型数据库就像一个大抽屉柜,每个抽屉都有一个唯一的标签(键),里面可以放任何东西(值)。当你需要存或取东西时,直接看标签拿对应的抽屉就行,不用管其他抽屉里有什么。这种简…...
多线程 - 线程安全 2 -- > 死锁问题
目录 小结复习: 线程安全: 如何解决线程安全问题? synchronized “死锁” 死锁的三种经典场景: 1. 一个线程,一把锁。 2.两个线程,两把锁。 3. N 个线程 M 把锁 完! 小结复习:…...
JavaScript函数详解
目录 一、函数的基础概念 1. 函数的定义方式 2. 函数的参数处理 3.匿名函数与立即执行函数 4.同名函数与函数提升 二、函数的作用域与闭包 1. 作用域(Scope) 2. 闭包(Closure) 三、高阶函数与函数式编程 1. 高阶函数 2…...
Python-八股总结
目录 1 python 垃圾处理机制2 yield3 python 多继承,两个父类有同名方法怎么办?4 python 多线程/多进程/协程4.1 多线程与GIL全局解释器锁4.2 多进程4.3 协程 5 乐观锁/悲观锁6 基本数据结构**1. 列表(List)****2. 元组࿰…...
整合分块请求大模型返回的测试用例及小工具显示bug修复
在之前的分块发送需求数据给大模型进行测试用例生成时,由于数据结构的改变,需要对分块的回复进行整合,正确的整合是保障系统稳定性和功能正确性的核心。随着测试需求的复杂化,这对测试工程师提出了更高的整合和管理要求。本文将为…...
记一道CTF题—PHP双MD5加密+”SALT“弱碰撞绕过
通过分析源代码并找到绕过限制的方法,从而获取到flag! 部分源码: <?php $name_POST[username]; $passencode(_POST[password]); $admin_user "admin"; $admin_pw get_hash("0e260265122865008095838959784793");…...
stm32F103RCT6 FLASH模拟EEPROM 读写32位数据
#include “stm32flash.h” #ifndef __STMFLASH_H__ #define __STMFLASH_H__ #include "main.h" #define</...
Spring Data审计利器:@LastModifiedDate详解!!!
🕒 Spring Data审计利器:LastModifiedDate详解🔥 🌟 简介 在数据驱动的应用中,记录数据的最后修改时间是常见需求。Spring Data的LastModifiedDate注解让这一过程自动化成为可能!本篇带你掌握它的核心用法…...
【SLURM】介绍
SLURM Slurm(Simple Linux Utility for Resource Management) 是一个用于管理和调度计算集群任务的开源作业调度系统。它主要用于高性能计算(HPC)环境,比如超算中心、大学的计算集群或企业的数据中心。 本文主要针对使…...
算法-贪心算法
圣诞老人的礼物-Santa Clau’s Gifts 现在有多箱不同的糖果,每箱糖果有自己的价值和重量,每箱糖果都可以拆分成任意散装组合带走。圣 诞老人的驯鹿雪橇最多只能装下重量W的糖果,请 问圣诞老人最多能带走多大价值的糖果。 输入 第一行由两个…...
Nginx — Nginx处理Web请求机制解析
一、Nginx请求默认页面资源 1、配置文件详解 修改端口号为8080并重启服务: 二、Nginx进程模型 1、nginx常用命令解析 master进程:主进程(只有一个) worker进程:工作进程(可以有多个,默认只有一…...
GAN随手笔记
文章目录 1. description2. code 1. description 后续整理 GAN是生成对抗网络,主要由G生成器,D判别器组成,具体形式如下 D 判别器: G生成器: 2. code 部分源码,暂定,后续修改 import nump…...
Java 8 时区与历法处理指南:跨越全球的时间管理
Java 8 的 java.time API 不仅修复了旧版日期时间 API 的设计缺陷,还提供了对时区和多历法的全面支持。无论是处理全球化应用的时区转换,还是适配不同文化的日历系统,Java 8 都能轻松应对。本文将深入解析其核心功能,并提供实用代…...
【STM32】对stm32F103VET6指南者原理图详解(超详细)
目录 一、原理图基本概念二、STM32F103VET6 的主要特性二、MCU模块三、电源模块四、时钟模块五、复位模块NRST 六、GPIO模块LED 七、调试模块JTAG 八、外设模块UARTSPII2CADC 九、其它模块BOOT 一、原理图基本概念 原理图/电路图通常由硬件工程师使用Altium Designer/ KiCad / …...
瑞芯微RKRGA(librga)Buffer API 分析
一、Buffer API 简介 在瑞芯微官方的 librga 库的手册中,有两组配置 buffer 的API: importbuffer 方式: importbuffer_virtualaddr importbuffer_physicaladdr importbuffer_fd wrapbuffer 方式: wrapbuffer_virtualaddr wrapb…...
移动端六大语言速记:第1部分 - 基础语法与控制结构
移动端六大语言速记:第1部分 - 基础语法与控制结构 本文将对比Java、Kotlin、Flutter(Dart)、Python、ArkTS和Swift这六种移动端开发语言的基础语法与控制结构,帮助开发者快速理解各语言间的差异与共性。 1. 基础语法 1.1 数据类型 各语言的基本数据…...
Java 大视界 -- Java 大数据在智能金融区块链跨境支付与结算中的应用(154)
💖亲爱的朋友们,热烈欢迎来到 青云交的博客!能与诸位在此相逢,我倍感荣幸。在这飞速更迭的时代,我们都渴望一方心灵净土,而 我的博客 正是这样温暖的所在。这里为你呈上趣味与实用兼具的知识,也…...
Python Playwright库全面详解
Playwright 是 Microsoft 开发的一个现代化的端到端测试和浏览器自动化库,支持 Chromium、WebKit 和 Firefox 浏览器。它提供了跨浏览器、跨平台的自动化能力,且具有高性能和可靠性。 一、核心特性 多浏览器支持: Chromium (Chrome, Edge)We…...