Chromium Mojo(IPC)进程通信演示 c++(1)

   网上搜索关于mojo教程 多数都是理论 加上翻译谷歌mojo文档的,但是如何自定义两个进程使用mojo通信呢?看下面的完整例子介绍:(本人也是参考谷歌代码例子改编而成)

本文演示了client.exe和service.exe 通过mojo::IncomingInvitation模式进行通信使用例子。

废话不多说直接上代码:

一、目录结构如下图:

按照如图所示目录结构添加文件即可。 

二、定义sample.mojom接口

1、demo\logger\public\mojom\sample.mojom

module sample.mojom;interface Logger {Log(string message);GetTail() => (string message);
};

2、demo\logger\public\mojom\BUILD.gn

import("//mojo/public/tools/bindings/mojom.gni")mojom("mojom") {sources = ["sample.mojom",]
}

3、gn gen out/debug 自动生成代码如下:

out\Debug\gen\demo\logger\public\mojom\sample.mojom.h

out\Debug\gen\demo\logger\public\mojom\sample.mojom.cc 

其他更多参考out\Debug\gen\demo\logger\目录下

三、client.exe端代码: 

1、demo\client\client.cc  //client.exe main函数实现文件:

#include<iostream>
#include <stdarg.h>
#include <stddef.h>
#include <stdint.h>#include "base/at_exit.h"
#include "base/command_line.h"
#include "base/files/file_path.h"
#include "base/files/file_util.h"
#include "base/files/memory_mapped_file.h"
#include "base/logging.h"
#include "base/process/launch.h"
#include "base/run_loop.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_util.h"
#include "base/strings/utf_string_conversions.h"
#include "base/threading/thread.h"
#include "build/build_config.h"
#include "demo/logger/public/mojom/sample.mojom.h"
#include "demo/client/logger_test.h"
#include "demo/process_bootstrapper_helper.h"
#include "mojo/core/embedder/configuration.h"
#include "mojo/core/embedder/embedder.h"
#include "mojo/core/embedder/scoped_ipc_support.h"
#include "mojo/public/cpp/bindings/associated_remote.h"
#include "mojo/public/cpp/bindings/pending_remote.h"
#include "mojo/public/cpp/bindings/remote.h"
#include "mojo/public/cpp/platform/platform_channel.h"
#include "mojo/public/cpp/system/invitation.h"
#include "mojo/public/cpp/system/message_pipe.h"mojo::ScopedMessagePipeHandle LaunchAndConnect() {// Under the hood, this is essentially always an OS pipe (domain socket pair,// Windows named pipe, Fuchsia channel, etc).mojo::PlatformChannel channel;mojo::OutgoingInvitation invitation;// Attach a message pipe to be extracted by the receiver. The other end of the// pipe is returned for us to use locally. We choose the arbitrary name "pipe"// here, which is the same name that the receiver will have to use when// plucking this pipe off of the invitation it receives to join our process// network.mojo::ScopedMessagePipeHandle pipe = invitation.AttachMessagePipe("pipe");base::LaunchOptions options;// This is the relative path to the mock "renderer process" binary. We pass it// into `base::LaunchProcess` to run the binary in a new process.static const base::CommandLine::CharType* argv[] = {FILE_PATH_LITERAL("./service")};base::CommandLine command_line(1, argv);// Delegating to Mojo to "prepare" the command line will append the// `--mojo-platform-channel-handle=N` command line argument, so that the// renderer knows which file descriptor name to recover, in order to establish// the primordial connection with this process. We log the full command line// next, to show what mojo information the renderer will be initiated with.channel.PrepareToPassRemoteEndpoint(&options, &command_line);LOG(INFO) << "Browser: " << command_line.GetCommandLineString();base::Process child_process = base::LaunchProcess(command_line, options);channel.RemoteProcessLaunchAttempted();mojo::OutgoingInvitation::Send(std::move(invitation), child_process.Handle(),channel.TakeLocalEndpoint());return pipe;
}logger_test *g_logger_test = nullptr;void CreateProcessRemote(mojo::ScopedMessagePipeHandle pipe,const std::string& msg) {mojo::PendingRemote<sample::mojom::Logger> pending_remote(std::move(pipe),0u);mojo::Remote<sample::mojom::Logger> remote(std::move(pending_remote));g_logger_test = new logger_test(std::move(remote));LOG(INFO) << "Browser invoking SayHello() on remote pointing to renderer";g_logger_test->sendLoggerMsg(msg);g_logger_test->sendLoggerExt();
}int main(int argc, const char* argv[]) {base::AtExitManager at_exit_manager;base::CommandLine::Init(argc, argv);ProcessBootstrapperHelp bootstrapper;bootstrapper.InitMainThread(base::MessagePumpType::IO);bootstrapper.InitMojo(/*as_browser_process=*/true);mojo::ScopedMessagePipeHandle pipe = LaunchAndConnect();std::string msg("jd test");base::SequencedTaskRunner::GetCurrentDefault()->PostTask(FROM_HERE, base::BindOnce(&CreateProcessRemote, std::move(pipe),msg));base::RunLoop run_loop;// Delay shutdown of the browser process for visual effects, as well as to// ensure the browser process doesn't die while the IPC message is still being// sent to the target process asynchronously, which would prevent its// delivery.base::SequencedTaskRunner::GetCurrentDefault()->PostDelayedTask(FROM_HERE,base::BindOnce([](base::OnceClosure quit_closure) {LOG(INFO) << "'Browser process' shutting down";std::move(quit_closure).Run();},run_loop.QuitClosure()),base::Seconds(2));run_loop.Run();delete g_logger_test;g_logger_test = nullptr;return 0;
}

