linux EC驱动书写指南

2024-01-03 00:18
文章标签 linux 驱动 指南 书写 ec

本文主要是介绍linux EC驱动书写指南,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

驱动书写指南系列会提供另一个角度的驱动分析,linux内核把各驱动共同的部分抽象出来,做在一起称为框架。就比如说文件系统,linux内核定义好了文件系统中最通用的打开文件、读写文件等公共接口,但是并没有实现函数。这些定义好的接口,可以认为是框架。等到了真正的文件系统实现的时候 ,才会填充这些open、read等函数。对于实现文件系统的程序员来说,就是填充框架外的其他内容,一般都是和硬件相关性比较大。

power supply core介绍

在本文中,主要介绍怎么注册自己的ec驱动。ec驱动的框架部分,power supply都是实现过了,这里有一个介绍power supply core中,蜗窝科技有一个文章介绍power supply core的,这是连接:http://www.wowotech.net/pm_subsystem/psy_class_overview.html

在上文的 介绍里补充一点内容,power supply 硬件属性分别是什么意思,在写ec驱动的途中,一大部分时间花在研究这几个属性分别是描述什么的以及我需要什么属性,大部分还是蜗窝科技写的对几个属性的解释,能找到的中文资料非常非常少,还是挺值得记录一下的。

enum power_supply_property {/* Properties of type `int' */POWER_SUPPLY_PROP_STATUS = 0,POWER_SUPPLY_PROP_CHARGE_TYPE,POWER_SUPPLY_PROP_HEALTH,POWER_SUPPLY_PROP_PRESENT,POWER_SUPPLY_PROP_ONLINE,POWER_SUPPLY_PROP_AUTHENTIC,POWER_SUPPLY_PROP_TECHNOLOGY,POWER_SUPPLY_PROP_CYCLE_COUNT,POWER_SUPPLY_PROP_VOLTAGE_MAX,POWER_SUPPLY_PROP_VOLTAGE_MIN,POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN,POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN,POWER_SUPPLY_PROP_VOLTAGE_NOW,POWER_SUPPLY_PROP_VOLTAGE_AVG,POWER_SUPPLY_PROP_VOLTAGE_OCV,POWER_SUPPLY_PROP_VOLTAGE_BOOT,POWER_SUPPLY_PROP_CURRENT_MAX,POWER_SUPPLY_PROP_CURRENT_NOW,POWER_SUPPLY_PROP_CURRENT_AVG,POWER_SUPPLY_PROP_CURRENT_BOOT,POWER_SUPPLY_PROP_POWER_NOW,POWER_SUPPLY_PROP_POWER_AVG,POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN,POWER_SUPPLY_PROP_CHARGE_EMPTY_DESIGN,POWER_SUPPLY_PROP_CHARGE_FULL,POWER_SUPPLY_PROP_CHARGE_EMPTY,POWER_SUPPLY_PROP_CHARGE_NOW,POWER_SUPPLY_PROP_CHARGE_AVG,POWER_SUPPLY_PROP_CHARGE_COUNTER,POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT,POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX,POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE,POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE_MAX,POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT,POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT_MAX,POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT,POWER_SUPPLY_PROP_ENERGY_FULL_DESIGN,POWER_SUPPLY_PROP_ENERGY_EMPTY_DESIGN,POWER_SUPPLY_PROP_ENERGY_FULL,POWER_SUPPLY_PROP_ENERGY_EMPTY,POWER_SUPPLY_PROP_ENERGY_NOW,POWER_SUPPLY_PROP_ENERGY_AVG,POWER_SUPPLY_PROP_CAPACITY, /* in percents! */POWER_SUPPLY_PROP_CAPACITY_ALERT_MIN, /* in percents! */POWER_SUPPLY_PROP_CAPACITY_ALERT_MAX, /* in percents! */POWER_SUPPLY_PROP_CAPACITY_LEVEL,POWER_SUPPLY_PROP_TEMP,POWER_SUPPLY_PROP_TEMP_MAX,POWER_SUPPLY_PROP_TEMP_MIN,POWER_SUPPLY_PROP_TEMP_ALERT_MIN,POWER_SUPPLY_PROP_TEMP_ALERT_MAX,POWER_SUPPLY_PROP_TEMP_AMBIENT,POWER_SUPPLY_PROP_TEMP_AMBIENT_ALERT_MIN,POWER_SUPPLY_PROP_TEMP_AMBIENT_ALERT_MAX,POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW,POWER_SUPPLY_PROP_TIME_TO_EMPTY_AVG,POWER_SUPPLY_PROP_TIME_TO_FULL_NOW,POWER_SUPPLY_PROP_TIME_TO_FULL_AVG,POWER_SUPPLY_PROP_TYPE, /* use power_supply.type instead */POWER_SUPPLY_PROP_USB_TYPE,POWER_SUPPLY_PROP_SCOPE,POWER_SUPPLY_PROP_PRECHARGE_CURRENT,POWER_SUPPLY_PROP_CHARGE_TERM_CURRENT,POWER_SUPPLY_PROP_CALIBRATE,/* Properties of type `const char *' */POWER_SUPPLY_PROP_MODEL_NAME,POWER_SUPPLY_PROP_MANUFACTURER,POWER_SUPPLY_PROP_SERIAL_NUMBER,
};
  • status: 表示电池状态,充电中、放电、未充电和电池电量已满
  • charge type:表示充电类型,快充、点滴式充电器(trikle charge)
  • health:表示电池健康属性,主要用来表示电池健康、过热、损坏、过压等
  • present:表示电池或者适配器是否存在
  • online:表示电池或者适配器是否在线,有时候可能电池存在但是并没有接线。有的ec芯片这两个状态都是可以读到的。
  • authentic:真实参数,暂时在内核里没发现哪个ec驱动使用了这个属性。这个属性是为了给用户看,移动设备的适配器 或者电池是不是非标准、不合格的。
  • technology:表示电池采用的技术,lion(锂离子电池)、nimh(镍氢电池)、lipo(锂聚合物电池)、nicd(镍铬电池)、limn(锰酸锂电池)
  • voltage:电压参数,框架定义了最大、最小、设计最大、设计最小等,框架定义的单位是μV
  • current:电流参数,框架定义了最大、目前和平均等,放电为负值,单位是μA,
  • power:电流参数,和current不同的是,这个电流参数是智能电池导出的。智能电池使用功率作为工作单位和充电放电测量单位,所以不适合再导出在current属性里了。
  • charge:容量参数,框架定义了设计满、设计空、充满、空等等,框架定义的很多设计值都是用来计算电池的健康属性的,比如说设计满容量是3000,但是目前充满只有1500了,那就说明电池的健康系数是50%。只是举个例子,实际上电池健康系数不可能用仅仅一个属性来计算,单位是μAh
  • energy:功率参数,用来计算功耗,单位是μWh
  • capacity:容量百分比,常被桌面用来显示电池图标选中后,出现的提示框里显示电池容量多少。
  • temp:温度参数,注意这个问题是电池温度,并不是cpu温度,单位是摄氏度
  • time:时间参数,充满时间、放空时间等等,单位是s

