当前位置: 首页 > news >正文

Android 9.0 Vold挂载流程解析(上)

前言

我们分2篇文章来介绍Android 9.0中存储卡的挂载流程,本篇文章先介绍总体的挂载模块、Vold进程的入口main函数的详细分析,有了这些基础知识,下一篇中我们再详细介绍收到驱动层消息是怎么挂载和卸载存储卡的,还有framework层如何与vold进程通讯交流。Android 9.0 Vold挂载流程解析(下)

Android挂载模块整体框架

存储卡挂载模块由驱动层、vold进程、framework层、App层这几个模块注册,vold进程通过Socket方式监听驱动层存储卡热插拔事件(Add、Change 、Remove),创建相应的磁盘管理类,管理磁盘的生命周期状态,提供挂载、卸载等功能,并把相应磁盘信息状态通过Binder的方式回调给Framework层,方便App层获取磁盘信息和状态。以下是其整体的模块框架图:
挂载流程框架图

从图中我们知道vold有三个核心的类,NetlinkManager、VolumeManager、VoldNativeService,这三个类在启动vold进程时就会调用其start方法启动,NetlinkManager里创建了Socket连接并交给NetlinkHandler处理通讯,由NetlinkHandler监听驱动层发送的uevent事件,并转发给VolumeManager处理,VolumeManager接受到相应的事件,会创建存储管理的类获取存储卡的信息和状态的并通过VoldNativeService回调给Framework层StorageManagerService处理,StroageManagerService也可以通过binder机制调用VoldNativeSerivice的方法,设置userId,shutdown等,好让vold进程进行相应的处理。StorageManagerService也提供了存储卡操作相关的方法给APP调用,App通过获取StorageManager类间接调用StorageManagerService中的方法。

Vold进程main函数详细分析

我们从Vold进程的main.cpp中入手开始分析
vold进程main.cpp路径:system/vold/main.cpp

int main(int argc, char** argv) {atrace_set_tracing_enabled(false);//设置日志等级setenv("ANDROID_LOG_TAGS", "*:v", 1);android::base::InitLogging(argv, android::base::LogdLogger(android::base::SYSTEM));LOG(INFO) << "Vold 3.0 (the awakening) firing up";ATRACE_BEGIN("main");//打印支持的底层文件系统LOG(VERBOSE) << "Detected support for:"<< (android::vold::IsFilesystemSupported("ext4") ? " ext4" : "")<< (android::vold::IsFilesystemSupported("f2fs") ? " f2fs" : "")<< (android::vold::IsFilesystemSupported("vfat") ? " vfat" : "")// Mediatek Android Patch Begin<< (android::vold::IsFilesystemSupported("ntfs") ? " ntfs" : "")<< (android::vold::IsFilesystemSupported("cifs") ? " cifs" : "");// Mediatek Android Patch EndVolumeManager *vm;NetlinkManager *nm;//解析参数parse_args(argc, argv);sehandle = selinux_android_file_context_handle();if (sehandle) {selinux_android_set_sehandle(sehandle);}
//创建/dev/block/vold目录,挂载存储卡了其下有对应的节点信息mkdir("/dev/block/vold", 0755);/* For when cryptfs checks and mounts an encrypted filesystem */klog_set_level(6);/* Create our singleton managers *///单例模式获取VolumeManager对象if (!(vm = VolumeManager::Instance())) {LOG(ERROR) << "Unable to create VolumeManager";exit(1);}
//单例模式获取NetlinkManager对象if (!(nm = NetlinkManager::Instance())) {LOG(ERROR) << "Unable to create NetlinkManager";exit(1);}
//设置是否打开VolumeManager中的日志,默认falseif (android::base::GetBoolProperty("vold.debug", false)) {vm->setDebug(true);}
//调用其start方法,稍后分析 1if (vm->start()) {PLOG(ERROR) << "Unable to start VolumeManager";exit(1);}bool has_adoptable;bool has_quota;bool has_reserved;
//解析fstab文件,该文件描述系统中各种文件系统的信息;我以MTK9669为例分析其fsab文件路径在vendor/etc/fstab.m7642
//稍后详细分析该方法 2if (process_config(vm, &has_adoptable, &has_quota, &has_reserved)) {PLOG(ERROR) << "Error reading configuration... continuing anyways";}ATRACE_BEGIN("VoldNativeService::start");//启动与framework通讯的服务if (android::vold::VoldNativeService::start() != android::OK) {LOG(ERROR) << "Unable to start VoldNativeService";exit(1);}ATRACE_END();LOG(DEBUG) << "VoldNativeService::start() completed OK";ATRACE_BEGIN("NetlinkManager::start");//调用NetlinkManager start 方法    //稍后详细分析 3if (nm->start()) {PLOG(ERROR) << "Unable to start NetlinkManager";exit(1);}ATRACE_END();// This call should go after listeners are started to avoid// a deadlock between vold and init (see b/34278978 for details)//解析的参数设置到属性中android::base::SetProperty("vold.has_adoptable", has_adoptable ? "1" : "0");android::base::SetProperty("vold.has_quota", has_quota ? "1" : "0");android::base::SetProperty("vold.has_reserved", has_reserved ? "1" : "0");// Do coldboot here so it won't block booting,// also the cold boot is needed in case we have flash drive// connected before Vold launchedcoldboot("/sys/block");ATRACE_END();//将vold进程中主线程加入到线程池中android::IPCThreadState::self()->joinThreadPool();LOG(INFO) << "vold shutting down";exit(0);
}