2、demo\client\logger_test.cc

#include "demo/client/logger_test.h"
#include <stdarg.h>
#include <stddef.h>
#include <stdint.h>
#include <iostream>
#include "base/functional/bind.h"logger_test::logger_test(mojo::Remote<sample::mojom::Logger> rc): logger_client_(std::move(rc)) {}logger_test::~logger_test() {std::cout << "~logger_test()\n";
}void logger_test::sendLoggerMsg(const std::string& msg) {logger_client_->Log(msg);
}void logger_test::GetTailCallbackTest(const std::string& msg){LOG(ERROR) << "GetTail callback " << msg;}
void logger_test::sendLoggerExt(){logger_client_->GetTail(base::BindOnce(&logger_test::GetTailCallbackTest,base::Unretained(this)));
}

3、demo\client\logger_test.h


#ifndef CHROME_BROWSER_LOGGER_TEST_H_
#define CHROME_BROWSER_LOGGER_TEST_H_
#include <string>
#include <vector>
#include "base/logging.h"
#include "demo/logger/public/mojom/sample.mojom.h"
#include "mojo/public/cpp/bindings/pending_receiver.h"
#include "mojo/public/cpp/bindings/pending_remote.h"
#include "mojo/public/cpp/bindings/receiver.h"
#include "mojo/public/cpp/bindings/remote.h"class logger_test {public:logger_test(mojo::Remote<sample::mojom::Logger> rc);~logger_test();void sendLoggerMsg(const std::string& msg);void sendLoggerExt();private:void GetTailCallbackTest(const std::string& msg);/* data */mojo::Remote<sample::mojom::Logger> logger_client_;
};#endif  // CHROME_BROWSER_LOGGER_TEST_H_

四、service.exe端代码: 

1、demo\service\service.cc  //service.exe main函数实现文件:

#include <stdarg.h>
#include <stddef.h>
#include <stdint.h>
#include <iostream>#include <initializer_list>
#include <memory>
#include <string>
#include <tuple>
#include <vector>#include "base/at_exit.h"
#include "base/command_line.h"
#include "base/files/file_path.h"
#include "base/files/file_util.h"
#include "base/files/memory_mapped_file.h"
#include "base/logging.h"
#include "base/process/launch.h"
#include "base/run_loop.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_util.h"
#include "base/strings/utf_string_conversions.h"
#include "base/threading/thread.h"
#include "build/build_config.h"
#include "demo/process_bootstrapper_helper.h"
#include "demo/service/LoggerImpl.h"
#include "mojo/core/embedder/configuration.h"
#include "mojo/core/embedder/embedder.h"
#include "mojo/core/embedder/scoped_ipc_support.h"
#include "mojo/public/cpp/bindings/associated_receiver.h"
#include "mojo/public/cpp/bindings/associated_remote.h"
#include "mojo/public/cpp/bindings/pending_receiver.h"
#include "mojo/public/cpp/bindings/pending_remote.h"
#include "mojo/public/cpp/bindings/receiver.h"
#include "mojo/public/cpp/bindings/remote.h"
#include "mojo/public/cpp/platform/platform_channel.h"
#include "mojo/public/cpp/system/invitation.h"
#include "mojo/public/cpp/system/message_pipe.h"LoggerImpl* g_process_impl = nullptr;void BindProcessImpl(mojo::ScopedMessagePipeHandle pipe) {// Create a receivermojo::PendingReceiver<sample::mojom::Logger> pending_receiver(std::move(pipe));g_process_impl = new LoggerImpl(std::move(pending_receiver));
}int main(int argc, const char* argv[]) {base::AtExitManager at_exit_manager;base::CommandLine::Init(argc, argv);const base::CommandLine& command_line =*base::CommandLine::ForCurrentProcess();std::wcout << command_line.GetCommandLineString().c_str()<<"\n";ProcessBootstrapperHelp bootstrapper;bootstrapper.InitMainThread(base::MessagePumpType::IO);bootstrapper.InitMojo(/*as_browser_process=*/false);// Accept an invitation.//// `RecoverPassedEndpointFromCommandLine()` is what makes use of the mojo// platform channel handle that gets printed in the above `LOG()`; this is the// file descriptor of the first connection that this process shares with the// browser.mojo::IncomingInvitation invitation = mojo::IncomingInvitation::Accept(mojo::PlatformChannel::RecoverPassedEndpointFromCommandLine(*base::CommandLine::ForCurrentProcess()));// Extract one end of the first pipe by the name that the browser process// added this pipe to the invitation by.mojo::ScopedMessagePipeHandle pipe = invitation.ExtractMessagePipe("pipe");base::RunLoop run_loop;base::SequencedTaskRunner::GetCurrentDefault()->PostTask(FROM_HERE, base::BindOnce(&BindProcessImpl, std::move(pipe)));run_loop.Run();std::cout << "service\n";return 0;
}

LoggerImpl.h 和LoggerImpl.cc 是sample::mojom::Logger 实现类:

2、demo\service\LoggerImpl.h

#ifndef CHROME_BROWSER_LOGGERIMPL_H_
#define CHROME_BROWSER_LOGGERIMPL_H_#include <string>
#include <vector>
#include "base/logging.h"
#include "demo/logger/public/mojom/sample.mojom.h"
#include "mojo/public/cpp/bindings/pending_receiver.h"
#include "mojo/public/cpp/bindings/receiver.h"class LoggerImpl : public sample::mojom::Logger {public:// NOTE: A common pattern for interface implementations which have one// instance per client is to take a PendingReceiver in the constructor.explicit LoggerImpl(mojo::PendingReceiver<sample::mojom::Logger> pending_receiver);~LoggerImpl() override;// sample::mojom::Logger:void Log(const std::string& message) override;void GetTail(GetTailCallback callback) override;void OnError();private:mojo::Receiver<sample::mojom::Logger> receiver_;std::vector<std::string> lines_;LoggerImpl(const LoggerImpl&) = delete;LoggerImpl& operator=(const LoggerImpl&) = delete;
};#endif  // CHROME_BROWSER_LOGGERIMPL_H_

3、demo\service\LoggerImpl.cc