最后还有几个字符串类型的属性,模块名称、生产商和序列号。

实现自己的ec驱动

定义psy设备

第一步是明确设备的充电类型,框架定义的充电类型有:


enum power_supply_type {POWER_SUPPLY_TYPE_UNKNOWN = 0,POWER_SUPPLY_TYPE_BATTERY,POWER_SUPPLY_TYPE_UPS,POWER_SUPPLY_TYPE_MAINS,POWER_SUPPLY_TYPE_USB, /* Standard Downstream Port */POWER_SUPPLY_TYPE_USB_DCP, /* Dedicated Charging Port */POWER_SUPPLY_TYPE_USB_CDP, /* Charging Downstream Port */POWER_SUPPLY_TYPE_USB_ACA, /* Accessory Charger Adapters */POWER_SUPPLY_TYPE_USB_TYPE_C, /* Type C Port */POWER_SUPPLY_TYPE_USB_PD, /* Power Delivery Port */POWER_SUPPLY_TYPE_USB_PD_DRP, /* PD Dual Role Port */POWER_SUPPLY_TYPE_APPLE_BRICK_ID, /* Apple Charging Method */
};  

一般来讲笔记本都是这两个充电类型:mains类型和battery类型,移动设备需要定义usb的充电类型。先定义两个最常见的psy设备:适配器和电池,适配器的充电类型是mains,电池是battery。


