IcingTomato's Archive Enjoy it while it lasts, because it never does.

【Calc】CASIO Graph Light Lycée 法版 中文翻译

主菜单 Calcul 计算 Stats 统计 Probabilité 概率 Tableur 表格 Graphe 图形 Tabl fonct 函数表 Équation 方程 Complexe 复数 Vecteurs 向量 Prod. crolx 叉积 Math Box 数学盒 Config 配置 Paramètre calcul 计算参数 Saisie/Résultat 输入/结果 Smaths/Rmaths 自然显示/包含分数显示 Smaths/Rdéc 自然显示/转换成科学计数法或小数显示 Sligne/Rligne 线性显示/科学计数法或分数显示 Sligne/Rdéc 线性显示/转换成科学计数法显示 Unité d’angle 角度单位 Degré 度 Radian 弧度 Grade 渐度 Arrondi 取整 Fix 保留位数 Sci 科学计数法 有效数字 Norm 1: 科学计数法; 2: 小数 Forme complexe 复数形式 a+bi a+bi r∠θ r∠θ Sépart chiffers 数字分隔符 Séparat Lecture? 读数分隔符 Activé 启用 Désactivé 禁用 Puissance de 10 10的幂 ×10□ ×10□ ×10 ×10 Paramètre systèm 系统参数 Contraste 对比度 Clair 明亮 Sombre 暗色 Extinct auto 自动灭屏 10 min 10分钟 60 min 60分钟 Pol multiligne 多行显示 Police normale 正常字体 Petite police 小字体 QR Code 二维码 Version 3 版本3 Version 11 版本11 Réinitialiser 重置 Config & données 配置和数据 Réinitialis OK? Config & données 重置确定? Oui 是 Annuler 取消 Mém variables 内存变量 Réinitialis OK? Mém variables 重置确定? Oui 是 Annuler 取消 Tout 全部 Réinitialis OK? Tout 重置确定? Oui 是 Annuler 取消 Mode d’emploi 使用说明 Calcul 计算 Outils 工具 Annuler 取消 Simplifier 简化 Automatique 自动 Manuel 手动 Vérification 检查 Catalog 目录 Analyse fonction 解析函数 Nb dérivé(d/dx) 导数 Intégrale 积分 Somme 求和 Reste 余数 Simplification 简化 Logarithme base a 对数 Logarithme(log) 对数 Logarithme(ln) 自然对数 Probabilité 概率 % Factorielle 阶乘 Permutation 排列 Combinaison 组合 Nombre aléatoire 随机数 Entier aléatoire 随机整数 Calcul numérique 数值计算 PGCD 最大公约数 PPCM 最小公倍数 Valeur absolue 绝对值 Tronc. à l’unité 截断到整数 Arrondi 到整数 Partie entière 整数部分 Arrondi(,) 四舍五入 Angl/Coord/Sexag 角度/坐标/六十进制 Degrés 度 Radians 弧度 Gradians 渐度 Cartésien. à pol. 笛卡尔坐标系 Pol. à cartésien. 极坐标系 Degrés min sec 度分秒 Trigonmétrique 三角函数 sin 正弦 cos 余弦 tan 正切 sin⁻¹ 反正弦 cos⁻¹ 反余弦 tan⁻¹ 反正切 Conversion unité 单位转换 Vitesse 速度 km/h→m/s km/h→m/s m/s→km/h m/s→km/h Température 温度 Celsius→Fahrenheit 摄氏→华氏 Fahrenheit→Celsius 华氏→摄氏 Autre 其他 Rép 重复 Pré-Rép 预设重复 pi 圆周率 e 自然对数底 e^ 自然对数底的幂 √ 平方根 𝒙√ 𝒙次方根 ^-1 反函数 ^2 平方 ^() 幂 - 负号 , 小数点 () 括号 Stats 统计 1 variable 一个变量 2 variables 两个变量 Probabilités 概率 Binomiale P(X=) 二项分布 P(X=) Liste de valeurs 值列表 Valeur unique 单值 Poisson P(X≥) 泊松分布 P(X≥) Liste de valeurs 值列表 Valeur unique 单值 Binomiale P(X≤) 二项分布 P(X≤) Densité Normale 正态分布密度 Normale P(≤X≤) 正态分布 P(≤X≤) Inverse normale 正态分布反函数 Poisson P(X=) 泊松分布 P(X=) Liste de valeurs 值列表 Valeur unique 单值 Poisson P(X≤) 泊松分布 P(X≤) Liste de valeurs 值列表 Valeur unique 单值 Tableur 表格 Outils 工具 Remplir formule 填充公式 Formul= 公式 Plage 范围 Confirmer 确认 Remplir valeur 填充值 Valeur 值 Plage 范围 Confirmer 确认 Modifier cell 修改单元格 Espace libre 空间 2380 octets libre 2380字节可用 Couper & Coller 剪切和粘贴 Copier & Coller 复制和粘贴 Tout supprimer 删除所有内容 Recalculer 重新计算 Calcul auto 自动计算 Activé 启用 Désactivé 禁用 Afficher cell 显示单元格 Formule 公式 Valeur 值 Graphe 图形 f(x) 函数图像 Modifier 修改 Afficher/Masquer 显示/隐藏 Afficher 显示 Masquer 隐藏 Type de ligne 线条类型 g(x) 函数图像 Tracer 绘制 Fenêtre graphique 图形窗口 Pré-enregistrées 预设 Orthonormé 正交标准 Trigonométrique 三角函数 Standard(-10;10) 标准(-10;10) 𝒙min 𝒙最小值 max 最大值 grad. 刻度 𝒚min 𝒚最小值 max 最大值 grad. 刻度 Résolution graph 图形分辨率 Racines 根 Maximum local 局部最大值 Minimum local 局部最小值 Intersection 交点 Ordonnée origine 原点纵坐标 𝒚(image) 𝒚(因变量) 𝒙(antécédent) 𝒙(自变量) Parcourir courbe 遍历曲线 Zoom 缩放 Zoom automatique 自动缩放 Zoom avant 向前缩放 Zoom arrière 向后缩放 Facteur de zoom 缩放因子 2× 2× 3× 3× 10× 10× Placer un point 放置一个点 Tracer tangente 绘制切线 Nb dérivé 导数个数 Activé 启用 Désactivé 禁用 Affich. Fonction 函数显示 Activé 启用 Désactivé 禁用 Arrière-plan 背景 Label 标签 Activé 启用 Désactivé 禁用 Grille 网格 Activé 启用 Désactivé 禁用 Ligne 线 Axes 轴 Activé 启用 Désactivé 禁用 Outils 工具 Fenêtre graphique 图形窗口 Pré-enregistrées 预设 Orthonormé 正交标准 Trigonométrique 三角函数 Standard(-10;10) 标准(-10;10) 𝒙min 𝒙最小值 max 最大值 grad. 刻度 point 点 𝒚min 𝒚最小值 max 最大值 grad. 刻度 Nb dérivé 导数个数 Activé 启用 Désactivé 禁用 Affich. Fonction 函数显示 Activé 启用 Désactivé 禁用 Arrière-plan 背景 Label 标签 Activé 启用 Désactivé 禁用 Grille 网格 Activé 启用 Désactivé 禁用 Ligne 线 Axes 轴 Activé 启用 Désactivé 禁用 Tabl fonct 函数表 f(x)/g(x): Vide 函数表: 空 Outils 工具 Plage du tableau 表格范围 Défin f(x)/g(x) 定义f(x)/g(x) Défin f(x) Défin g(x) Type de tableau 表格类型 f(x)/g(x) 函数表 f(x) 函数表 g(x) 函数表 Éditer 编辑 Insérer ligne 插入行 Tout supprimer 删除所有内容 Recalculer 重新计算 Vérification 检查 Équation 方程 Syst équations 方程组 2 inconnues 2个未知数 3 inconnues 3个未知数 4 inconnues 4个未知数 Polynomiale 多项式 a(x²)+bx+c a(x²)+bx+c a(x³)+bx²+cx+d a(x³)+bx²+cx+d a(x⁴)+bx³+cx²+dx+e a(x⁴)+bx³+cx²+dx+e Outils 工具 Racine complexe 复根 Activé 启用 Désactivé 禁用 Vérification 检查 Solveur 求解 Saisir l’équation 输入方程 Outils 工具 Annuler 取消 Complexe 复数 Outils 工具 Annuler 取消 Vérification 检查 Vecteurs 向量 [OUTILS] pour définir un vecteur [工具] 定义向量 Outils 工具 VctA: Vide 向量A: 空 Prod. crolx 叉积 A/B=X/D 向量A/B=X/D A/B=C/X 向量A/B=C/X Math Box 数学盒 Lancer de dés 掷骰子 Dés 骰子 1 dé 1个骰子 2 dés 2个骰子 3 dés 3个骰子 Essais 尝试 :5 5次 Confirmer 确认 Même résult 相同结果 Non 同结果 #1 #1 #2 #2 #3 #3 Exécuter 执行 Pile ou face 正反面 Pièces 硬币 1 pièce 1个硬币 2 pièces 2个硬币 3 pièces 3个硬币 Essais 尝试 :5 5次 Confirmer 确认 Même résult 相同结果 Non 同结果 #1 #1 #2 #2 Exécuter 执行 Droite grad. 不等式区间 A: 区间A 𝒙<𝒂 𝒙≤𝒂 𝒙=𝒂 𝒙>𝒂 𝒙≥𝒂 𝒂<𝒙<𝒃 𝒂≤𝒙<𝒃 𝒂<𝒙≤𝒃 𝒂≤𝒙≤𝒃 B: 区间B C: 区间C Exécuter 执行 Cercle 圆 Type: CercleTrigo 三角函数圆 CercleTrigo 三角函数圆 Demi-cercle 半圆 Horloge 时钟 θ1: θ1 θ2: θ2 Exécuter 执行

