(02)Cartographer源码无死角解析-(09) gflags与glog简介、及其main函数讲解

article/2025/10/19 13:38:34

本人讲解关于slam一系列文章汇总链接:史上最全slam从零开始,针对于本栏目讲解(02)Cartographer源码无死角解析-链接如下:
(02)Cartographer源码无死角解析- (00)目录_最新无死角讲解:https://blog.csdn.net/weixin_43013761/article/details/127350885
 
文末正下方中心提供了本人 联系方式, 点击本人照片即可显示 W X → 官方认证 {\color{blue}{文末正下方中心}提供了本人 \color{red} 联系方式,\color{blue}点击本人照片即可显示WX→官方认证} 文末正下方中心提供了本人联系方式,点击本人照片即可显示WX官方认证
 

一、前言

下面我们就正式开始讲解代码了,那么不用多说,首先我们是从 main 函数开始分析,那么问题来了,如何找到 main 函数呢?根据src/cartographer_ros/cartographer_ros/launch/lx_rs16_2d_outdoor.launch 文件,可以看到其有 node,名字name分别为:cartographer_node,cartographer_occupancy_grid_node 他们分别做什么的,先不去理会,就假设现在我们不知道。src/cartographer_ros/cartographer_ros/cartographer_ros/CMakeLists.txt 文件中搜索 cartographer_node、cartographer_occupancy_grid_node 可以看到看到如下内容:

# 生成建图节点
google_binary(cartographer_nodeSRCSnode_main.cc
)
google_binary(cartographer_occupancy_grid_nodeSRCSoccupancy_grid_node_main.cc
)

其上的 google_binary 是 google 自己封装好的cmake(cmake是一门编程语言,可以定义函数)函数,其会把 node_main.cc 生成二进制可执行文件 cartographer_node,occupancy_grid_node_main.cc 生成二进制可执行文件 cartographer_occupancy_grid_node。对于 .launch 文件中的节点 rviz 与 playbag 后续过程中进行讲解。

node_main.cc 文件位于 src/cartographer_ros/cartographer_ros/cartographer_ros/ 文件夹下,那么下面我们就开始分析吧。
 

二、gflags与glog

1、gflags

首先在 node_main.cc 文件中,可以看到如下几个宏:

DEFINE_bool 、 DEFINE_string、

其都是来自于 gflags 中,其是谷歌命令行解析工具。主要用于解析用命令行执行可执行文件时传入的参数。与getops()不同的是,在gflags中flag可以分散的定义在各个文件之中,而不用定义在一起,这就意味着在我们可以在一个单独的文件中只定义这个文件所需要用到的一些flag,链接了该文件应用都可以使用该文件中的flag,这样就能非常方便的实现代码的复用,如果不同的文件定义了相同的flag,则会产生错误,所以需要明确规范gflags的使用规范。

/*** note: gflags是一套命令行参数解析工具* DEFINE_bool在gflags.h中定义* gflags主要支持的参数类型包括bool, int32, int64, uint64, double, string等* 定义参数通过DEFINE_type宏实现, 该宏的三个参数含义分别为命令行参数名, 参数默认值, 以及参数的帮助信息* 当参数被定义后, 通过FLAGS_name就可访问到对应的参数*/

如下源码中的示例:

DEFINE_bool(collect_metrics, false,"Activates the collection of runtime metrics. If activated, the ""metrics can be accessed via a ROS service.");

DEFINE_bool 表示闯入的参数为 bool类型, collect_metrics 表示命令行需要传入的参数,false 代表默认值,后的剩下的字符串是备注, 在 .launch 中的的代码有如下:

      type="cartographer_node" args="-configuration_directory $(find cartographer_ros)/configuration_files-configuration_basename lx_rs16_2d_outdoor.lua"<remap from="points2" to="rslidar_points" /><remap from="scan" to="front_scan" /><remap from="odom" to="odom_scout" /><remap from="imu" to="imu" />