通过以上代码分析我们总结其做了以下几件事:
1.创建/dev/block/vold目录
2.单例模式获取NetlinkManager对象并调用其start方法
3.解析fstab文件
4.调用VoldNativeService::start()方法,与framework通讯
5.单例模式获取VolumeManager对象并调用其start方法

接下来分析NetlinkManager中start方法,路径:system/vold/NetlinkManager.cpp

int NetlinkManager::start() {struct sockaddr_nl nladdr;int sz = 64 * 1024;int on = 1;memset(&nladdr, 0, sizeof(nladdr));nladdr.nl_family = AF_NETLINK;nladdr.nl_pid = getpid();nladdr.nl_groups = 0xffffffff;
//创建socket客户端if ((mSock = socket(PF_NETLINK, SOCK_DGRAM | SOCK_CLOEXEC,NETLINK_KOBJECT_UEVENT)) < 0) {PLOG(ERROR) << "Unable to create uevent socket";return -1;}// When running in a net/user namespace, SO_RCVBUFFORCE will fail because// it will check for the CAP_NET_ADMIN capability in the root namespace.// Try using SO_RCVBUF if that fails.if ((setsockopt(mSock, SOL_SOCKET, SO_RCVBUFFORCE, &sz, sizeof(sz)) < 0) &&(setsockopt(mSock, SOL_SOCKET, SO_RCVBUF, &sz, sizeof(sz)) < 0)) {PLOG(ERROR) << "Unable to set uevent socket SO_RCVBUF/SO_RCVBUFFORCE option";goto out;}if (setsockopt(mSock, SOL_SOCKET, SO_PASSCRED, &on, sizeof(on)) < 0) {PLOG(ERROR) << "Unable to set uevent socket SO_PASSCRED option";goto out;}
//绑定服务端if (bind(mSock, (struct sockaddr *) &nladdr, sizeof(nladdr)) < 0) {PLOG(ERROR) << "Unable to bind uevent socket";goto out;}
//交给NetlinkHander处理通讯mHandler = new NetlinkHandler(mSock);if (mHandler->start()) {PLOG(ERROR) << "Unable to start NetlinkHandler";goto out;}return 0;
//关闭socket
out:close(mSock);return -1;
}

该方法处理很简单就是建立了Socket并交给其NetlinkHandler处理,调用其start方法,接下来看NetlinkHandler中做了什么
路径:system/vold/NetlinkHandler.cpp