【老物新用】Mojo V3 FPGA 在 Ubuntu 上的开发环境搭建

今天翻出一个12年前老物件(虽说是12年前,其实是两三年前买的),是一款 FPGA 开发板,还记得自己当时因为修这个开发板,使用烙铁风枪的水平突飞猛进。 这款板子还是比较坑的,DC接口居然只能接5V,当时公司里面只有9-12V的适配器,插上去就一股电子元器件的清香。拿万用表打了一下,好家伙直接一带一路,烧掉一个稳压(AMS1117)、两个LED、一个SPI Flash、一个FPGA芯片(XC6SLX9)。很感谢当时在M5的同事,帮我找到了这么多坏的元器件,不然我还以为只是一个稳压坏了。 这老物件,生产它的公司 Embedded Micro已经无了(貌似是转生成Alchitry了,有机会把板子买过来试试),官网也无了,这些资料也只能在Internet Archive上找到,还是很感谢这个网站的存在,已经捐了10刀作为感谢,希望这个网站能继续运营下去。 捐赠照片 对我来说,FPGA(现场可编程逻辑门阵列)一直是电气工程设计中排行老二的领域(老大是制造芯片,那个我还远远做不到),在高中的时候就开始接触,直到大学的时候第一次去Seeed Studio实习,跟同事们聊起FPGA时,我们一致认为FPGA是一个很有意思的领域,但是门槛太高,学习成本太高: —— “我搞过FPGA” —— “歪日,牛的,兄弟” —— “这碉板子贵的一批” —— “多少钱” —— “便宜的都得好几百” —— “……我还是琢磨公司的XIAO吧” 这玩意儿不是简单地修改代码就能运行起来的,不仅仅是“玩”,FPGA 使用自己的语言(通常是 Verilog 或 VHDL)进行编程,使用基于同步硬件的技术,并使用极其复杂的软件开发。 Mojo V3 FPGA 开发板概况 1 个 Xilinx Spartan6 FPGA 8 个可编程的 LED 84 个 I/O 引脚。 1 个 Atmel AVR 32U4 MCU。 1 个 flash memory chip 用于存储 FPGA 配置数据 XC6SLX9 FPGA 是开发板的核心,它的内部有: 9,152 个逻辑块 16 个 DSP 切片 576 KB RAM 102 个 I/O 引脚 我估摸着这应该是世界上第一个把FPGA和MCU结合在一起的开发板,之后的都是什么ARM+FPGA、ESP32+FPGA的设计,也都是参照这个设计的,让MCU可以把比特流文件从SPI Flash中烧录到FPGA里面,就不需要额外的 JTAG 线了。(赛灵思一个原厂JTAG实在是贵的不行,就相当于买一个FPGA一样,仿制的也贵的离谱) 我也想借着闲赋在家的时候,整理整理这个FPGA的基础使用方法。板子的环境搭建我就放在【老物新用】这个栏目里面,具体的知识我再另外开设一个栏目。 Xilinx ISE WebPACK 安装 AMD FPGA ISE Archive 这个软件是 Xilinx 早期的 FPGA 开发软件 ISE,现在已经不再维护了。也是神奇,之前接触FPGA还是赛灵思,转眼就被按摩店收购了。收购赛灵思之后感觉按摩店都起飞了,估计是用了不少赛灵思的IP核。 进入到这个网页,找到14.7(14.7的Win10版本就是VirtualBox虚拟机),ISE Design Suite - 14.7 Full Product Installation,Full Installer for Linux (TAR/GZIP - 6.09 GB) MD5 SUM Value : e8065b2ffb411bb74ae32efa475f9817,点击该选项,跳转到AMD登录界面,登录完成后即可下载。 下载完成后,解压到一个目录,然后进入到该目录,执行 ./xsetup,然后一路下一步,到选择产品的时候需要选择ISE WebPACK即可,等待安装完成。 注意需要添加License,这个License可以在Xilinx官网申请,也可以在网上找到,我这里就不提供了。 安装完之后,需要添加环境变量,打开 `/etc/environment` 文件,添加如下内容: ```shell # Xilinx ISE WebPACK . /opt/Xilinx/14.7/ISE_DS/settings64.sh >/dev/null 2>&1 ``` 其中 `. /opt/Xilinx/14.7/ISE_DS/settings64.sh` 是官方给的,安装完之后会提示你运行这个命令,这个命令会设置一些环境变量,然后就可以在终端中使用 `ise` 命令打开软件了。我就是图方便加到 `.bashrc` 里面的。但是每次开终端都会有运行结果显示,所以我加了 `>/dev/null 2>&1`。 至于为什么不加到 `/etc/environment` 和 `.bashrc` 文件里面,是因为加了之后不知道为啥会影响到其他软件的运行,导致其他软件无法正常运行 建议是使用shell脚本启动环境: # /opt/start_ise.sh #!/bin/bash # Xilinx ISE WebPACK . /opt/Xilinx/14.7/ISE_DS/settings64.sh >/dev/null 2>&1 ise & >/dev/null 2>&1 然后给这个脚本添加执行权限: sudo chmod +x /opt/start_ise.sh Mojo Loader 和 Mojo IDE 安装 IcingTomato/MojoFPGA - GitHub 这个是我以前下载的 Mojo Loader 和 Mojo IDE 的程序,上传到了 GitHub 上留作备份。这两个程序一个是用来烧录 FPGA 的,另一个是用来编写代码的。 将 mojo-ide-B1.3.6-linux64.tgz 和 mojo-loader-1.3.0-linux64.tgz 下载到本地,解压到一个目录,然后进入到该目录,执行 ./mojo-ide 和 ./mojo-loader 即可。 要注意的是,Mojo IDE 需要JDK8,如果没有安装的话,可以使用 sudo apt install openjdk-8-jdk 安装。 我这边为了方便,把这两个程序解压到了 /opt/ 目录下,然后创建软链接: sudo chmod +x /opt/mojo-ide-B1.3.6-linux64/mojo-ide sudo chmod +x /opt/mojo-loader-1.3.0-linux64/mojo-loader sudo ln -s /opt/mojo-ide-B1.3.6-linux64/mojo-ide /usr/bin/mojo-ide sudo ln -s /opt/mojo-loader-1.3.0-linux64/mojo-loader /usr/bin/mojo-loader 这样就可以在终端中使用 mojo-ide 和 mojo-loader 命令了。 Mojo Loader 使用 打开终端,输入 mojo-loader,然后选择你的 FPGA 开发板,然后选择你的比特流文件,然后点击 Erase,等待擦除完成后即可点击 Load 将bin文件加载到Flash。 Mojo Loader Mojo IDE 使用 建议是使用shell脚本启动环境: # /opt/start_mojo_ide.sh #!/bin/bash # Xilinx ISE WebPACK . /opt/Xilinx/14.7/ISE_DS/settings64.sh >/dev/null 2>&1 mojo_ide & >/dev/null 2>&1 然后给这个脚本添加执行权限: sudo chmod +x /opt/start_mojo_ide.sh Mojo IDE 补充记录 针对 ISE 和 Mojo IDE/Loader 在开始菜单的显示问题,我在后续使用的时候琢磨出来了,找到一个相对比较好的解决方案: ISE 配置 先安装完 ISE WebPACK,打开终端,输入: cd /usr/share/applications/ 然后创建一个 ise.desktop 文件: sudo vim ise.desktop 然后输入以下内容: [Desktop Entry] Version=1.0 Name=ISE Exec=bash -c "unset LANG && unset QT_PLUGIN_PATH && source /opt/Xilinx/14.7/ISE_DS/settings64.sh && ise" Icon=/opt/Xilinx/14.7/ISE_DS/ISE/data/images/pn-ise.png Terminal=false Type=Application Categories=Development; 保存退出。 Mojo IDE 配置 同样的,打开终端,输入: cd /opt/MojoFPGA/mojo-ide-B1.3.6/ sudo vim mojo-ide.sh 然后输入以下内容: #!/bin/bash # Xilinx ISE WebPACK . /opt/Xilinx/14.7/ISE_DS/settings64.sh >/dev/null 2>&1 exec /opt/MojoFPGA/mojo-ide-B1.3.6/mojo-ide & >/dev/null 2>&1 保存退出。 然后给这个脚本添加执行权限: sudo chmod +x /opt/MojoFPGA/mojo-ide-B1.3.6/mojo-ide.sh 然后添加软链接: sudo ln -s /opt/MojoFPGA/mojo-ide-B1.3.6/mojo-ide.sh /usr/sbin/mojo-ide 再回到 /usr/share/applications/ 目录,创建一个 mojo-ide.desktop 文件: sudo vim mojo-ide.desktop 然后输入以下内容: [Desktop Entry] Version=1.0 Name=Mojo IDE Exec=/usr/sbin/mojo-ide Icon=/opt/MojoFPGA/mojo-ide-B1.3.6/icon.png Terminal=false Type=Application Categories=Development; 保存退出。 这样就可以在开始菜单中找到 ISE 和 Mojo IDE 了。 Mojo Loader 配置 先创建软链接: sudo ln -s /opt/MojoFPGA/mojo-loader-1.3.0/mojo-loader /usr/sbin/mojo-loader 同样的,打开终端,输入: cd /usr/share/applications/ 然后创建一个 mojo-loader.desktop 文件: [Desktop Entry] Version=1.0 Name=Mojo Loader Exec=/usr/sbin/mojo-loader Icon=/opt/MojoFPGA/mojo-loader-1.3.0/icon.png Terminal=false Type=Application Categories=Development; 保存退出。 这样就可以在开始菜单中找到 Mojo Loader 了。 Desktop Shortcut