表示再在执行 cartographer_node 程序的时候,其传入的两个参数,分别为 -configuration_directory,-configuration_basename,其与 node_main.cc 文文件中的 configuration_directory,configuration_basename 是一一对应的。另外,runlaunch 指令还会把 remap(重映射)操作也当作参数传入到 main 函数的 **argv 中。如下:
在这里插入图片描述
传送进来的参数会经过

  // note: 初始化glog库google::InitGoogleLogging(argv[0]);// 使用gflags进行参数的初始化. 其中第三个参数为remove_flag// 如果为true, gflags会移除parse过的参数, 否则gflags就会保留这些参数, 但可能会对参数顺序进行调整.google::ParseCommandLineFlags(&argc, &argv, true);

两个函数的处理,处理之后变换成如下:
在这里插入图片描述
先来看看 DEFINE_xxx 的宏定义变量:

( 1 ) \color{blue}(1) (1) collect_metrics→默认为false,激活运行时度量的集合.如果激活, 可以通过ROS服务访问度量。.launch文件中没有配置,所以其使用的是默认值。
( 2 ) \color{blue}(2) (2) configuration_directory→在.launch中被被配置为 $(find cartographer_ros)/configuration_files。
( 3 ) \color{blue}(3) (3) configuration_basename→在.launch中被被配置为 lx_rs16_2d_outdoor.lua
( 4 ) \color{blue}(4) (4) load_frozen_state→默认为true,.launch中未配置,使用默认值。将保存的状态加载为冻结(非优化)轨迹。
( 5 ) \color{blue}(5) (5) start_trajectory_with_default_topics→使用默认主题(话题)开始第一个轨迹
( 6 ) \color{blue}(6) (6) save_state_filename→保存轨迹文件的路径

如果想引用这些参数,在参数的前面加入FLAGS_即可,比如引用上面的 cartographer_node,书写成 FLAGS_configuration_directory 即可。其变量本人打印如下:

FLAGS_configuration_directory→"/my_work/ROS/work/cartographer_detailed_comments_ws/install_isolated/share/cartographer_ros/configuration_files"
FLAGS_configuration_basename="lx_rs16_2d_outdoor.lua"

 

2、glog

Google glog 是一个应用级别的日志系统库.它提供基于 C++风格的流和各种辅助宏的日志 API.支持以下功能:

01.参数设置, 以命令行参数的方式设置标志参数来控制日志记录行为
02.严重性分级, 根据日志严重性分级记录日志 - INFO WARNING ERROR FATAL
03.可有条件地记录日志信息 - LOG_IF LOG_EVERY_N LOG_IF_EVERY_N LOG_FIRST_N
04.条件中止程序。丰富的条件判定宏, 可预设程序终止条件 - CHECK 宏
05.异常信号处理。程序异常情况, 可自定义异常处理过程
06.支持 debug 功能
07.自定义日志信息
08.线程安全日志记录方式
09.系统级日志记录
10.google perror 风格日志信息
11.精简日志字符串信息

其最终的结果不仅会在屏幕终端显示出来, 同时会将 log 日志写入到/tmp/…log…这个文件中,另外在 glog 中有如下宏定义,用于两个值的比较:

#define CHECK_EQ(val1, val2) CHECK_OP(_EQ, ==, val1, val2) //检查val1、val2是否相等
#define CHECK_NE(val1, val2) CHECK_OP(_NE, !=, val1, val2) //检查val1、val2是否不相等
#define CHECK_LE(val1, val2) CHECK_OP(_LE, <=, val1, val2) //检查val1是否小于等于val2
#define CHECK_LT(val1, val2) CHECK_OP(_LT, < , val1, val2) //.......
#define CHECK_GE(val1, val2) CHECK_OP(_GE, >=, val1, val2) //.......
#define CHECK_GT(val1, val2) CHECK_OP(_GT, > , val1, val2) //.......