#include "demo/service/LoggerImpl.h"
#include "base/functional/bind.h"LoggerImpl::LoggerImpl(mojo::PendingReceiver<sample::mojom::Logger> pending_receiver): receiver_(this, std::move(pending_receiver)) {// receiver_.Bind(std::move(pending_receiver));receiver_.set_disconnect_handler(base::BindOnce(&LoggerImpl::OnError, base::Unretained(this)));
}LoggerImpl::~LoggerImpl() {}// sample::mojom::Logger:
void LoggerImpl::Log(const std::string& message) {LOG(ERROR) << "[Logger] " << message;lines_.push_back(message);
}void LoggerImpl::GetTail(GetTailCallback callback) {std::move(callback).Run(lines_.back());
}void LoggerImpl::OnError() {LOG(ERROR) << "Client disconnected! Purging log lines.";lines_.clear();
}

五、初始化mojo和线程辅助类: 

demo\process_bootstrapper_helper.h

// Copyright 2023 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.#ifndef CODELABS_MOJO_EXAMPLES_PROCESS_BOOTSTRAPPER_HELPER_H_
#define CODELABS_MOJO_EXAMPLES_PROCESS_BOOTSTRAPPER_HELPER_H_#include "base/message_loop/message_pump.h"
#include "base/run_loop.h"
#include "base/task/sequence_manager/sequence_manager.h"
#include "base/threading/thread.h"
#include "mojo/core/embedder/embedder.h"
#include "mojo/core/embedder/scoped_ipc_support.h"class ProcessBootstrapperHelp {public:ProcessBootstrapperHelp();~ProcessBootstrapperHelp();// This sets up the main thread with a message pump of `type`, and optionally// a dedicated IO thread if `type` is *not* `base::MessagePumpType::IO`.void InitMainThread(base::MessagePumpType type) {// Creates a sequence manager bound to the main thread with a message pump// of some specified type. The message pump determines exactly what the// event loop on its thread is capable of (i.e., what *kind* of messages it// can "pump"). For example, a `DEFAULT` message pump is capable of// processing simple events, like async timers and posted tasks. The `IO`// message pump type — which is used in every example in this codelab — is// capable of asynchronously processing IO over IPC primitives like file// descriptors, used by Mojo. A thread with *that* kind of message pump is// required for any process using Mojo for IPC.std::unique_ptr<base::MessagePump> pump = base::MessagePump::Create(type);sequence_manager =base::sequence_manager::CreateSequenceManagerOnCurrentThreadWithPump(std::move(pump),base::sequence_manager::SequenceManager::Settings::Builder().SetMessagePumpType(type).Build());default_tq = std::make_unique<base::sequence_manager::TaskQueue::Handle>(sequence_manager->CreateTaskQueue(base::sequence_manager::TaskQueue::Spec(base::sequence_manager::QueueName::DEFAULT_TQ)));sequence_manager->SetDefaultTaskRunner((*default_tq)->task_runner());if (type == base::MessagePumpType::DEFAULT) {InitDedicatedIOThread();}}// Must be called after `InitMainThread()`.void InitMojo(bool as_browser_process) {CHECK(default_tq) << "Must call `InitMainThread()` before `InitMojo()`";// Basic Mojo initialization for a new process.mojo::core::Configuration config;// For mojo, one process must be the broker process which is responsible for// trusted cross-process introductions etc. Traditionally this is the// "browser" process.config.is_broker_process = as_browser_process;mojo::core::Init(config);// The effects of `ScopedIPCSupport` are mostly irrelevant for our simple// examples, but this class is used to determine how the IPC system shuts// down. The two shutdown options are "CLEAN" and "FAST", and each of these// may determine how other processes behave if *this* process has a message// pipe that is in the middle of proxying messages to another process where// the other end of the message pipe lives.//// In real Chrome, both the browser and renderer processes can safely use// `FAST` mode, because the side effects of quickly terminating the IPC// system in the middle of cross-process IPC message proxying is not// important. See this class's documentation for more information on// shutdown.//// We initialize `ipc_support` with a task runner for whatever thread should// be the IO thread. This means preferring `io_task_runner` when it is// non-null, and the default task runner otherwise.mojo::core::ScopedIPCSupport ipc_support(io_task_runner ? io_task_runner : (*default_tq)->task_runner(),mojo::core::ScopedIPCSupport::ShutdownPolicy::FAST);}std::unique_ptr<base::sequence_manager::TaskQueue::Handle> default_tq;std::unique_ptr<base::sequence_manager::SequenceManager> sequence_manager;scoped_refptr<base::SingleThreadTaskRunner> io_task_runner;private:// Note that you cannot call this if you've ever called// `InitMainThread(base::MessagePumpType::IO)` since that means the main// thread *itself* the IO thread.void InitDedicatedIOThread() {io_thread_ = std::make_unique<base::Thread>("ipc!");io_thread_->StartWithOptions(base::Thread::Options(base::MessagePumpType::IO, 0));io_task_runner = io_thread_->task_runner();}std::unique_ptr<base::Thread> io_thread_;
};ProcessBootstrapperHelp::ProcessBootstrapperHelp() = default;
ProcessBootstrapperHelp::~ProcessBootstrapperHelp() = default;#endif  // CODELABS_MOJO_EXAMPLES_PROCESS_BOOTSTRAPPER_HELPER_H_