【家庭网络改造】QNAP NAS计划任务自动删除米家摄像头三个月前的视频文件

去年8月29日在京东下单了一个QNAP威联通的TS-216,替换掉我家原来自己用树莓派3B+(Ubuntu Server + Samba)搭建的NAS。装了两个东芝P300 2TB的硬盘,开始组的RAID 1。后来发现家里面四五个摄像头每天的数据量大得很,没几天就把硬盘写满了。后面想想RAID 1不实在,家里没啥重要数据,今年回家的时候就格盘换成RAID 0了。但是米家摄像头数据还是很大,没几个月3.6TB就快写满了。 于是在米家APP中找到了视频存储时长设置选项,改成3个月: 米家APP中视频存储时长设置选项 改了之后NAS上面数据一点没动,应该是不会删除NAS上面的视频数据,所以就想着让NAS怎么自动删除米家摄像头三个月前的视频文件。 (以下所有操作需提前在NAS中开启SSH服务) 首先想到的是用crontab,但是QNAP的Linux系统是基于BusyBox的,crontab -e 这种命令有,但是不会运行成功,即便是加上 sudo ,也不能保存计划任务。 所以只能使用 vi 命令编辑计划任务文件,文件路径是 /etc/config/crontab ,在文件末尾加上: 0 * * * * cd /share/MiCam/xiaomi_camera_videos && bash auto_del.sh 这个命令的意思是每小时执行一次 auto_del.sh 脚本,脚本内容如下: #!/bin/bash # Set the base directory where random directories are located base_directory="./" # Calculate the date threshold for deletion (3 months ago) date_threshold=$(date --date='-3 months' +'%Y%m%d00') echo "Before $date_threshold will be deleted!" # Find all random directories and process them find "$base_directory" -maxdepth 1 -type d -name '[0-9a-f]*' | while read -r dir; do # Change directory to the random directory cd "$dir" || continue # Skip if directory change fails echo "Entry $dir." # Find and delete directories older than the threshold find . -maxdepth 1 -type d -name '??????????' | while read -r subdir; do if [[ "$(basename "$subdir")" -lt "$date_threshold" ]]; then echo "Deleting directory: $subdir" rm -rf "$subdir" # Remove the directory recursively fi done # Change back to the base directory cd .. done 手动运行结果如下: 因为米家摄像头会在设置的文件夹下创建 xiaomi_camera_videos 文件夹,然后在这个文件夹下以设备序列号(?)创建的很像乱码的文件夹,每个乱码文件夹下面又以年月日时为名的文件夹,所以这个脚本的作用是删除 xiaomi_camera_videos/?????????? 文件夹下三个月前的视频文件夹。所以脚本必须放在 /目标文件夹/xiaomi_camera_videos下:

【网络基础】什么是NAT?