int NetlinkHandler::start() {//调用其父类SocketListener中的方法,开始监听服务端中的消息//消息会解析成NetlinkEvent对象作为参数并回调onEvent(NetlinkEvent *evt)方法return this->startListener();
}void NetlinkHandler::onEvent(NetlinkEvent *evt) {VolumeManager *vm = VolumeManager::Instance();const char *subsys = evt->getSubsystem();if (!subsys) {LOG(WARNING) << "No subsystem found in netlink event";return;}// 如果subsys是block类型的就调用VolumeManager的handleBlockEvent方法处理if (std::string(subsys) == "block") {vm->handleBlockEvent(evt);}
}

通过以上代码分析NetlinkManager主要与驱动层建立Socket连接,接收存储卡的插拔事件传递给VolumeManager处理。

回到main.cpp中分析下怎么解析fstab文件的,先看下MTK9669中vendor/etc/fstab.m7642中的内容
fstab文件内容
第一列Src,下面方法解析中的rec->blk_device属性,用于匹配解析驱动穿过来的挂载的文件系统路径
第二列mnt_point,挂载点,外部存储卡挂载为auto
第三列类型,文件系统类型,外部存储卡为auto
第四列第五列为mnt_flags、fs_mgr_flags文件系统挂载标志位

static int process_config(VolumeManager* vm, bool* has_adoptable, bool* has_quota,bool* has_reserved) {ATRACE_NAME("process_config");//解析fstab文件获取fstab结构体对象fstab_default = fs_mgr_read_fstab_default();if (!fstab_default) {PLOG(ERROR) << "Failed to open default fstab";return -1;}/* Loop through entries looking for ones that vold manages */*has_adoptable = false;*has_quota = false;*has_reserved = false;//遍历每一行数据for (int i = 0; i < fstab_default->num_entries; i++) {auto rec = &fstab_default->recs[i];//fs_mgr_flags列是否包含了quotaif (fs_mgr_is_quota(rec)) {*has_quota = true;}//reserved_size是否大于0if (rec->reserved_size > 0) {*has_reserved = true;}//fs_mgr_flags列是否有voldmanaged标志if (fs_mgr_is_voldmanaged(rec)) {//是否是不可移动的if (fs_mgr_is_nonremovable(rec)) {LOG(WARNING) << "nonremovable no longer supported; ignoring volume";continue;}std::string sysPattern(rec->blk_device);std::string nickname(rec->label);//add by liuxin debugLOG(DEBUG) << "sysPattern="<<rec->blk_device<<",nickname="<<rec->label<<",mountPoint="<<rec->mount_point;int flags = 0;//fs_mgr_flags是否encryptableif (fs_mgr_is_encryptable(rec)) {flags |= android::vold::Disk::Flags::kAdoptable;*has_adoptable = true;}//没有主存储卡if (fs_mgr_is_noemulatedsd(rec)|| android::base::GetBoolProperty("vold.debug.default_primary", false)) {flags |= android::vold::Disk::Flags::kDefaultPrimary;}//把解析的参数创建DiskSource对象添加到VolumeManager中vm->addDiskSource(std::shared_ptr<VolumeManager::DiskSource>(new VolumeManager::DiskSource(sysPattern, nickname, flags)));}}return 0;
}

上面代码解析了fstabe文件设置了has_adoptable、has_quota 、has_reserved属性,并且fs_mgr_flags列是voldmanager解析处理创建DiskSource对象添加到VolumeManager中。
看打印如下:
日志打印
接下来分析VoldNativeService的start()方法,VoldNativeService继承自BinderService,BinderService继承BBinder,所以它是Binder机制中的服务端程序

status_t VoldNativeService::start() {IPCThreadState::self()->disableBackgroundScheduling(true);//注册当前客户端到binder驱动status_t ret = BinderService<VoldNativeService>::publish();if (ret != android::OK) {return ret;}//加入到binder线程池sp<ProcessState> ps(ProcessState::self());ps->startThreadPool();ps->giveThreadPoolName();return android::OK;
}