这些语句能够很方便的把信息输入到日志中,如源码中的:

  CHECK(!FLAGS_configuration_directory.empty())<< "-configuration_directory is missing.";CHECK(!FLAGS_configuration_basename.empty())<< "-configuration_basename is missing.";

如果输入参数 configuration_directory 或 FLAGS_configuration_basename 为空,则会记录错误信息,并且终止程序。那么我们应该如何使用 glog,自定义一些我们想要的信息呢?源码中可以看到如下代码(位于node_main.cc):

  // 使用ROS_INFO进行glog消息的输出cartographer_ros::ScopedRosLogSink ros_log_sink;

其中的 ScopedRosLogSink 的定义与实现如下(位于install_isolated/include/cartographer_ros/ros_log_sink.h):

// Makes Google logging use ROS logging for output while an instance of this
// class exists.
/*** @brief 自定义的输出日志的方式: 使用ROS_INFO进行glog消息的输出*/
class ScopedRosLogSink : public ::google::LogSink {public:ScopedRosLogSink();~ScopedRosLogSink() override;void send(::google::LogSeverity severity, const char* filename,const char* base_filename, int line, const struct std::tm* tm_time,const char* message, size_t message_len) override;void WaitTillSent() override;private:bool will_die_;
};

首先其继承了 google::LogSink,只需要实现 send() 、WaitTillSent()、WaitTillSent() 三个函数即可,在src/cartographer_ros/cartographer_ros/cartographer_ros/ros_log_sink.cc 中实现,如下:

/** Copyright 2016 The Cartographer Authors** Licensed under the Apache License, Version 2.0 (the "License");* you may not use this file except in compliance with the License.* You may obtain a copy of the License at**      http://www.apache.org/licenses/LICENSE-2.0** Unless required by applicable law or agreed to in writing, software* distributed under the License is distributed on an "AS IS" BASIS,* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.* See the License for the specific language governing permissions and* limitations under the License.*/#include "cartographer_ros/ros_log_sink.h"#include <chrono>
#include <cstring>
#include <string>
#include <thread>#include "glog/log_severity.h"
#include "ros/console.h"namespace cartographer_ros {namespace {/*** @brief 根据给定的文件全路径名, 获取文件名* * @param[in] filepath * @return const char* 返回文件名*/
const char* GetBasename(const char* filepath) {// 找到 '/' 最后一次在filepath中出现的位置const char* base = std::strrchr(filepath, '/');// 找到'/',就将'/'之后的字符串返回;找不到'/', 就将整个filepath返回return base ? (base + 1) : filepath;
}}  // namespace/*** @brief 在构造函数中调用AddLogSink(), 将ScopedRosLogSink类注册到glog中*/
ScopedRosLogSink::ScopedRosLogSink() : will_die_(false) { AddLogSink(this); }
ScopedRosLogSink::~ScopedRosLogSink() { RemoveLogSink(this); }/*** @brief 重载了send()方法, 使用ROS_INFO进行glog消息的输出* * @param[in] severity 消息级别* @param[in] filename 全路径文件名* @param[in] base_filename 文件名* @param[in] line 消息所在的文件行数* @param[in] tm_time 消息的时间* @param[in] message 消息数据本体* @param[in] message_len 消息长度*/
void ScopedRosLogSink::send(const ::google::LogSeverity severity,const char* const filename,const char* const base_filename, const int line,const struct std::tm* const tm_time,const char* const message,const size_t message_len) {const std::string message_string = ::google::LogSink::ToString(severity, GetBasename(filename), line, tm_time, message, message_len);switch (severity) {case ::google::GLOG_INFO:ROS_INFO_STREAM(message_string);break;case ::google::GLOG_WARNING:ROS_WARN_STREAM(message_string);break;case ::google::GLOG_ERROR:ROS_ERROR_STREAM(message_string);break;case ::google::GLOG_FATAL:ROS_FATAL_STREAM(message_string);will_die_ = true;break;}
}// WaitTillSent()会在每次send后调用, 用于一些异步写的场景
void ScopedRosLogSink::WaitTillSent() {if (will_die_) {// Give ROS some time to actually publish our message.std::this_thread::sleep_for(std::chrono::milliseconds(1000));}
}}  // namespace cartographer_ros