NAT是一种地址转换技术,它可以将IP数据报文头中的IP地址转换为另一个IP地址,并通过转换端口号达到地址重用的目的。NAT作为一种缓解IPv4公网地址枯竭的过渡技术,由于实现简单,得到了广泛应用。 1 NAT解决了什么问题?  2 NAT的类型  3 NAT是如何工作的?  4 如何使用NAT?  NAT解决了什么问题? 随着网络应用的增多,IPv4地址枯竭的问题越来越严重。尽管IPv6可以从根本上解决IPv4地址空间不足问题,但目前众多网络设备和网络应用大多是基于IPv4的,因此在IPv6广泛应用之前,使用一些过渡技术(如CIDR、私网地址等)是解决这个问题的主要方式,NAT就是这众多过渡技术中的一种。 当私网用户访问公网的报文到达网关设备后,如果网关设备上部署了NAT功能,设备会将收到的IP数据报文头中的IP地址转换为另一个IP地址,端口号转换为另一个端口号之后转发给公网。在这个过程中,设备可以用同一个公网地址来转换多个私网用户发过来的报文,并通过端口号来区分不同的私网用户,从而达到地址复用的目的。 早期的NAT是指Basic NAT,Basic NAT在技术上实现比较简单,只支持地址转换,不支持端口转换。因此,Basic NAT只能解决私网主机访问公网问题,无法解决IPv4地址短缺问题。后期的NAT主要是指网络地址端口转换NAPT(Network Address Port Translation),NAPT既支持地址转换也支持端口转换,允许多台私网主机共享一个公网IP地址访问公网,因此NAPT才可以真正改善IP地址短缺问题。 NAT的类型 根据NAT转换是对报文中的源地址进行转换还是对目的地址进行转换,NAT可以分为源NAT、目的NAT和双向NAT,下面我们分别介绍这三种NAT类型。 SNAT(源NAT) 源NAT在NAT转换时,仅对报文中的源地址进行转换,主要应用于私网用户访问公网的场景。当私网用户主机访问Internet时,私网用户主机发送的报文到达NAT设备后,设备通过源NAT技术将报文中的私网IPv4地址转换成公网IPv4地址,从而使私网用户可以正常访问Internet。 根据转换时是否同时转换源端口号,源NAT可以细分为如下几种类型,详见下图。 源NAT分类 DNAT(目的NAT) 目的NAT在NAT转换时,仅对报文中的目的地址和目的端口号进行转换,主要应用于公网用户访问私网服务的场景。当公网用户主机发送的报文到达NAT设备后,设备通过目的NAT技术将报文中的公网IPv4地址转换成私网IPv4地址,从而使公网用户可以使用公网地址访问私网服务。 根据转换前后的地址是否存在一种固定的映射关系,目的NAT可以细分为如下几种类型,详见下图。 目的NAT分类 双向NAT 双向NAT指的是在转换过程中同时转换报文的源信息和目的信息。双向NAT不是一个单独的功能,而是源NAT和目的NAT的组合。双向NAT是针对同一条流,在其经过设备时同时转换报文的源地址和目的地址。双向NAT主要应用在同时有外网用户访问内部服务器和私网用户访问内部服务器的场景。 STUN中定义的NAT类型 在STUN标准中,根据私网IP地址和端口到NAT出口的公网IP地址和端口的映射方式,把NAT分为如下四种类型,详见下图。 STUN中定义的NAT类型 Full Cone NAT(完全锥型NAT) 所有从同一个私网IP地址和端口(IP1:Port1)发送过来的请求都会被映射成同一个公网IP地址和端口(IP:Port)。并且,任何外部主机通过向映射的公网IP地址和端口发送报文,都可以实现和内部主机进行通信。 这是一种比较宽松的策略,只要建立了私网IP地址和端口与公网IP地址和端口的映射关系,所有的Internet上的主机都可以访问该NAT之后的主机。 Restricted Cone NAT(限制锥型NAT) 所有从同一个私网IP地址和端口(IP1:Port1)发送过来的请求都会被映射成同一个公网IP和端口号(IP:Port)。与完全锥型NAT不同的是,当且仅当内部主机之前已经向公网主机发送过报文,此时公网主机才能向私网主机发送报文。 Port Restricted Cone NAT(端口限制锥型NAT) 与限制锥型NAT很相似,只不过它包括端口号。也就是说,一台公网主机(IP2:Port2)想给私网主机发送报文,必须是这台私网主机先前已经给这个IP地址和端口发送过报文。 Symmetric NAT(对称NAT) 所有从同一个私网IP地址和端口发送到一个特定的目的IP地址和端口的请求,都会被映射到同一个IP地址和端口。如果同一台主机使用相同的源地址和端口号发送报文,但是发往不同的目的地,NAT将会使用不同的映射。此外,只有收到数据的公网主机才可以反过来向私网主机发送报文。 这和端口限制锥型NAT不同,端口限制锥型NAT是所有请求映射到相同的公网IP地址和端口,而对称NAT是不同的请求有不同的映射。 NAT是如何工作的? 根据前面的分类,我们分别从源NAT和目的NAT中各选一种NAT为代表,介绍其工作原理。其他类型的NAT虽然在转换时,转换的内容有细微差别,但是工作原理都相似,不再重复介绍。此外,双向NAT是源NAT和目的NAT的组合,双向NAT的工作原理也不再重复介绍。 NAPT工作原理 NAPT在进行地址转换的同时还进行端口转换,可以实现多个私网用户共同使用一个公网IP地址上网。NAPT根据端口来区分不同用户,真正做到了地址复用。 NAPT工作原理示意图 当Host访问Web Server时,设备的处理过程如下: 设备收到Host发送的报文后查找NAT策略,发现需要对报文进行地址转换。 设备根据源IP Hash算法从NAT地址池中选择一个公网IP地址,替换报文的源IP地址,同时使用新的端口号替换报文的源端口号,并建立会话表,然后将报文发送至Internet。 设备收到Web Server响应Host的报文后,通过查找会话表匹配到步骤2中建立的表项,将报文的目的地址替换为Host的IP地址,将报文的目的端口号替换为原始的端口号,然后将报文发送至Intranet。 NAT Server工作原理 使用NAT Server时,需要先在设备上配置公网地址和私网地址的固定映射关系。配置完成后,设备将会生成Server-Map表项,存放公网地址和私网地址的映射关系。该表项将一直存在除非NAT Server的配置被删除。 NAT Server工作原理示意图 内部Server的私网IPv4地址为192.168.1.2/24,对外的公网IPv4地址为1.1.1.10,端口号都为80,它们之间的映射关系在设备上已提前配置好。当Host访问Server时,设备的处理过程如下: 设备收到Internet上用户访问1.1.1.10的报文的首包后,查找并匹配到Server-Map表项,将报文的目的IP地址转换为192.168.1.2。 设备建立会话表,然后将报文发送至Intranet。 设备收到Server响应Host的报文后,通过查找会话表匹配到步骤2中建立的表项,将报文的源地址替换为1.1.1.10,然后将报文发送至Internet。 后续Host继续发送给Server的报文,设备都会直接根据会话表项的记录对其进行转换,而不会再去查找Server-map表项。 如何使用NAT? 前面已经介绍了,不同的NAT类型适用于不同的应用场景。下面介绍几种典型的NAT应用,帮助用户使用NAT。 私网用户通过NAPT访问Internet 在许多小区、学校和企业的私网规划中,由于公网地址资源有限,通常给私网用户分配私网IPv4地址。此时,可以配置源NAT来实现私网用户访问Internet。用户可以根据自己拥有的公网IPv4地址的个数,选择使用NAPT或者Easy IP。 当用户拥有的公网IP地址个数较多时,配置了NAT设备出接口的IP地址和其他应用之后,还有可用的空闲公网IP地址时,可以选择NAPT。NAPT使用地址池内的IPv4地址作为私网主机转换后的公网IPv4地址。如下图所示,在设备上配置NAPT,实现私网主机访问Internet功能。 私网用户通过NAPT访问Internet 私网用户通过Easy IP访问Internet 当用户拥有的公网IPv4地址个数较少时,配置了NAT设备出接口的IPv4地址和其他应用之后,没有可用的空闲公网IPv4地址时,可以选择Easy IP。Easy IP使用出接口的IPv4地址作为私网主机转换后的公网IPv4地址。如下图所示,在设备上配置Easy IP,实现私网主机访问Internet功能。 私网用户通过Easy IP访问Internet 公网用户通过NAT Server访问私网服务器 在某些场合,私网中有一些服务器需要向公网用户提供服务,比如私网中部署的一些Web服务器、FTP服务器等,NAT支持这样的应用,此时可以配置NAT Server来实现公网用户访问私网服务器。如下图所示,在设备上配置NAT Server,固定“公网IP地址+端口号”与“私网IP地址+端口号”间的映射关系,实现公网主机通过该映射关系访问私网服务器功能。 公网用户通过NAT Server访问私网服务器