上述代码主要把当前的binder服务端加入到binder驱动中,方便提供给客户端调用

接下来看第五个步骤VolumeManager的start方法:

int VolumeManager::start() {ATRACE_NAME("VolumeManager::start");// Always start from a clean slate by unmounting everything in// directories that we own, in case we crashed.//卸载掉所有的存储卡unmountAll();Devmapper::destroyAll();Loop::destroyAll();// Assume that we always have an emulated volume on internal// storage; the framework will decide if it should be mounted.CHECK(mInternalEmulated == nullptr);//创建内部存储卡mInternalEmulated = std::shared_ptr<android::vold::VolumeBase>(new android::vold::EmulatedVolume("/data/media"));mInternalEmulated->create();// Consider creating a virtual disk//虚拟存储卡不考虑updateVirtualDisk();return 0;
}

上面代码很简单,先卸载所有的存储卡,再创建了内部储存卡,其EmulateVolume关于挂载卸载的操作我们在下一篇文章中再介绍了。

总结

本篇文章介绍总体的挂载模块、Vold进程的入口main函数的详细分析,下一篇将介绍收到插拔事件如果管理存储卡信息和状态与Framework层通讯。
Android 9.0 Vold挂载流程解析(下)

相关文章:

Android 9.0 Vold挂载流程解析(上)

前言 我们分2篇文章来介绍Android 9.0中存储卡的挂载流程&#xff0c;本篇文章先介绍总体的挂载模块、Vold进程的入口main函数的详细分析&#xff0c;有了这些基础知识&#xff0c;下一篇中我们再详细介绍收到驱动层消息是怎么挂载和卸载存储卡的&#xff0c;还有framework层如…...

界面组件Telerik UI for WinForms R2 2023——拥有VS2022暗黑主题

Telerik UI for WinForms拥有适用Windows Forms的110多个令人惊叹的UI控件。所有的UI for WinForms控件都具有完整的主题支持&#xff0c;可以轻松地帮助开发人员在桌面和平板电脑应用程序提供一致美观的下一代用户体验。 Telerik UI for WinForms R2 2023于今年6月份发布&…...

vue+elementui 实现文本超出长度显示省略号,鼠标移上悬浮展示全部内容

一、场景 表单内的输入框一般为固定宽度&#xff0c;当输入框内容长度超出输入框宽度时&#xff0c;需要显示省略号&#xff0c;并设置鼠标移到输入框上时悬浮展示全部内容。 <el-tooltipplacement"top-start"effect"light":content"basicData[Or…...

【STM32RT-Thread零基础入门】 5. 线程创建应用(线程创建、删除、初始化、脱离、启动、睡眠)

硬件&#xff1a;STM32F103ZET6、ST-LINK、usb转串口工具、4个LED灯、1个蜂鸣器、4个1k电阻、2个按键、面包板、杜邦线 文章目录 前言一、线程管理接口介绍二、任务&#xff1a;使用多线程的方式同时实现led闪烁和按键控制喇叭&#xff08;扫描法&#xff09;1. RT-Thread相关接…...

计算机竞赛 python+深度学习+opencv实现植物识别算法系统

0 前言 &#x1f525; 优质竞赛项目系列&#xff0c;今天要分享的是 &#x1f6a9; 基于深度学习的植物识别算法研究与实现 &#x1f947;学长这里给一个题目综合评分(每项满分5分) 难度系数&#xff1a;4分工作量&#xff1a;4分创新点&#xff1a;4分 &#x1f9ff; 更多…...

深度探索ChatGPT:如何进行专业提问以获取精确答案

ChatGPT&#xff0c;作为OpenAI的先锋&#xff0c;已经展示出其惊人的交流和理解能力。但如何才能充分利用其潜能&#xff0c;并与之进行更深入、更专业的交流呢? 下面&#xff0c;我们将从专业的角度探讨一些提问策略&#xff0c;并附上实际案例&#xff0c;让你更加熟练地与…...