上面你的代码逻辑也比较建档,核心的语句为:

  const std::string message_string = ::google::LogSink::ToString(severity, GetBasename(filename), line, tm_time, message, message_len);ROS_xxxx_STREAM(message_string);

::google::LogSink::ToString 函数会把输入的参数如 severity,line,message 等汇聚成一条字符串信息,然后根据信息等级通过 ROS_xxxx_STREAM 输出。这样输出,最终就保持了ROS 格式,如下就是一条打印信息:

[ INFO] [1667016236.198881881, 1606808687.533274071]: I1029 12:03:56.000000 24790 collated_trajectory_builder.cc:115] points2 rate: 19.29 Hz 5.18e-02 s +/- 5.36e-04 s (pulsed at 1.79% real time)

含有信息安全级别、时间戳、文件名,消息长度等。每次消息发送完成之后会调用 void ScopedRosLogSink::WaitTillSent() 函数,如果发生致命(FATAL)错误,则会通过延时,给ROS一些时间来发布我们的消息。
 

三、main函数

在理解了 gflags 与 glog 之后,再来看 main 函数就比较简单了,主要步骤如下:

( 1 ) \color{blue}(1) (1) 初始化glog库,然后解析命令行参数判断 configuration_directory 与 configuration_basename 两个参数是否输入,如果却是则报错

( 2 ) \color{blue}(2) (2) 调用 ::ros::init(argc, argv, “cartographer_node”); 初始化一个节点,再调用 ::ros::start(); 启动ROS相关的线程,网络等(一般不需要在自己的代码中显式调用)

( 3 ) \color{blue}(3) (3) 创建一个 cartographer_ros::ScopedRosLogSink 类对象,使用 ROS_INFO 进行glog消息的输出。

( 4 ) \color{blue}(4) (4) 调用 cartographer_ros 命名空间中的 Run() 函数,然后等待结束ROS相关的线程, 网络等→ros::shutdown()。

代码注释如下:

int main(int argc, char** argv) {// note: 初始化glog库google::InitGoogleLogging(argv[0]);// 使用gflags进行参数的初始化. 其中第三个参数为remove_flag// 如果为true, gflags会移除parse过的参数, 否则gflags就会保留这些参数, 但可能会对参数顺序进行调整.google::ParseCommandLineFlags(&argc, &argv, true);/*** @brief glog里提供的CHECK系列的宏, 检测某个表达式是否为真* 检测expression如果不为真, 则打印后面的description和栈上的信息* 然后退出程序, 出错后的处理过程和FATAL比较像.*/CHECK(!FLAGS_configuration_directory.empty())<< "-configuration_directory is missing.";CHECK(!FLAGS_configuration_basename.empty())<< "-configuration_basename is missing.";// ros节点的初始化,可以理解为创建一个节点::ros::init(argc, argv, "cartographer_node"); // 一般不需要在自己的代码中显式调用// 但是若想在创建任何NodeHandle实例之前启动ROS相关的线程, 网络等, 可以显式调用该函数.::ros::start();// 使用ROS_INFO进行glog消息的输出cartographer_ros::ScopedRosLogSink ros_log_sink;// 开始运行cartographer_roscartographer_ros::Run();// 结束ROS相关的线程, 网络等::ros::shutdown();
}

 

四、Run()函数

主函数main虽然比较简单,但是其调用的 cartographer_ros::Run(); 函数还是比较复杂,主要逻辑如下:

( 1 ) \color{blue}(1) (1) 关键字 constexpr 定义一个常量且初始 kTfBufferCacheTimeInSeconds = 10,调用 tf2_ros::Buffer 创建一个缓存 tf(不懂没关系,后面会讲解) 的类对象,其缓存 kTfBufferCacheTimeInSeconds(10) 秒内的 tf。然后再调用
tf2_ros::TransformListener 开启监听 tf 的独立线程。