六、添加demo\build.gn文件:

   1、  dem\BUILD.gn

# Copyright 2014 The Chromium Authors
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.import("//build/config/compiler/compiler.gni")group("jdcode_mojo_examples") {testonly = trueif (is_win) {deps = [":client",":service",]}
}executable("client") {sources = ["client/client.cc","client/logger_test.cc","client/logger_test.h","process_bootstrapper_helper.h",]if (is_win) {ldflags = [ "/LARGEADDRESSAWARE" ]}deps = ["//base","//build/win:default_exe_manifest","//demo/logger/public/mojom","//ipc","//mojo/core/embedder","//mojo/public/cpp/platform","//mojo/public/mojom/base",]
}executable("service") {sources = ["service/service.cc","service/LoggerImpl.h","service/LoggerImpl.cc","process_bootstrapper_helper.h",]if (is_win) {ldflags = [ "/LARGEADDRESSAWARE" ]}deps = ["//base","//build/win:default_exe_manifest","//demo/logger/public/mojom","//ipc","//mojo/core/embedder","//mojo/public/cpp/platform","//mojo/public/mojom/base",]
}

2、src\BUILD.gn 追加如下:

    if (is_win) {deps += ["//demo/logger/public/mojom","//demo:jdcode_mojo_examples",]}

七、开始编译和调试:

1、gn gen out/debug

   自动生成mojo文件:   

2、ninja -C out/debug client

编译client.exe

3、ninja -C out/debug service

编译service.exe

 4、最终生成文件如图:

八、看下调试堆栈效果: 

  8.1)、 client.exe主要流程:

1、初始化线程和mojo // mojo::core::Init(config);

  ProcessBootstrapperHelp bootstrapper;

  bootstrapper.InitMainThread(base::MessagePumpType::IO);

  bootstrapper.InitMojo(/*as_browser_process=*/true); 

2、LaunchAndConnect();

 2.1)、mojo::OutgoingInvitation建立并且启动service.exe[通过base::LaunchProcess],

2.2)将./service --mojo-platform-channel-handle=508传递给service.exe

3、CreateProcessRemote调用Log和GetTail接口函数

8.2)、service.exe主要流程:

1、初始化线程和mojo // mojo::core::Init(config);

  ProcessBootstrapperHelp bootstrapper;

  bootstrapper.InitMainThread(base::MessagePumpType::IO);

  bootstrapper.InitMojo(/*as_browser_process=*/false);

2、与client.exe建立链接:

  mojo::IncomingInvitation invitation = mojo::IncomingInvitation::Accept(

      mojo::PlatformChannel::RecoverPassedEndpointFromCommandLine(

          *base::CommandLine::ForCurrentProcess()));

  // Extract one end of the first pipe by the name that the browser process

  // added this pipe to the invitation by.

  mojo::ScopedMessagePipeHandle pipe = invitation.ExtractMessagePipe("pipe");

3、BindProcessImpl(mojo::ScopedMessagePipeHandle pipe)处理客户端client.exe发出的函数请求Log和GetTail