1.vue3+vite开发中axios使用及跨域问题解决

一、跨域问题解决 1.基于vitevue3配置时&#xff0c;在vite.congig.js文件server项目中添加 proxy代理 文件名&#xff1a;vite.congig.js server: {open: true,//启动项目自动弹出浏览器port: 3000,proxy: {/api: {target: http://localhost:8000/api/,changeOrigin: true,rew…...

【LangChain】P1 LangChain 应用程序的核心构建模块 LLMChain 以及其三大部分

LangChain 的核心构建模块 LLMChain LangChain 应用程序的核心构建模块语言模型 - LLMs提示模板 - Prompt templates输出解析器 - Output Parsers LLMChain 组合 LangChain 应用程序的核心构建模块 LangChain 应用程序的核心构建模块 LLMChain 由三部分组成&#xff1a; 语言…...

关于查看处理端口号和进程[linux]

查看端口号 lsof -i:端口号如果-bash: lsof: 未找到命令那我们可以执行yum install lsof 删除端口号进程 一般我们都会使用kill命令 kill -l#列出所有可用信号1 (HUP)&#xff1a;重新加载进程。9 (KILL)&#xff1a;杀死一个进程。15 (TERM)&#xff1a;正常停止一个进程。 …...

C 语言的 strcat() 函数和 strncat() 函数

文章目录 strcat() 函数strncat() 函数 strcat() 函数 原型: char *strcat(char *dest, const char *src) 参数: dest – 指向目标数组&#xff0c;该数组包含了一个 C 字符串&#xff0c;且足够容纳追加后的字符串。 src – 指向要追加的字符串&#xff0c;该字符串不会覆…...

C++ string 的用法

目录 string类string类接口函数及基本用法构造函数&#xff0c;析构函数及赋值重载函数元素访问相关函数operator[]atback和front 迭代器iterator容量操作size()和length()capacity()max_sizeclearemptyreserveresizeshrink_to_fit string类对象修改操作operatorpush_backappen…...

MyBatis-Flex学习记录1---请各位大神指教

简介&#xff08;官网介绍&#xff09; MyBatis-Flex 是一个优雅的 MyBatis 增强框架&#xff0c;它非常轻量、同时拥有极高的性能与灵活性。我们可以轻松的使用 Mybaits-Flex 链接任何数据库&#xff0c;其内置的 QueryWrapper帮助我们极大的减少了 SQL 编写的工作的同时&…...

二分查找旋转数组

已知整数数组nums&#xff0c;先按升序排序后&#xff0c;再旋转。旋转k位后&#xff0c;元素分别为nums[k],nums[k1]...nums[0]...nums[k-1]。请查找target 是否存在&#xff0c;如果存在返回所在索引&#xff1b;否则返回-1。假定nums没有重复的元素。 假定排序后的数组为{1…...

关于3D位姿旋转

一. 主动旋转和被动旋转 1. active rotation 主动旋转 站在坐标系的位置看旋转目标物&#xff1a;目标物主动发生旋转。 2. passive rotation 被动旋转 站在旋转目标物的位置看坐标系&#xff1a; 坐标系发生旋转&#xff0c;相当于目标物在坐标系内的位置被动地发生了旋转…...

解锁项目成功的关键:项目经理的结构化思维之道

1. 项目经理的核心职责 作为项目经理&#xff0c;我们的工作不仅仅是跟踪进度和管理团队。我们的角色在整个项目生命周期中都是至关重要的&#xff0c;从初始概念到最终交付。以下是项目经理的几个核心职责&#xff1a; 确保项目目标的清晰性项目的成功在很大程度上取决于其目…...

力扣974被K整除的子数组