( 2 ) \color{blue}(2) (2) 创建 NodeOptions node_options 与 TrajectoryOptions trajectory_options 两个文件,分别用来存储来自 .lua 文件的相关参数,具体是如何加载的,加载了那些参数下一篇博客为大家介绍。

( 3 ) \color{blue}(3) (3) 构建一个 MapBuilder 类对对象 map_builder,其继承于 MapBuilderInterface。MapBuilder 包含前端(TrajectoryBuilders,scan to submap) 与 后端(用于查找回环的PoseGraph) 。该为后续分析的重点。

( 4 ) \color{blue}(4) (4) Node类的初始化, 将ROS的topic传入SLAM, 也就是MapBuilder,这样接收到的数据,就能够处理然后传递给 MapBuilder,从而运转起 Cartographer 的核心算法。

( 5 ) \color{blue}(5) (5) 根据输入参数决定是否加载pbstream文件,是否使用默认topic 开始轨迹。然后执行 ros::spin();→单线程,回调函数执行有一定次序,不能并发。

( 6 ) \color{blue}(6) (6) ros线程结束时,结束所有处于活动状态的轨迹,当所有的轨迹结束时, 再执行一次全局优化,如果save_state_filename非空, 就保存pbstream文件。

代码注释如下:

namespace cartographer_ros {
namespace {void Run() {constexpr double kTfBufferCacheTimeInSeconds = 10.;tf2_ros::Buffer tf_buffer{::ros::Duration(kTfBufferCacheTimeInSeconds)};// 开启监听tf的独立线程tf2_ros::TransformListener tf(tf_buffer);NodeOptions node_options;TrajectoryOptions trajectory_options;// c++11: std::tie()函数可以将变量连接到一个给定的tuple上,生成一个元素类型全是引用的tuple// 根据Lua配置文件中的内容, 为node_options, trajectory_options 赋值std::tie(node_options, trajectory_options) =LoadOptions(FLAGS_configuration_directory, FLAGS_configuration_basename);// MapBuilder类是完整的SLAM算法类// 包含前端(TrajectoryBuilders,scan to submap) 与 后端(用于查找回环的PoseGraph) auto map_builder =cartographer::mapping::CreateMapBuilder(node_options.map_builder_options);// c++11: std::move 是将对象的状态或者所有权从一个对象转移到另一个对象, // 只是转移, 没有内存的搬迁或者内存拷贝所以可以提高利用效率,改善性能..// 右值引用是用来支持转移语义的.转移语义可以将资源 ( 堆, 系统对象等 ) 从一个对象转移到另一个对象, // 这样能够减少不必要的临时对象的创建、拷贝以及销毁, 能够大幅度提高 C++ 应用程序的性能.// 临时对象的维护 ( 创建和销毁 ) 对性能有严重影响.// Node类的初始化, 将ROS的topic传入SLAM, 也就是MapBuilderNode node(node_options, std::move(map_builder), &tf_buffer,FLAGS_collect_metrics);// 如果加载了pbstream文件, 就执行这个函数if (!FLAGS_load_state_filename.empty()) {node.LoadState(FLAGS_load_state_filename, FLAGS_load_frozen_state);}// 使用默认topic 开始轨迹if (FLAGS_start_trajectory_with_default_topics) {node.StartTrajectoryWithDefaultTopics(trajectory_options);}::ros::spin(); //单线程,回调函数执行有一定次序,不能并发。// 结束所有处于活动状态的轨迹node.FinishAllTrajectories();// 当所有的轨迹结束时, 再执行一次全局优化node.RunFinalOptimization();// 如果save_state_filename非空, 就保存pbstream文件if (!FLAGS_save_state_filename.empty()) {node.SerializeState(FLAGS_save_state_filename,true /* include_unfinished_submaps */);}
}}  // namespace
}  // namespace cartographer_ros

 