static const struct power_supply_desc shiwen_ac_desc = {.name = "shiwen_ac",.type = POWER_SUPPLY_TYPE_MAINS,.properties = shiwen_power_ac_props,.num_properties = ARRAY_SIZE(shiwen_power_ac_props),.get_property = shiwen_power_get_ac_property,
};
static const struct power_supply_desc shiwen_bat_desc = {.name = "shiwen_battery",.type = POWER_SUPPLY_TYPE_BATTERY,.properties = shiwen_power_battery_props,.num_properties = ARRAY_SIZE(shiwen_power_battery_props),.get_property = shiwen_power_get_battery_property,
};

选择psy设备属性

定义好之后,再选择每个psy设备的硬件属性,这部分要看ec芯片提供了什么数据然后选择定义什么属性。上一节提到的属性是框架定义的全部属性,ec芯片不可能提供这全部的数据。这里写的示例,就简单选择几个上层需要的属性吧。适配器就一个是否在线属性,电池桌面需要的属性有电池存在标志、充满时间、放空时间、电池状态、电池容量百分比、电池容量等级、模块名、制造商。


static enum power_supply_property shiwen_power_ac_props[] = {POWER_SUPPLY_PROP_ONLINE,
};
static enum power_supply_property shiwen_power_battery_props[] = {POWER_SUPPLY_PROP_STATUS,POWER_SUPPLY_PROP_PRESENT,POWER_SUPPLY_PROP_CAPACITY, /* in percents! */POWER_SUPPLY_PROP_CAPACITY_LEVEL,POWER_SUPPLY_PROP_TIME_TO_EMPTY,POWER_SUPPLY_PROP_TIME_TO_FULL,POWER_SUPPLY_PROP_MODEL_NAME,POWER_SUPPLY_PROP_MANUFACTURER,
};

填充获取psy属性的方法

接着要告诉内核如何获取前面定义的psy属性,psy core留了一个get_property接口,需要驱动工程师把方法填充在接口里。这部分就完全和硬件相关了,不同的ec芯片,读取psy属性方法不同,比如说本文的示例代码里仅仅实现了一个固定值。