同余定理 使用前缀和哈希表 由于可能是负数所以要进行修正&#xff1a;(sum%kk)%k class Solution { public:int subarraysDivByK(vector<int>& nums, int k) {unordered_map<int,int> hash;hash[0 % k] 1; //0 这个数的余数int sum 0, ret 0;for(auto x…...

简单认识Docker数据管理

文章目录 为何需要docker数据管理数据管理类型 一、数据卷二、数据卷容器三、容器互联 为何需要docker数据管理 因为数据写入后如果停止了容器&#xff0c;再开启数据就会消失&#xff0c;使用数据管理的数据卷挂载&#xff0c;实现了数据的持久化&#xff0c;重启数据还会存在…...

UDP数据报结构分析(面试重点)

在传输层中有UDP和TCP两个重要的协议&#xff0c;下面将针对UDP数据报的结构进行分析 UDP结构图示 UDP报头结构的分析 UDP报头有4个属性&#xff0c;分别是源端口&#xff0c;目的端口&#xff0c;UDP报文长度&#xff0c;校验和&#xff0c;它们都占16位2个字节&#xff0c;所…...

【Java 动态数据统计图】动态数据统计思路案例(动态,排序,数组)二(113)

需求&#xff1a; 有一个List<Map<String.Object>>,存储了区域的数据&#xff0c; 数据是根据用户查询条件进行显示的&#xff1b;所以查询的数据是动态的&#xff1b;按区域维度统计每个区域出现的次数&#xff0c;并且按照次数的大小排序&#xff08;升序&#…...

C++进阶 类型转换

本文简介&#xff1a;介绍C中类型转换的方式 类型转换 C语言中的类型转换为什么C需要四种类型转换C强制类型转换static_castreinterpret_castconst_castdynamic_cast RTTI&#xff08;了解&#xff09;总结 C语言中的类型转换 在C语言中&#xff0c;如果赋值运算符左右两侧类型…...

Idea中隐藏指定文件或指定类型文件

Setting ->Editor ->Code Style->File Types → Ignored Files and Folders输入要隐藏的文件名&#xff0c;支持*号通配符回车确认添加...

第2步---MySQL卸载和图形化工具展示

第2步---MySQL卸载和图形化工具展示 1.MySQL的卸载 2.MySQL的图形化工具 2.1常见的图形化工具 SQLyog&#xff1a;简单。SQLyog首页、文档和下载 - MySQL 客户端工具 - OSCHINA - 中文开源技术交流社区 Mysql Workbench &#xff1a;MySQL :: MySQL Workbench DataGrip&…...

原型和原型链

好久没记了有点忘记了&#xff0c;来记录一下。 1、函数和对象的关系&#xff1a;对象都是通过函数创建的&#xff0c;函数也是一个对象。 2、原型和原型链 1.原型&#xff1a;原型分为两种 prototype&#xff1a;每一个函数都会有prototype属性&#xff0c;它指向函数的原型…...

解决ios隔空播放音频到macos没有声音的问题

解决ios隔空播放音频到macos没有声音的问题 一、检查隔空播放支持设备和系统要求二、打开隔空播放接收器三、重置MAC控制中心进程END 一、检查隔空播放支持设备和系统要求 Mac、iPhone、iPad 和 Apple Watch 上“连续互通”的系统要求 二、打开隔空播放接收器 ps;我设备是同一…...

LTPP在线开发平台【使用教程】

LTPP在线开发平台 点击访问 LTPP在线开发平台 LTPP&#xff08;Learning teaching practice platform&#xff09;在线开发平台是一个编程学习网站&#xff0c;该网站集文章学习、短视频、在线直播、代码训练、在线问答、在线聊天和在线商店于一体&#xff0c;专注于提升用户编…...

0818 新增码表 git拉取代码

目的是新增两个码表字段。然后和前端联调。 use db; delete from sys_dict_data where dict_type res_switch_status; INSERT INTO sys_dict_data VALUES (0, 1, 已接入, 1, res_switch_status, NULL, default, N, 0, , 2022-07-26 10:43:41, , NULL, NULL); INSERT INTO sys…...