五、结语

通过该篇博客。针对 src/cartographer_ros/cartographer_ros/cartographer_ros/node_main.cc 文件进行了讲解,但是这仅仅是开始而已。接下来我们先讲解一下如何加载 .lua 配置文件的。

 
 
 


http://chatgpt.dhexx.cn/article/BucY9wUd.shtml

相关文章

glog和gflags的使用

glog&#xff1a;日志输出 支持debug功能 glog提供特定的宏只在debug模式下生效。以下分别对应LOG、LOG_IF、DLOG_EVERY_N操作的专用宏。 DLOG(INFO) << "Found cookies"; DLOG_IF(INFO, num_cookies > 10) << "Got lots of cookies"; DLO…

【gflags】gflags 命令行参数配置库

gflag介绍 google开源的gflags是一套命令行参数解析工具 主要就是用来解析命令行参数&#xff0c;并且支持从文件或者从环境变量中读入参数&#xff0c;功能十分强大 gflag的使用 我们以使用为目标进行讲解&#xff0c;不会进行深入了解&#xff08;其实并不是很难&#xff0c;…

js判断文件类型详解

js判断文件类型详解 通过file的type属性判断 <input type"file" onchange"onchangecb(this)" /> <script> function onchangecb(e) {const file e.files[0];console.log(file.type); } </script>像html中input标签&#xff0c;就是根…

JS——判断变量类型方法汇总

判断变量类型 一、使用typeof判断变量类型二、使用instanceof判断变量的类型三、使用constructor判断变量的类型四、使用Object.prototype.toString.call判断变量的类型五、使用jquery中$.type判断变量的类型。六、总结 在JS中&#xff0c;有 5 种基本数据类型和 1 种复杂数据类…

JavaScript中的类型判断