8.3)、看下最终效果图:

总结: 

本文演示了通过mojo::IncomingInvitation进行通信的完整例子,仅供参考,mojo基础和原理参考官网介绍。

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

sparkSQL面试题

一、查询所有数学课程成绩大于语文课程成绩的学生学号 数据 1,yuwen,43 1,shuxue,55 2,yuwen,77 2,shuxue,88 3,yuwen,98 3,shuxue,65 3,yingyu,88 基本步骤&#xff1a; 进行行转列比较语文与数学的成绩 SQL代码&#xff1a; with t1 as(SELECT id,sum(if(name yuwen,sc…

算法|牛客网华为机试21-30C++

牛客网华为机试 上篇&#xff1a;算法|牛客网华为机试10-20C 文章目录 HJ21 简单密码HJ22 汽水瓶HJ23 删除字符串中出现次数最少的字符HJ24 合唱队HJ25 数据分类处理HJ26 字符串排序HJ27 查找兄弟单词HJ28 素数伴侣HJ29 字符串加解密HJ30 字符串合并处理 HJ21 简单密码 题目描…

浅谈QT中Tab键的切换逻辑

浅谈QT中Tab键的切换逻辑 无意中发现在输入界面中按下Tab键时&#xff0c;没有按照预想的顺序切换焦点事件&#xff0c;如下图所示 这个现象还是很有趣&#xff0c;仔细观察了下&#xff0c;默认的切换顺序是按照控件拖入顺序&#xff0c;那么知道了这个问题想要解决起来就很简…

科研绘图系列:R语言组合连线图和箱线图(linechart+boxplot)

文章目录 介绍加载R包数据数据预处理画图1画图2系统信息介绍 连线图(Line Chart)是一种常用的数据可视化图表,它通过将一系列数据点用直线段连接起来来展示数据随时间或有序类别变化的趋势。以下是连线图可以表示的一些内容: 时间序列数据:展示数据随时间变化的趋势,例如…

PKG_CHECK_MODULES(FUSE,fuse)

运行 ./configure 命令报错如下&#xff1a; ./configure: line 13934: syntax error near unexpected token FUSE,fuse ./configure: line 13934: PKG_CHECK_MODULES(FUSE,fuse)解决方案&#xff1a; 命令窗口运行如下命令&#xff0c;安装 pkg-config&#xff1a; sudo …

react18中redux-promise搭配redux-thunk完美简化异步数据操作

用过redux-thunk的应该知道&#xff0c;操作相对繁琐一点&#xff0c;dispatch本只可以出发plain object。redux-thunk让dispatch可以返回一个函数。而redux-promise在此基础上大大简化了操作。 实现效果 关键逻辑代码 store/index.js import { createStore, applyMiddlewar…

Lucene分析器的详细使用(5)

文章目录 第5章 分析器5.1 分析器的组成5.1.1 字符过滤器1&#xff09;HTMLStripCharFilter2&#xff09;PatternReplaceCharFilter3&#xff09;MappingCharFilter4&#xff09;Luke使用字符过滤器 5.1.2 分词器1&#xff09;StandardTokenzier2&#xff09;keywordTokenizer3…

selinux和防火墙

SElinux 1、selinux代表的什么&#xff1f; SELinux是Security-Enhanced Linux的缩写&#xff0c;意思是安全强化的linux。 SELinux 主要由美国国家安全局&#xff08;NSA&#xff09;开发&#xff0c;当初开发的目的是为了避免资源的误用。 SELinux是对程序、文件等权限设置依…

CentOS 7 安装 ntp,自动校准系统时间

1、安装 ntp yum install ntp 安装好后&#xff0c;ntp 会自动注册成为服务&#xff0c;服务名称为 ntpd 2、查看当前 ntpd 服务的状态 systemctl status ntpd 3、启动 ntpd 服务、查看 ntpd 服务的状态 systemctl start ntpdsystemctl status ntpd 4、设置 ntpd 服务开机启…

Oracle OCP认证考试考点详解082系列11