【WSFC 学习笔记】如何理解 Windows Server 故障转移群集 的 Quorum

前言 最近一个月一直在学高可用的东西,其中 WSFC 是其中一个重要的组成部分。在学习 WSFC 的过程中,我发现了一个很重要的概念:Quorum,这个概念网上一找全是照着微软官方文档写的 仲裁配置选项。问有什么模型的时候头头是道,什么节点多数无见证啊、仅磁盘见证啊什么的,一问啥是仲裁(Quorum)和见证(Witness)的时候傻眼了。所以我决定写一篇文章来总结一下我对 Quorum 的理解。 仲裁(Quorum)是什么 仲裁(Quorum)是 WSFC 中的一个重要概念,它是用来保证群集中的节点之间能够达成一致的一个机制。在 WSFC 中,仲裁是通过仲裁资源来实现的,这个资源可以是磁盘、文件共享、或者是其他的仲裁资源。在 WSFC 中,仲裁资源的作用是用来保证群集中的节点之间能够达成一致,从而保证群集的正常运行。 依我看,Quorum 其实应该翻译成 有效参与仲裁的设备(数),因为 Quorum 本来就翻译成 法定人数/出席会议最小人数,不应该直接叫 仲裁。 现在涉及到 仲裁 这个法律概念,所以我们可以引入法律属于去解释。在法律中,仲裁 是指一种解决纠纷的方式,当纠纷发生时,双方可以通过仲裁的方式来解决纠纷。在法律仲裁中,仲裁员是一个独立的第三方,他们会根据法律和事实来做出裁决。但在 WSFC 中,仲裁员(节点/服务器/虚拟机)既是当事人(所有节点),又参与仲裁庭,它们会根据法律和事实(仲裁模型)来做出裁决,是用来保证群集中的节点之间能够达成一致的。 节点多数,无见证:由节点组织“合议制仲裁庭”,当事人(所有参与群集的节点/服务器)约定由奇数名仲裁员(节点)组成仲裁庭(偶数节点就使其中一个节点下线),其中一名为首席仲裁员(主节点/服务器);如果是双节点就采取“独任制仲裁庭”,只推举一名仲裁员(一个节点做主服务器,另外一个下线)。 磁盘、文件共享见证:见证人是证人之外知道案件情况的当事人(所以磁盘、文件共享见证需要对所有节点可见,在注册表中节点文件是Cluster,见证文件是0.Cluster,可以证明见证确实是“当事人”)。见证人不参与仲裁庭,但是可以提供证据(见证文件)。 总结来说: 节点多数,无见证: 合议庭:集群的所有活动节点。 仲裁庭成员:每个节点都可以投票决定集群状态,若节点总数为偶数,可能需要让一个节点下线以避免平票。 首席仲裁员(主节点):在实际的WSFC中,通常没有固定的“首席仲裁员”或主节点,所有节点理论上是平等的,但在实践中,某些节点可能因为资源位置或网络优势暂时承担更多责任。 双节点集群的“独任制”: 这种情况下,通常需要额外的见证(磁盘或文件共享见证),因为单纯的两个节点在一个节点失效时无法决定集群状态。如果不使用见证,确实可能会推举一个节点为主导,另一个则在主节点活跃时处于待命状态。 磁盘、文件共享见证: 见证人:在这种情况下,见证(文件共享或磁盘)充当了“知情人”,它存储关于集群配置的关键信息,确保在节点间的意见不一致时提供“证据”来帮助做出决策。见证的存在特别在节点数为偶数时非常关键,以避免平票问题。 但是大多数人对法律仲裁制度不了解,而且法律仲裁制度和 WSFC 中的仲裁机制有很大的不同,所以我觉得这个比喻不太合适。 所以我决定引入日常生活,比如双节点集群就像情侣之间决定晚餐吃什么,引入磁盘见证相当于三口之家中孩子的存在,这样比喻起来更加贴近生活,也更容易理解。 原文如下: Mode Description Node majority (no witness) 节点多数 Only nodes have votes. No quorum witness is configured. The cluster quorum is the majority of voting nodes in the active cluster membership. Node majority with witness (disk or file share) 节点多数+见证 Nodes have votes. In addition, a quorum witness has a vote. The cluster quorum is the majority of voting nodes in the active cluster membership plus a witness vote. A quorum witness can be a designated disk witness or a designated file share witness. No majority (disk witness only) 仅磁盘见证 No nodes have votes. Only a disk witness has a vote. The cluster quorum is determined by the state of the disk witness. Generally, this mode is not recommended, and it should not be selected because it creates a single point of failure for the cluster. 我们可以这样理解: 仲裁模型 描述 男生女生二人决定晚餐 节点多数 【双节点】男生让渡(即其中一节点下线,有一票投票权但动态见证干预其不投票),让女生(主节点/服务器/虚拟机 Owner Node)来选择点什么外卖或者出去吃什么餐馆。你就负责买单就行。 【三节点及以上】女生带闺蜜来了,女生和闺蜜手拉手胳膊挽胳膊掌握晚餐选择主动权(即具有集群的控制权,在一个三节点集群中,通常需要至少两个节点在线并相互通信,以维持集群的正常操作)。象征性问你一下想吃什么。所以你还是负责买单的小丑。 核心家庭(三口之家)或扩展家庭(爸妈或者和爷爷奶奶外公外婆一起为节点,孩子为见证) 节点多数+见证 爸妈想吃啥就做啥,你的意见仅供参考 因为爷爷奶奶外公外婆说孩子想吃这个那个,所以爸妈做饭就做了大家都爱吃的东西。(换句话说见证也参与,而不仅仅是节点多数) 你过生日那天 仅磁盘见证 “妈,我生日想吃开封菜。” “不行,油炸食品不健康。” (别看上面的见证是你,但是这一条就不是你了) 总结 给我写乐了。宿舍停水了,舍友用我的博客洗完了澡。

【阅板无数 之 Intel x86 IoT系列】Intel Arduino/Genuino 101 简单测评