js中的类型判断 1、使用typeof进行类型判断2、使用 instanceof 判断对象3、使用Array.isArray()判断数组 1、使用typeof进行类型判断 function fun() { }; console.log("数字的类型为&#xff1a;" typeof (1)); console.log("字符串的类型为&#xff1a;&quo…

js:判断数据类型的几种方法

参考文章&#xff1a;JavaScript中判断对象类型的几种方法总结 根据要判断的数据对象&#xff0c;来选用合适的方法 1、原始数据类型 6种&#xff1a;number、string、boolean、undefined、null、symbol 一、typeof let str hello; console.log(typeof str); // string【特…

JS判断数据类型的几种方式

JS中常见的几种数据类型 基本类型&#xff1a;string、boolean、number特殊类型&#xff1a;undefined和null3、引用类型&#xff1a;Array、Date、RegExp… typeof typeof检测基本数据类型时没有问题&#xff0c;但是当其对引用类型进行检测时&#xff0c;会返回object&…

js 精确判断对象的类型

在 JavaScript 里使用 typeof 来判断数据类型&#xff0c;只能区分基本类型&#xff0c;即 “number”&#xff0c;”string”&#xff0c;”undefined”&#xff0c;”boolean”&#xff0c;”object” 五种。 对于数组、函数、对象来说&#xff0c;其关系错综复杂&#xff0c…

JavaScript——判断js数据类型(类型检测)

判断js数据类型&#xff0c;也就是我们说的类型检测&#xff0c;本文提供五种方法&#xff0c;分别是typeof运算符、instanceof操作符、Object.prototype.toString方法、constructor属性、duck type。 1、typeof运算符 typeof算是最常见的了&#xff0c;使用它会返回一个字符…

js判断数据类型常用的6种方法

js判断数据类型常用的方法,不论在开发中开始在面试中,都是经常遇到的问题,尤其是在面试时,当面试官问及js判断数据类型的方法时,回答的越多,说明掌握的广度跟深度越多,感觉自己逼格也越高.废话不多说了,进入正题 常见的判断js数据类型的方法有如下几种 1.最常见的判断方法&am…

js判断数据类型的几种方法

第一种&#xff1a;Object.prototype.toString()方法&#xff1a; 注意这里是Object原型对象上的toString方法才能判断&#xff0c;其他原型对象上的不能判断&#xff0c;如 Array原型对象上的toString方法是判断不了的&#xff0c;因为它重写了toString方法。 要使用Object…

js判断数据类型(全)

js基本数据类型 基本数据类型&#xff1a; String、Number、Boolean、Undefined未定义、null空、symbol表示独一无二的值&#xff08;es6引入的新的原始数据基本类型&#xff09;。 Number&#xff1a;返回的特殊值NaN&#xff0c;表示不是数值&#xff0c;用于表示本来要返回…

判断JS数据类型的五种方法

JavaScript 中常见的几种数据类型&#xff1a; 目录 JavaScript 中常见的几种数据类型&#xff1a; 一、分类 二、判断 1、typeof null既然属于基本数据类型&#xff0c;为什么用typeof返回的是object呢&#xff1f; js小数运算出现的问题(精度丢失) 解决方法一 解决方…

Linux 文件类型及常用命令(2)(cp、mv、cat、more、less、head、tail、chmod、getenforce)

1、命令 cp&#xff1a;拷贝&#xff08;类似于Windows系统的复制&#xff0c;但区别是可以重命名&#xff09; 拷贝文件、目录&#xff08;需要使用 -r 参数&#xff09;&#xff0c;可以重命名。 2、命令 mv&#xff1a;移动或重命名 ①如果是在当前目录下移动&#xff0c…

Linux验证SELinux状态

安全增强型Linux&#xff08;SELinux&#xff09;是一个Linux内核的功能&#xff0c;它提供支持访问控制的安全政策保护机制。记录操作如何验证SELinux&#xff0c;并且避免系统无法启动的问题。 以root权限执行&#xff1a;sudo su - 运行命令getenforce&#xff0c;验证SELi…

Linux 查看 SELinux 的状态

可以运行下面的命令来查看当前服务配置的 SELinux 的状态。 getenforce 上面的输出状态显示了当前 SELinux 的配置状态。 如果上图显示当前的状态。 临时禁用 SELinux 。 使用下面的命令来临时禁用 SELinux。 setenforce 0 Linux 查看 SELinux 的状态 - 系统和容器 - OSSE…

Android Senlinux 基础说明

一、SELinux 三种模式简介 Enforcing&#xff1a;强制模式。代表SELinux在运行中&#xff0c;且已经开始限制domain/type之间的验证关系Permissive&#xff1a;宽容模式。代表SELinux在运行中&#xff0c;不过不会限制domain/type之间的验证关系&#xff0c;即使验证不正确&…

如何关闭防火墙和selinux操作

在每次部署相关软件的时候总是会遇到各种奇奇怪怪的问题&#xff0c;多半是防火墙没有关&#xff0c;或者是selinux没有关 如何关闭防火墙&#xff1a; 那先来看一下防火墙的状态&#xff1a;systemctl status firewalld 如果是下图所示开着的&#xff0c;那就把它给关了&am…

Linux下如何关闭SELinux的图文教程(完整版)

大家好&#xff01; 最近遇到Mysql数据启动报错的问题&#xff0c;于是发现是因为SELinux没有关闭&#xff0c;那么这篇文章就简单的记录SElinux是如何让关闭的。 前言 SELinux(Security-Enhanced Linux) 是美国国家安全局&#xff08;NSA&#xff09;对于强制访问控制的实现…

Android8.x/9.x/10.x user版本关闭selinux以及打开UART控制台输入和关闭内核日志输出功能

1、user版本关闭selinux 仅限于系统调试时使用&#xff0c;量产版本需要将其设置回去成ELINUX_ENFORCING模式。 1.1)背景 C:\Users\zhaojr> adb root C:\Users\zhaojr> adb remount C:\Users\zhaojr> adb shell ac8257:/ # getenforce getenforce Enforcing这个表明…