题记&#xff1a; 本系列主要讲解Oracle OCP认证考试考点&#xff08;题目&#xff09;&#xff0c;适用于19C/21C,跟着学OCP考试必过。 51. 第51题&#xff1a; 题目 51.View the Exhibit and examine the description of the tables You execute this SQL statement Whi…

C#属性 Property

属性Property不是变量。 它们是由名为访问器方法来实现的一种方法。 实例属性表示的是实例的某个数据&#xff0c;通过这个数据反映实例当前的状态 静态属性表示的是类型的某个数据&#xff0c;通过这个数据反映类型当前的状态 意义&#xff1a; 防止恶意赋值(通过属性间接访问…

Spring框架的事务管理

目录 一、spring框架事务管理相关的类 1.PlatformTransactionManager接口 2.TransactionDefinition接口 二、spring框架声明式事务管理 1.配置文件的方式 &#xff08;1&#xff09;配置文件 &#xff08;2&#xff09;业务层 &#xff08;3&#xff09;持久层 &#…

angular实现list列表和翻页效果

说明&#xff1a;angular实现list列表和翻页效果 上一页 当前页面 下一页 效果图&#xff1a; step1: E:\projectgood\ajnine\untitled4\src\app\car\car.component.css .example-form-fields {display: flex;align-items: flex-start; }mat-list-item{background: antiquew…

PHP常量

PHP 中的常量是指一旦定义后将不能被改变的标识符。 常量可以用const和define&#xff08;&#xff09;来定义。 PHP常量的特性 不变性: 常量一旦定义&#xff0c;其值不能改变。全局作用域: 常量在定义后&#xff0c;可以在整个脚本的任何地方使用&#xff0c;无需使用 glo…

服务器作业(2)

架设一台NFS服务器&#xff0c;并按照以下要求配置 关闭防火墙 [rootlocalhost ~]# systemctl stop firewalld [rootlocalhost ~]# setenforce 0 配置文件设置&#xff1a; [rootlocalhost ~]# vim /etc/exports 1、开放/nfs/shared目录&#xff0c;供所有用户查询资料 共享…

云轴科技ZStack在CID大会上分享VF网卡热迁移技术

近日&#xff0c;2024中国云计算基础架构开发者大会&#xff08;以下简称CID大会&#xff09;在北京举行。此次大会集中展示了云计算基础架构技术领域最前沿的科创成果&#xff0c;汇聚众多的技术专家和行业先锋&#xff0c;共同探讨云计算基础设施的最新发展和未来趋势。云轴科…

【Linux】命令行参数 | 环境变量

&#x1fa90;&#x1fa90;&#x1fa90;欢迎来到程序员餐厅&#x1f4ab;&#x1f4ab;&#x1f4ab; 主厨&#xff1a;邪王真眼 主厨的主页&#xff1a;Chef‘s blog 所属专栏&#xff1a;青果大战linux 总有光环在陨落&#xff0c;总有新星在闪烁 前几天在搞硬件&…

Spring Boot 配置文件启动加载顺序

前言 Spring Boot的启动加载顺序是一个涉及多个步骤和组件的过程。Spring Boot通过一系列默认设置简化了应用程序的配置&#xff0c;使得开发者能够快速地搭建和部署应用。为了实现这一目标&#xff0c;Spring Boot采用了一种分层和优先级机制来加载配置文件。 一、Spring Bo…

Linux(inode + 软硬链接 图片+大白话)

后面也会持续更新&#xff0c;学到新东西会在其中补充。 建议按顺序食用&#xff0c;欢迎批评或者交流&#xff01; 缺什么东西欢迎评论&#xff01;我都会及时修改的&#xff01; 在这里真的很感谢这位老师的教学视频让迷茫的我找到了很好的学习视频 王晓春老师的个人空间…

python在word的页脚插入页码

1、插入简易页码 import win32com.client as win32 from win32com.client import constants import osdoc_app win32.gencache.EnsureDispatch(Word.Application)#打开word应用程序 doc_app.Visible Truedoc doc_app.Documents.Add() footer doc.Sections(1).Footers(cons…