struct int shiwen_power_get_battery_property(struct power_supply *psy,enum power_supply_property psp,union power_supply_propval *val)
{case POWER_SUPPLY_PROP_ONLINE:val->intval = 1;                        //always assume ac onlinebreak;default:val->intval = -1;pr_err("property error\n");
}
struct int shiwen_power_get_battery_property(struct power_supply *psy,enum power_supply_property psp,union power_supply_propval *val)
{switch (psp) {case POWER_SUPPLY_PROP_MODEL_NAME:val->strval = "Shiwen example driver";break;case POWER_SUPPLY_PROP_MANUFACTURER:val->strval = "Shiwen example";break;case POWER_SUPPLY_PROP_STATUS:val->intval = Charging;                                    //always assume battery chargingbreak;case POWER_SUPPLY_PROP_PRESENT:val->intval = 1;                                                    //always assume battery presentbreak;case POWER_SUPPLY_PROP_CAPACITY:val->intval = 100;                                                //always assume battery capacity 100%break;case POWER_SUPPLY_PROP_CAPACITY_LEVEL:val->intval = POWER_SUPPLY_CAPACITY_LEVEL_FULL; break;case POWER_SUPPLY_PROP_TIME_TO_EMPTY:val->intval = 7200;                                                //always assume battery need 2 hours to emptybreak;case POWER_SUPPLY_PROP_TIME_TO_FULL:val->intval = 0;break;default:val->intval =-1;pr_err("property error\n");break;
}

到这一步,两个psy设备定义完成了。接下来只需要把注册设备,在sys下就能看到接口了。

注册设备

设备注册,psy core提供了接口,只需要调用接口并做好错误处理即可。

static int __init shiwen_power_init(void)
{shiwen_ac = power_supply_register(NULL, &shiwen_ac_desc, NULL);if (IS_ERR(shiwen_ac)) {pr_err("%s: failed to register\n",__func__, shiwen_ac_desc.name);ret = PTR_ERR(shiwen_ac);goto failed_ac;}shiwen_bat = power_supply_register(NULL, &shiwen_bat_desc, NULL);if (IS_ERR(shiwen_ac)) {pr_err("%s: failed to register %s\n", __func__, shiwen_bat_desc.name);ret = PTR_ERR(shiwen_bat);goto failed_bat;}return 0;
failed_bat:power_supply_unregister(shiwen_bat);
failed_ac:power_supply_unregister(shiwen_ac);return ret;
}

查看结果

到这里,非常简单的ec示例驱动注册就完成了,在sys下查看一下是否正确注册了设备,并且获取到正确的内容。

deepin@deepin-PC:~$ ls /sys/class/power_supply/
shiwen_ac shiwen_battery
deepin@deepin-PC:~$ 
deepin@deepin-PC:~$ ls /sys/class/power_supply/shiwen_ac/
online power subsystem type uevent
deepin@deepin-PC:~$ cat /sys/class/power_supply/shiwen_ac/uevent 
POWER_SUPPLY_NAME=shiwen_ac
POWER_SUPPLY_ONLINE=1
deepin@deepin-PC:~$ ls /sys/class/power_supply/shiwen_battery
capacity capacity_level  manufacturer mcu_time_effect model_name power present status subsystem time_to_empty_avg time_to_full_avg type uevent
deepin@deepin-PC:~$ cat /sys/class/power_supply/shiwen_battery/uevent 
POWER_SUPPLY_NAME=shiwen_battery
POWER_SUPPLY_STATUS=Charging
POWER_SUPPLY_PRESENT=1
POWER_SUPPLY_CAPACITY=100
POWER_SUPPLY_CAPACITY_LEVEL=POWER_SUPPLY_CAPACITY_LEVEL_FULL
POWER_SUPPLY_TIME_TO_EMPTY_AVG=7200
POWER_SUPPLY_TIME_TO_FULL_AVG=0
POWER_SUPPLY_MODEL_NAME=Shiwen example driver
POWER_SUPPLY_MANUFACTURER=Shiwen example

到这,就大功告成啦。sys下文件创建、sys下文件的读写都有sysfs框架和psy core框架帮我们实现。上文是一个非常非常简单的psy驱动实现流程,ec状态的改变驱动并没有关注,都是依赖上层daemon或者读取 sys下的文件来感知,对于用户来说相当于是轮询读取。实际上日常我们遇到的ec芯片都没有这么原始,ec事件基本上都是中断通知,所以一个完成的ec驱动还要在上面的流程里面加上中断的注册和处理代码。

增加中断处理

关注过我之前写过两个中断介绍文章的童鞋肯定知道驱动注册中断的第一步,肯定是实现中断处理函数。处理函数也和硬件有关系,有的ec芯片ac插拔事件和电池事件的中断是分开的,假设我们虚构的ec芯片只有ac和电池的插拔才能触发中断(看内核psy实现的芯片,这样的中断也是最常见的)。假设ac事件和电池事件共享155号中断(随意选的,没有任何理论依据),那么中断处理函数处理第一步肯定是确定中断源。接着根据中断源,更新一下设备状态即可。

static irqreturn_t shiwen_ec_interrupt(int irq, void *dev_id)
{int status;int source;source = SHIWEN_READ_INTSOURCE_REG();if (source == SHIWEN_BATTERY)power_supply_changed(shiwen_battery);else if(source == SHIWEN_AC)power_supply_changed(shiwen_ac);else//there are no psy eventreturn IRQ_NONE;                                                   return IRQ_HANDLED;
}

更新整个psy设备状态的函数是psy core框架提供的,很好用吧。psy会根据参数传进去的设备,调用获取属性函数,更新psy设备属性。这个处理函数非常简单,因为ec芯片的中断就很简单。如果是比较复杂的ec芯片的话,可能中断源分的比较多,比如说电压变化、容量变化、电流变化等等都会有一种中断的产生。注意:为了避免读到的状态有问题,需要在中断产生后等一段时间再去读ec状态,所以中断处理函数一般都需要有延时读取ec状态。power_supply_changed函数内部实现是采用work queue方式读取状态的,我们自己实现的话,为了简单且保险,可以用delay work。假设说,需要实现一个电压变化中断的处理函数:

static irqreturn_t shiwen_voltage_interrupt(int irq, void *dev_id)
{int status;schedule_delayed_work(&ec_work, JIFFIES_NUM);return IRQ_HANDLED;
}static void ec_work_func(struct work_struct *work)
{shiwen_update_voltage();
}

在 中断处理函数里面,仅有调用delay work内容,延时过了之后函数会被加载工作队列里,借此达到延时的目的。接下来指定一下延时工作进程需要执行的函数,这是工作进程要求的。这就是一个基本的读取某一个属性的中断函数实现,没实现真正电压读取是因为,电压读取是真正和硬件相关的,每款芯片都不一样,我也实在是虚构不出来了orz…

处理函数写完了之后,中断注册到内核里就可以使用啦。剩下的中断触发是设备的事,中断触发之后的感知是cpu的事,感知到中断之后在调到处理函数之前是内核中断子系统的事,前面中断的两篇文章介绍过内核已经做好了,驱动并不关心。中断的注册一般放在psy设备注册之后,修改过得psy设备注册代码如下:

static int __init shiwen_power_init(void){shiwen_ac = power_supply_register(NULL, &shiwen_ac_desc, NULL);......shiwen_bat = power_supply_register(NULL, &shiwen_bat_desc, NULL);......//shiwen_ac and shiwen_battery called shiwen_battery_acret =request_irq(155,  shiwen_ec_interrupt, IRQF_SHARED, shiwen_battery_ac);                  INIT_DELAYED_WORK(&ec_work, ec_work_func);return 0;......
}

增加了中断注册个delay work初始化的代码(如果需要自己使用delay work读取某一个属性的时候),代码写到这里,一个带基本功能的ec驱动就做完了。看吧,还是很简单的吧。

如果电池芯片不支持中断呢?

对于虚拟的电池或者其他规范的电池芯片,硬件认为驱动只要读到正确的值就可以了,电池芯片把状态送到某个位置,任何电池事件并不会触发中断,包括适配器插拔和电池插拔。这时候就需要驱动工程师想个办法把电池驱动套到psy框架下了,最简单的办法肯定是——定时器。既然不支持中断,那我定时读取总可以了吧,虽然不能保证状态及时更新。
首先定义一个定时器和定时器超时处理函数:

struct timer_list power_timer;
#define TIMER_COUNT    60   /*设置超时时间为1分钟*/void power_timer_handler(struct timer_list *unused)
{mod_timer(&power_timer, jiffies+HZ * TIMER_COUNT);power_supply_changed(shiwen_ac);power_supply_changed(shiwen_bat);
}

设置超时时间是60s,定时器超时处理函数就做了俩事,再次设置定时器超时时间和更新shiwen_ac和shiwen_bat状态。这里强调一点,尽量调用psy core提供的更新状态函数,而不要自己实现。因为这个函数的功能并不仅仅是调用get_properties接口,还有触发 内核uevent等,不要问为啥强调。orz…
最后在驱动入口函数加上定时器设置和 定时器修改超时时间等代码即可。

timer_setup(&power_timer, power_timer_handler, 0);
mod_timer(&power_timer, jiffies+HZ * TIMER_COUNT);

到这,就算设备没有电池,我也能写一个电池驱动出来!就这么霸道,快去试试看。

热心读者疑问解答

为什么需要ec芯片呢?不能做一个纯软件实现的吗?

先说结论:当然可以做,但不推荐做。对于ec芯片来说,他需要做的就是传递电池信息,这个信息包括:电池是否在线、电压值、电流值、容量温度事件等等,这些值难道不能模拟吗?当然不是,只要经过多次实验,得到一个放电曲线和充电曲线,在充电放电途中电压、电流、容量等值都可以通过计算得来。至于电池是否在线,可以写一个历程轮询查看,虽然代价有点大,也算是可以做。
为什么不推荐做呢?原因有下面几条:

  • 计算状态不准确。电池是个很精密的外设,想要仅仅通过数学计算得到每一个准确的状态过于困难。
  • 内核无法做硬件排障。通过计算得到一个相对靠谱的电池信息,都是建立在电池工作正常的情况下,万一发生电池过压、过热、过流等硬件故障呢?内核要怎么知道呢?电池温度如果超过80度,笔记本就很危险了。
  • 电池在线轮询例程是一个不小的软件开销,如果这个工作放在硬件来做,也是个性能的提升不是。

欢迎大家踊跃提问,一起交流!

这篇关于linux EC驱动书写指南的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/564056

相关文章

Java中String字符串使用避坑指南

《Java中String字符串使用避坑指南》Java中的String字符串是我们日常编程中用得最多的类之一,看似简单的String使用,却隐藏着不少“坑”,如果不注意,可能会导致性能问题、意外的错误容... 目录8个避坑点如下:1. 字符串的不可变性:每次修改都创建新对象2. 使用 == 比较字符串,陷阱满

Linux使用nload监控网络流量的方法

《Linux使用nload监控网络流量的方法》Linux中的nload命令是一个用于实时监控网络流量的工具,它提供了传入和传出流量的可视化表示,帮助用户一目了然地了解网络活动,本文给大家介绍了Linu... 目录简介安装示例用法基础用法指定网络接口限制显示特定流量类型指定刷新率设置流量速率的显示单位监控多个

ElasticSearch+Kibana通过Docker部署到Linux服务器中操作方法

《ElasticSearch+Kibana通过Docker部署到Linux服务器中操作方法》本文介绍了Elasticsearch的基本概念,包括文档和字段、索引和映射,还详细描述了如何通过Docker... 目录1、ElasticSearch概念2、ElasticSearch、Kibana和IK分词器部署

python使用fastapi实现多语言国际化的操作指南

《python使用fastapi实现多语言国际化的操作指南》本文介绍了使用Python和FastAPI实现多语言国际化的操作指南,包括多语言架构技术栈、翻译管理、前端本地化、语言切换机制以及常见陷阱和... 目录多语言国际化实现指南项目多语言架构技术栈目录结构翻译工作流1. 翻译数据存储2. 翻译生成脚本

Linux流媒体服务器部署流程

《Linux流媒体服务器部署流程》文章详细介绍了流媒体服务器的部署步骤,包括更新系统、安装依赖组件、编译安装Nginx和RTMP模块、配置Nginx和FFmpeg,以及测试流媒体服务器的搭建... 目录流媒体服务器部署部署安装1.更新系统2.安装依赖组件3.解压4.编译安装(添加RTMP和openssl模块

linux下多个硬盘划分到同一挂载点问题

《linux下多个硬盘划分到同一挂载点问题》在Linux系统中,将多个硬盘划分到同一挂载点需要通过逻辑卷管理(LVM)来实现,首先,需要将物理存储设备(如硬盘分区)创建为物理卷,然后,将这些物理卷组成... 目录linux下多个硬盘划分到同一挂载点需要明确的几个概念硬盘插上默认的是非lvm总结Linux下多

使用 sql-research-assistant进行 SQL 数据库研究的实战指南(代码实现演示)

《使用sql-research-assistant进行SQL数据库研究的实战指南(代码实现演示)》本文介绍了sql-research-assistant工具,该工具基于LangChain框架,集... 目录技术背景介绍核心原理解析代码实现演示安装和配置项目集成LangSmith 配置(可选)启动服务应用场景

linux进程D状态的解决思路分享

《linux进程D状态的解决思路分享》在Linux系统中,进程在内核模式下等待I/O完成时会进入不间断睡眠状态(D状态),这种状态下,进程无法通过普通方式被杀死,本文通过实验模拟了这种状态,并分析了如... 目录1. 问题描述2. 问题分析3. 实验模拟3.1 使用losetup创建一个卷作为pv的磁盘3.

SQL Server数据库迁移到MySQL的完整指南

《SQLServer数据库迁移到MySQL的完整指南》在企业应用开发中,数据库迁移是一个常见的需求,随着业务的发展,企业可能会从SQLServer转向MySQL,原因可能是成本、性能、跨平台兼容性等... 目录一、迁移前的准备工作1.1 确定迁移范围1.2 评估兼容性1.3 备份数据二、迁移工具的选择2.1

Linux环境变量&&进程地址空间详解

《Linux环境变量&&进程地址空间详解》本文介绍了Linux环境变量、命令行参数、进程地址空间以及Linux内核进程调度队列的相关知识,环境变量是系统运行环境的参数,命令行参数用于传递给程序的参数,... 目录一、初步认识环境变量1.1常见的环境变量1.2环境变量的基本概念二、命令行参数2.1通过命令编程