注:下文中 Intel Arduino/Genuino 101 简称为 Arduino 101 Arduino 101 Arduino/Genuino 101 的由来 2015年10月16日1,距离世界上第一台商用可编程计算器 Olivetti Programma 1012 问世过去了整整50年。为了致敬这个伟大的项目,英特尔和 Massimo Banzi3(Arduino项目的联合创始人)在 Maker Faire 宣布推出一款新的开发板,名为Arduino/Genuino 101 —— 一款专为教育用途、创客世界和首次接触编程的人设计的开发板。 Olivetti Programma 101 Arduino/Genuino 101 的硬件和软件 Arduino 101是一款学习和开发板,以入门级价格提供英特尔® Curie™ 模块的性能和低功耗以及 Arduino 的简单性。它保留了与 UNO 相同的强大外形和外设列表,并增加了板载低功耗蓝牙®(Bluetooth Low Energy,BLE,Nordic 的 nRF512822)功能和 6 轴加速度计/陀螺仪(Bosch 的 BMI160)。 Arduino 101 使用的 Curie™ 模块属于异构双核,包含两个微型内核,一个 x86 (Quark SE,SE 即 Second Edition) 和一个 32 位 ARC 架构内核,时钟频率均为 32MHz。英特尔工具链可在两个内核上以最佳方式编译 Arduino 草图,以完成要求最苛刻的任务。英特尔开发的实时操作系统 (Zephyr) 和框架是开源的。其中,ARC架构是一种32位的RISC处理器架构,由 Synopsys(新思科技)开发。 101 带有 14 个数字输入/输出引脚(其中 4 个可用作 PWM 输出)、6 个模拟输入、一个用于串行通信和草图上传的 USB 连接器、一个电源插孔、一个带 SPI 信号的 ICSP 接头和 I2C 专用引脚。电路板工作电压和 I/O 为 3.3V,但所有引脚均具有 5V 过压保护。 相较于标准 Arduino Uno, Arduino 101 采用的异构双核的 Curie™ 模块比 8位的 Atmel 328p 微控制器更强大,存储空间也更大(Intel 官方的 Curie 规格是 384KB Flash 与 80KB SRAM ,但相关报导写 196KB Flash 与 24KB RAM,Arduino 官网写 384KB Flash 与 80KB SRAM,但 SRAM 部分注明只有 24KB 可供应用程序 Sketch 使用。因此,推估之所以写 196KB Flash,应该也是系统占据一部分,真正可供运用的是 196KB。除此之外电路板上似乎又增设2MB Flash 可供 Curie 使用) 在2016年4月21日,英特尔发布 Arduino 101 固件源代码。包含用于 101 上 Curie 处理器的完整 BSP(板级支持包)。它允许您编译和修改核心操作系统和固件,以管理更新和引导加载程序。固件在 Curie 模块内的 x86 芯片上运行,并使用 回调 与 ARC 内核(运行 Arduino 程序)进行通信。x86 内核负责处理低功耗蓝牙® (BLE) 和 USB 通信,从而减轻 ARC 内核的负担。4 技术规格5 Microcontroller Intel Curie Operating Voltage 3.3V (5V tolerant I/O) Input Voltage (recommended) 7-12V Input Voltage (limit) 7-17V Digital I/O Pins 14 (of which 4 provide PWM output) PWM Digital I/O Pins 4 Analog Input Pins 6 DC Current per I/O Pin 20 mA Flash Memory 196 kB SRAM 24 kB Clock Speed 32MHz LED_BUILTIN 13 Features Bluetooth® Low Energy, 6-axis accelerometer/gyro Length 68.6 mm Width 53.4 mm Weight 34 gr. Arduino 代码实现 Blink #include <Arduino.h> void setup() { Serial.begin(9600); while (!Serial) { delay(10); // wait for serial port to connect. Needed for native USB port only } pinMode(LED_BUILTIN, OUTPUT); } void loop() { digitalWrite(LED_BUILTIN, HIGH); Serial.println("LED ON!"); delay(1000); digitalWrite(LED_BUILTIN, LOW); Serial.println("LED OFF!"); delay(1000); } 读取板载 IMU #include <Arduino.h> #include "CurieIMU.h" void setup() { Serial.begin(9600); while (!Serial) { delay(10); } // Start the acceleromter CurieIMU.begin(); // Set the accelerometer range to 2G CurieIMU.setAccelerometerRange(2); } void loop() { // read accelerometer: int x = CurieIMU.readAccelerometer(X_AXIS); int y = CurieIMU.readAccelerometer(Y_AXIS); int z = CurieIMU.readAccelerometer(Z_AXIS); Serial.print("x: "); Serial.print(x); Serial.print(" y: "); Serial.print(y); Serial.print(" z: "); Serial.print(z); Serial.println(""); } 读取板载 RTC #include <Arduino.h> #include <CurieTime.h> void setup() { Serial.begin(9600); while (!Serial) { delay(10); } setTime(1, 23, 24, 25, 1, 2024); } void loop() { //create a character array of 16 characters for the time char clockTime[16]; //use sprintf to create a time string of the hour, minte and seconds sprintf(clockTime, "%2d:%2d:%2d", hour(), minute(), second()); //create a character array of 15 characters for the date char dateTime[16]; //use sprintf to create a date string from month, day and year sprintf(dateTime, "%2d/%2d/%4d", month(), day(), year()); //print the time and date to the serial monitor Serial.print(clockTime); Serial.println(dateTime); delay(1000); } 使用板载 BLE 控制 LED 需要配合Nordic nRF Connect使用,可以在 Google Play Store 或 App Store 下载 #include <Arduino.h> #include <CurieBLE.h> BLEService ledService("19B10000-E8F2-537E-4F6C-D104768A1214"); // BLE LED Service // BLE LED Switch Characteristic - custom 128-bit UUID, read and writable by central BLEUnsignedCharCharacteristic switchCharacteristic("19B10001-E8F2-537E-4F6C-D104768A1214", BLERead | BLEWrite); const int ledPin = 13; // pin to use for the LED void setup() { Serial.begin(9600); // set LED pin to output mode pinMode(ledPin, OUTPUT); // begin initialization BLE.begin(); // set advertised local name and service UUID: BLE.setLocalName("Arduino 101"); BLE.setAdvertisedService(ledService); // add the characteristic to the service ledService.addCharacteristic(switchCharacteristic); // add service BLE.addService(ledService); // set the initial value for the characeristic: switchCharacteristic.setValue(0); // start advertising BLE.advertise(); Serial.println("BLE LED Peripheral"); } void loop() { // listen for BLE peripherals to connect: BLEDevice central = BLE.central(); // if a central is connected to peripheral: if (central) { Serial.print("Connected to central: "); // print the central's MAC address: Serial.println(central.address()); // while the central is still connected to peripheral: while (central.connected()) { // if the remote device wrote to the characteristic, // use the value to control the LED: if (switchCharacteristic.written()) { if (switchCharacteristic.value()) { // any value other than 0 Serial.println("LED on"); digitalWrite(ledPin, HIGH); // will turn the LED on } else { // a 0 value Serial.println(F("LED off")); digitalWrite(ledPin, LOW); // will turn the LED off } } } // when the central disconnects, print it out: Serial.print(F("Disconnected from central: ")); Serial.println(central.address()); } } 后记 Arduino 101 于 2016年 Q1 季度发布。仅仅过去一年有余,在英特尔宣布 Galileo,Edison 和 Joule 模块停产一个月后,也草草停产 Curie 模块(07/17/2017)6,只能说是非常可惜,英特尔宏图壮志准备在 IoT 领域大展拳脚,但是却因为种种原因,最终只能黯然收场。也导致诸多使用 Curie 模块的企业被迫更换产品线,比如小米智能跑鞋。 图片源自Intel Curie Module, Arduino 101 Board Are Being Discontinued (Too) - CNX Software 《Arduino程序设计基础》 的作者 陈吕洲 先生对 Arduino 101 抱有极高的评价:Genuino 101是一个极具特色的Arduino开发板,它基于Intel Curie模组,不仅有着和Arduino UNO一样特性和外设,还集成了低功耗蓝牙(BLE)和六轴姿态传感器(IMU)功能,借助intel Curie模组上模式匹配引擎,甚至可以进行机器学习操作。因此使用Genuino 101,可以完成一些传统单片机或者Arduino难以胜任的工作,制作更为惊艳的作品。为此他本人还专门为 Arduino 101 著书 —— 《Arduino 101 开发入门》7。 不过 Arduino 101 也有三点比标准 Arduino Uno 差8: 只有4组 PWM 脉宽调变输出,Uno 有6组; 没有任何的 EEPROM 存储,Uno 至少还有1KB可以使用; 单一 I/O 的电流驱动能力最高仅4mA,Uno 可到20mA。 所以 Curie 只是引脚排列与 Arduino 相仿,不能完全保证原有设计电路或者 Arduino Shield可完全相容(兼容)沿用、续用。 如果再与 MediaTek 的 LinkIt ONE 小比一下,LinkIt One也有效能更佳的处理器核心与更多容量的存储,且依然提供EEPROM 可用,但 PWM 方面则只有2组。当然,LinkIt ONE 强在无线通讯(日后会测评一下),如 GPRS、Wi-Fi、GPS等,Curie 略强在惯性感测。 英特尔似乎在 IoT 领域并没有取得太大的成就,但是英特尔的 x86 芯片却是世界上最流行的 CPU 架构,这也是英特尔的核心竞争力,所以英特尔在 IoT 领域的失败并不会影响到英特尔的核心业务,但是英特尔的 IoT 产品却是非常有趣的,比如 Edison、Galileo、Curie、Arduino 101 等等,这些产品都是英特尔的 IoT 产品,但是英特尔并没有将这些产品做成一个系列,而是分散在各个不同的系列中,这也是英特尔在 IoT 领域失败的一个原因。 笔者手上的 Genuino 101 是 2021年在 SeeedStudio 任职时,从 FAE 仓库里找到的,当时已经停产了,但是还是有一些库存,所以就拿了一块回来,但是一直没有时间去折腾,直到最近才拿出来玩一玩,但是发现 Arduino 101 的资料实在是太少了,所以就写了这篇文章,谨此纪念一家伟大的公司一个伟大的产品。 引用 List of Arduino boards and compatible systems - Wikipedia Programma 101 - Wikipedia Massimo Banzi - massimobanzi.com Intel releases the Arduino 101 firmware source code - Arduino Arduino 101 - Arduino Arduino 101 - Intel 《Arduino 101 开发入门》 - XX到此一游 Arduino 101擁抱Intel Curie核心 優缺點比一比 - 陸向陽 MakerPro