AI 绘画Stable Diffusion 研究(十)sd图生图功能详解-精美二维码的制作

免责声明: 本案例所用安装包免费提供&#xff0c;无任何盈利目的。 大家好&#xff0c;我是风雨无阻。 为了让大家更直观的了解图生图功能&#xff0c;明白图生图功能到底是干嘛的&#xff0c;能做什么事情&#xff1f;今天我们继续介绍图生图的实用案例-精美二维码的制作。 对…...

C# File.ReadAllLines()报错

项目中需要读取一个文本文件的内容&#xff0c;调用C#的File.ReadAllLines(path)方法&#xff0c;但是报错&#xff0c;就提示unknown exception&#xff0c;也没其他提示了。 文件是在的&#xff0c;并且&#xff0c;如果把文件拷贝到另外一个路径&#xff0c;再次读取是正常…...

LeetCode 1162. As Far from Land as Possible【多源BFS】中等

本文属于「征服LeetCode」系列文章之一&#xff0c;这一系列正式开始于2021/08/12。由于LeetCode上部分题目有锁&#xff0c;本系列将至少持续到刷完所有无锁题之日为止&#xff1b;由于LeetCode还在不断地创建新题&#xff0c;本系列的终止日期可能是永远。在这一系列刷题文章…...

【算法】二分查找(整数二分和浮点数二分)

二分查找也称折半查找&#xff08;Binary Search&#xff09;&#xff0c;是一种效率较高的查找方法&#xff0c;时间复杂度为O(logN)。 二分查找采用了“分治”策略。使用二分查找时&#xff0c;数组中的元素之间得有单调性&#xff08;升序或者降序&#xff09;。 二分的模…...

手机网站设计思路/chatgpt网址

Ceph-dash是一款图形化展现Ceph状态的工具&#xff0c;并且部署起来非常简单&#xff08;在monitor节点上进行部署&#xff09;&#xff0c;操作如下&#xff1a;# mkdir -p /ceph-dash # cd /ceph-dash/ # ls app config.influxdb.json Dockerfile s…...

阿里云心选建站/seo技术教程博客

01 chromium较全的开关选项说明 https://peter.sh/experiments/chromium-command-line-switches/ 很多命令行开关项在这个文件中 src\chrome\common\chrome_switches.cc02 如何取消一些不感兴趣的工程 https://blog.csdn.net/dopi/article/details/27662959...

深圳福田高端网站建设/中国新闻今日头条

LAMP搭建和配置 LAMP是由Linux&#xff0c; Apache&#xff0c; MySQL&#xff0c; PHP组成的&#xff0c;即把Apache、MySQL以及PHP安装在Linux系统上&#xff0c;组成一个环境来运行PHP的脚本语言。Apache是最常用的Web服务软件&#xff0c;而MySQL是比较小型的数据库软件。…...

招聘网站怎么做seo/seo搜索引擎优化书籍

选择“项目”菜单->项目属性->配置属性->常规->字符集&#xff0c;改为“未设置”即可。 <div class"person-messagebox"><div class"left-message"><a href"https://blog.csdn.net/hellowording"><img src&qu…...

修车店怎么做网站/官网优化包括什么内容

C#中提供了好多格式化数字或字符串的方法&#xff0c;但在项目开发中&#xff0c;有很多自己需要的格式无法实现&#xff0c;那就需要我们去定义IFormatProvider,其实很简单&#xff0c;只需继承二个接口&#xff0c;然后实现二个方法就可以了。ICustomFormatter接口中实现Form…...

办网多少钱/官网seo哪家公司好

智能停车场管理系统全部采用计算机自动管理&#xff0c;监视车库情况&#xff0c;需要时&#xff0c;管理人员通过主控计算机对整个停车场情况进行监控管理。 可实时监察每辆车的出入情况&#xff0c;并自动记录&#xff0c;包括内部车辆的出入时间、车位号、停车费等信息。同时…...