【安卓搞机记】通过 adb 侧载低版本 SDK 的安卓应用

前些日子翻出来自己高中写的安卓应用,安装上去看看,但是发现小米的 HyperOS 装不上去,一直报错: 安装失败 (-29) 失败原因 安装包与系统不兼容 我第一反应应该是因为应用的目标 SDK 版本(19)太低了,先用 App Cloner 改改参数,改到 SDK26 看看。 还是不行,打开是能打开但是白屏。 最后看看用 adb 侧载算了: 安全性 最低可安装的目标 API 级别 从 Android 14 开始,targetSdkVersion 低于 23 的应用无法安装。要求应用满足这些最低目标 API 级别要求有助于提高用户的安全性和隐私性。 恶意软件通常会以较旧的 API 级别为目标平台,以绕过在较新版本 Android 中引入的安全和隐私保护机制。例如,有些恶意软件应用使用 targetSdkVersion 22,以避免受到 Android 6.0 Marshmallow(API 级别 23)在 2015 年引入的运行时权限模型的约束。这项 Android 14 变更使恶意软件更难以规避安全和隐私权方面的改进限制。尝试安装以较低 API 级别为目标平台的应用将导致安装失败,并且 Logcat 中会显示以下消息: INSTALL_FAILED_DEPRECATED_SDK_VERSION: App package must target at least SDK version 23, but found 7 在升级到 Android 14 的设备上,targetSdkVersion 低于 23 的所有应用都将继续保持安装状态。 如果您需要测试以旧版 API 级别为目标平台的应用,请使用以下 ADB 命令: adb install --bypass-low-target-sdk-block FILENAME.apk

【Git Push】Git 推送的配置

Git 代理设置 git config --global http.proxy http://proxyUsername:proxyPassword@proxy.server.com:port git config --global https.proxy http://proxyUsername:proxyPassword@proxy.server.com:port git push 到两个地址的仓库 好比我这个博客仓库,在国内因为某些不可抗力,github 无法访问,所以我在 gitee 上也有一个仓库,这样就可以在国内访问了。但是我每次都要推送两次,很麻烦。或者要登陆 gitee 的仓库,然后按一下同步按钮,也很麻烦。 所以我就想,能不能只推送一次,然后两个仓库都更新呢? # 原始推送地址 https://github.com/IcingTomato/icing.fun.git # 添加第二个推送地址 git remote set-url --add origin https://gitee.com/IcingTomato/icing.fun.git # 查看推送/拉取地址 git remote -v # origin https://github.com/IcingTomato/icing.fun.git (fetch) # origin https://github.com/IcingTomato/icing.fun.git (push) # origin https://gitee.com/IcingTomato/icing.fun.git (push) # 推送 git push Git 不公开邮箱账户 之前貌似更新了什么,好像是将保密你的邮箱地址,并在执行基于 Web WebIDE 的 Git 操作中,使用 xxxx@user.noreply.xxx.com 作为你的邮箱地址。如果你希望命令行 Git 操作使用你的私人邮箱地址,你必须在 Git 中设置你的邮箱地址。 # 我经常用 GitHub 的邮箱,全局就设置成这个了 git config --global user.email "xxxx@users.noreply.github.com" # cd 到 icing.fun 仓库目录下 git config user.email "xxxx@users.noreply.github.com" git config user.email "xxxx@users.noreply.gitee.com"

【Git Push】GitHub 的三种 Pull Request

在 GitHub 上处理 Pull Requests(PRs)时,有三种主要的合并策略:创建合并提交(Merge Commit)、挤压合并(Squash and Merge)、变基合并(Rebase and Merge)。下面简要解释每种策略的特点及其区别: 1. 创建合并提交(Merge Commit) 操作:当你选择创建合并提交时,GitHub 会创建一个新的合并提交来将 PR 的更改合并到基分支(如 main 或 master)。这个合并提交会包含一个特殊的提交信息,通常包括一个指向 PR 的引用。 结果:在基分支的提交历史中,PR 的所有提交都将被保留,并附加一个额外的合并提交。这保持了完整的历史记录和PR的独立性。 适用场景:当你想保留对 PR 的每一次单独提交的完整历史时。 2. 挤压合并(Squash and Merge) 操作:挤压合并会将 PR 中的所有提交合并成一个单独的提交,然后将这个提交合并到基分支。 结果:基分支的提交历史更简洁,因为它只包含一个代表整个 PR 的提交。但这意味着PR中原始提交的细节被压缩。 适用场景:适用于PR包含许多小的、渐进的更改,但你想在基分支上保持一份干净、未混杂的历史记录。 3. 变基合并(Rebase and Merge) 操作:变基合并首先会将 PR 的提交历史变基到目标分支的最新提交上,然后将这些提交直接加入到基分支,而不创建额外的合并提交。 结果:这在基分支上产生一条线性的提交历史,但不会保留 PR 作为独立实体的信息。 适用场景:当你希望保持线性历史且避免合并提交时。这通常适用于较小的、清晰的更改。 总结 创建合并提交保留了所有的提交历史和PR的独立性,增加了一个额外的合并提交。 挤压合并将PR的所有提交压缩成一个单独的提交,使历史更干净。 变基合并保持了线性的提交历史,但不会创建合并提交。 选择哪种策略取决于你的项目团队如何希望管理其版本控制历史的清晰度和连贯性。

【阅板无数】RaspberryPi 启动流程探究

树莓派学习第一步,探究树莓派的启动流程。 树莓派的启动流程 相较于一般/通常的 ARM SoC 来说,树莓派1/2/3/Zero的启动流程有些不同,这里简单的记录一下。 树莓派1/2/3/Zero的启动流程 在开机时,CPU是离线的,由GPU上的一个小型RISC核心负责启动SoC,因此大部分启动组件实际上是在GPU代码上运行,而不是CPU上。 启动顺序如下: 第一阶段引导程序:用于挂载SD卡上的FAT32启动分区,以便可以访问第二阶段引导程序。它在制造树莓派时已经烧录到SoC本身,并且用户无法重新编程。 第二阶段引导程序(bootcode.bin):用于从SD卡检索GPU固件,加载固件,然后启动GPU。 GPU固件(start.elf):一经加载,允许GPU启动CPU。另一个文件fixup.dat用于配置GPU和CPU之间的SDRAM分区。此时,CPU从复位模式释放并转移到CPU上执行。 用户代码(User Code):这可以是任何数量的二进制文件之一。默认情况下,它是Linux内核(通常命名为kernel.img),但它也可以是另一个引导程序(例如U-Boot)或一个简单的应用程序。 在2012年10月19日之前,曾经还有第三阶段引导程序(loader.bin),现已废除。 下面是原文elinux.org/RPi_Software At power-up, the CPU is offline, and a small RISC core on the GPU is responsible for booting the SoC, therefore most of the boot components are actually run on the GPU code, not the CPU. The boot order and components are as follows: First stage bootloader - This is used to mount the FAT32 boot partition on the SD card so that the second stage bootloader can be accessed. It is programmed into the SoC itself during manufacture of the RPi and cannot be reprogrammed by a user. Second stage bootloader (bootcode.bin) - This is used to retrieve the GPU firmware from the SD card, program the firmware, then start the GPU. GPU firmware (start.elf) - Once loaded, this allows the GPU to start up the CPU. An additional file, fixup.dat, is used to configure the SDRAM partition between the GPU and the CPU. At this point, the CPU is release from reset and execution is transferred over. User code - This can be one of any number of binaries. By default, it is the Linux kernel (usually named kernel.img), but it can also be another bootloader (e.g. U-Boot), or a bare-bones application. Prior to 19th October 2012, there was previously also a third stage bootloader (loader.bin) but this is no longer required.[1] 树莓派4B(BCM2711)的启动流程 树莓派4B(BCM2711)因为某些硬件升级导致启动流程复杂了不是一丁半点[2]。 当树莓派4开机时,引导过程涉及BCM2711 VideoCore VI 处理器单元(VPU)的操作。以下是引导过程的步骤概述: VPU核心0的初始化: 开机时,BCM2711芯片的VPU核心0开始运行。 程序计数器设置为0x60000000,映射到片上启动ROM。 VPU频率和启动ROM: 最初,VPU的频率设置为振荡器频率,树莓派4上为54.0 MHz。 这个低频率足以完成初始引导任务。 读取OTP寄存器: 启动ROM读取某些一次性可编程(OTP)寄存器(17/18, 66, 和 67)。 这些寄存器与树莓派3相比有不同的含义。更多信息可以在这个GitHub问题中找到。 引导指示引脚: 如果配置了“引导指示引脚”,则它会被激活几毫秒。 USB设备模式恢复: 如果配置了USB设备模式强制引导引脚且激活,某些步骤会被跳过。 这是一种从USB启动恢复的机制。 SD卡引导: 如果SD卡引导引脚处于激活状态或未配置,启动ROM尝试从SD卡的第一个FAT分区加载recovery.bin。 这种机制有助于在EEPROM损坏时恢复板子。更多细节可以在树莓派硬件文档中找到。 从EEPROM加载固件: BootROM尝试从EEPROM芯片加载固件,通常是通过SPI0在GPIO 40–43接口的Winbond W25X40芯片。 USB设备模式: 如果前面的步骤失败,芯片进入通过Type-C连接器的USB设备模式。 它等待从USB主机获取恢复映像,需要在主机上更新usbboot。更多信息可以在这个拉取请求中找到。 在这种模式下,VPU的频率提升到100 MHz。 固件映像签名: 需要在固件映像中附加一个20字节的签名,这是使用存储在OTP中的通用密钥的映像的HMAC-SHA1散列。 这个密钥无法通过树莓派提供的固件的API访问。 这种机制最初在树莓派3上是可选的,现在则是强制性的,尽管它并不提供安全保护,因为存在一个通用的recovery.bin映像。 这个引导过程突出显示了确保树莓派4能够成功引导并从潜在的固件问题中恢复的各种机制。 When the Raspberry Pi 4 is powered on, the BCM2711 VPU core 0 gets started. The program counter is set to 0x60000000. This address maps to the on-chip boot ROM. Note that the VPU frequency is set to the oscillator (54.0 MHz on RPi4), so it’s not very fast, but it does not do a lot of things either: Read boot-related OTP registers (17/18, 66 and 67). Note that these registers have a different meaning than on Raspberry Pi 3. More information can be found here. If a “boot indication pin” is configured, it is activated for some milliseconds. If USB device-mode recovery force boot pin is configured and active, steps 4 and 5 are skipped. If SD card boot pin is either active or not configured, the boot ROM tries to load recovery.bin from the first FAT partition. This provides a mechanism to unbrick your board if the EEPROM gets corrupted. More details can be found here. Next, the boot ROM tries to load firmware from the EEPROM chip. This is a standard Winbond W25X40 chip, access through SPI0 on GPIO 40–43. If everything else fails, the chip enters USB device mode (on the Type-C connector) and waits to get the recovery image from the USB host. You need an updated usbboot on the host to use this method. See also this pull request. The VPU frequency is increased to 100 MHz for the USB device mode. Note that a 20-byte signature must be appended to the firmware image. It is an HMAC-SHA1 hash of the image using a universal key that is also stored in the OTP, but not accessible using the Foundation firmware APIs. A similar mechanism was available as an optional feature on Raspberry Pi3. I originally assumed that the key is unique for each Raspberry Pi, but since there is one universal recovery.bin image that works for all, there must be only one key. Given that it provides no security, I’m not sure why this hash is now mandatory.