• MOSFET数据手册,试试这样看!

    MOS管数据手册上的相关参数有很多,以MOS管VBZM7N60为例,下面一起来看一看,MOS管的数据手册一般会包含哪些参数吧。极限参数也叫绝对最大额定参数,MOS管在使用过程当中,任何情况下都不能超过下图的这些极限参数,否则MOS管有可能损坏。VDS表示漏极与源极之间所能施加的最大电压值。VGS表示栅极与源极之间所能施加的最大电压值。ID表示漏极可承受的持续电流值,如果流过的电流超过该值,会引起击穿的风险。IDM表示的是漏源之间可承受的单次脉冲电流强度,如果超过该值,会引起击穿的风险。EAS表示单脉冲雪崩击穿能量,如果电压过冲值(通常由于漏电流和杂散电感造成)未超过击穿电压,则器件不会发生雪崩击穿,因此也就不需要消散雪崩击穿的能力。EAS标定了器件可以安全吸收反向雪崩击穿能量的高低。PD表示最大耗散功率,是指MOS性能不变坏时所允许的最大漏源耗散功率,使用时要注意MOS的实际功耗应小于此参数并留有一定余量,此参数一般会随结温的上升而有所减额。(此参数靠不住)TJ, Tstg,这两个参数标定了器件工作和存储环境所允许的结温区间,应避免超过这个温度,并留有一定余量,如果确保器件工作在这个温度区间内,将极大地延长其工作寿命。dV/dt反映的是器件承受电压变化速率的能力,越大越好。对系统来说,过高的dv/dt必然会带来高的电压尖峰,较差的EMI特性,不过该变化速率通过系统电路可以进行修正。热阻表示热传导的难易程度,热阻分为沟道-环境之间的热阻、沟道-封装之间的热阻,热阻越小,表示散热性能越好。△VDS/TJ表示的是漏源击穿电压的温度系数,正温度系数,其值越小,表明稳定性越好。VGS(th)表示的是MOS的开启电压(阀值电压),对于NMOS,当外加栅极控制电压 VGS超过 VGS(th) 时,NMOS就会导通。IGSS表示栅极驱动漏电流,越小越好,对系统效率有较小程度的影响。IDSS表示漏源漏电流,栅极电压 VGS=0 、 VDS 为一定值时的漏源漏流,一般在微安级。RDS(ON)表示MOS的导通电阻,一般来说导通电阻越小越好,其决定MOS的导通损耗,导通电阻越大损耗越大,MOS温升也越高,在大功率电源中,导通损耗会占MOS整个损耗中较大的比例。gfs表示正向跨导,反映的是栅极电压对漏源电流控制的能力,gfs过小会导致MOSFET关断速度降低,关断能力减弱,过大会导致关断过快,EMI特性差,同时伴随关断时漏源会产生更大的关断电压尖峰。Ciss表示输入电容,Ciss=Cgs Cgd,该参数会影响MOS的开关时间,该值越大,同样驱动能力下,开通及关断时间就越慢,开关损耗也就越大。Coss表示输出电容,Coss=Cds Cgd;Crss表示反向传输电容,Crss=Cgd(米勒电容)。这两项参数对MOSFET关断时间略有影响,其中Cgd会影响到漏极有异常高电压时,传输到MOSFET栅极电压能量的大小,会对雷击测试项目有一定影响。Qg、Qgs、Qgd、td(on)、tr、td(off)、tf这些参数都是与时间相互关联的参数。开关速度越快对应的优点是开关损耗越小,效率高,温升低,对应的缺点是EMI特性差,MOSFET关断尖峰过高。IS 、ISM这些参数如果过小,会有电流击穿风险。VSD、trr如果过大,在桥式或LCC系统中会导致系统损耗过大,温升过高。Qrr该参数与充电时间成正比,一般越小越好。输出特性曲线是用来描述MOS管电流和电压之间关系的曲线,特性曲线会受结温的影响,一般数据手册上会列出两种温度下的特性曲线。根据MOS管的输出特性曲线,取Uds其中的一点,然后用作图的方法,可取得到相应的转移特性曲线。从转移特性曲线上可以看出当Uds为某值时,Id与Ugs之间的关系。MOS的导通电阻跟结温是呈现正温度系数变化的,也就是结温越高,导通电阻越大。MOS数据手册上一般会画出当VGS=10V时的导通电阻随温度变化的曲线。电容容量值越小,栅极总充电电量QG越小,开关速度越快,开关损耗就越小,开关电源DC/DC变换器等应用,要求较小的QG值。MOS管一般会有一个寄生二极管,寄生二极管对MOS管有保护的作用,它的特性跟普通的二极管是一样的,也具有正向导通的特性。最大安全工作区是由一系列(电压,电流)坐标点形成的一个二维区域,MOS管工作时的电压和电流都不能超过该区域,如果超过这个区域就存在危险。可以看到,MOS管的相关参数其实有很多,其实,在一般应用中,我们主要考虑漏源击穿电压VDS、持续漏极电流ID、导通电阻RDS(ON)、最大耗散功率PD、开启电压VGS(th),开关时间,工作温度范围等参数就可以了。END来源:电子电路版权归原作者所有,如有侵权,请联系删除。▍

    嵌入式ARM MOSFET 数据手册

  • 编程会有“历史终结”的一天吗?

    作者 | Gabriel Gonzalez译者 | Sambodhi策划 | 刘燕本文作者对编程历史的终结作了一番畅想,这是作者的一家之言,我们无法准确判断未来编程将会转向何处,但是我们可以根据其发展轨迹,就像本文作者一样,做出大概的判断(未必准确)。本文最初发表于作者个人博客,经原作者 Gabriel Gonzalez 授权,InfoQ 中文站翻译并分享。我花了不少时间思考这个问题:编程历史的终结可能是什么样子的。我所说的“历史的终结”,就是编程范式不再有重大发展的时候。对于编程的“命运”我很关心,因为我更愿意参与那些让我们接近终极编程模式的开源项目。从我的经验来看,这类工作寿命更长,影响更大,有助于推动整个编程领域的发展。那么,对于编程而言,历史的终结会是怎样的呢?是不是:已经到来了?一些人将编程视为已经解决的问题,并将所有新的语言和范式视为旧语言或范式的翻版。在他们看来,剩下的工作就是慢慢地优化事物,排除错误,或者解决非技术性的问题(比如人事管理或者资金),从而完善我们的工艺。这一观点我个人并不认同,因为我相信,至少,函数式编程范式将逐渐取代面向对象和命令式编程范式(虽然函数式编程未必就是终极编程范式)。人工智能?或许机器可以将我们的自然语言指令翻译成代码,从而减轻了我们准确表达意图的负担。或许一些足够智能的、人工智能驱动的 IDE 可以为我们自动地完成大多数程序。同样,我也不相信这一观点,而且我认为 Dijkstra 在他的文章《论“自然语言程序设计”的愚蠢性》(On the foolishness of "natural language programming**")中很好地推翻了这一观点。老实说,我不能肯定什么是正确答案,但是我会给出我自己关于编程的历史终结的猜测。数学DSL我的观点是,编程的下一个逻辑步骤是分成两个没有重叠的编程领域:运行时构建数学编程语言具体地说,我预期编程语言在本质上将发展成为更具数学特性的语言,让用户编写的程序像纯粹的数学表达式一样表达其意图。举例来说,考虑下列布尔逻辑“和”运算符和函数合成运算符的数学规范:True

    嵌入式ARM 编程

  • 室内定位——UWB测距及定位原理

    关注、星标公众号,直达精彩内容来源:平行机器人整理:李肖遥我们都知道卫星信号在室内会被严重影响,从而导致GPS或是北斗无法定位。所以在室内定位主要采用无线通讯、基站定位、惯导定位等多种技术集成形成一套室内位置定位系统,从而实现对人员、物体等在室内空间中的位置监控。除了通讯网络的蜂窝定位技术外,常见的室内无线定位技术还有:Wi-Fi、蓝牙、红外线、超宽带、RFID、ZigBee和超声波,今天我们就来谈谈UWB(超宽带)定位原理。UWB是什么?UWB(Ultra Wideband)超宽带技术是一种全新的、与传统通信技术有极大差异的通信新技术。它不需要使用传统通信体制中的载波,而是通过发送和接收具有纳秒或纳秒级以下的极窄脉冲来传输数据,从而具有GHz量级的带宽。UWB与传统的窄带系统相比有什么区别?超宽带系统与传统的窄带系统相比,具有穿透力强、功耗低、抗多径效果好、安全性高、系统复杂度低、能提供精确定位精度等优点。因此,超宽带技术可以应用于室内静止或者移动物体以及人的定位跟踪与导航,且能提供十分精确的定位精度。UWB的测距原理双向飞行时间法(TW-TOF,two way-time of flight)每个模块从启动开始即会生成一条独立的时间戳 。模块A的发射机在其时间戳上的Ta1时刻发射请求性质的脉冲信号,模块B在Tb1时刻发射一个响应性质的信号,被模块A在自己的时间戳Tb2时刻接收。由次可以计算出脉冲信号在两个模块之间的飞行时间,从而确定飞行距离S。S=Cx[(Tb2-Ta1)-(Tb1-Ta2)](C为光速)TOF测距方法属于双向测距技术,它主要利用信号在两个异步收发机(Transceiver)之间飞行时间来测量节点间的距离。因为在视距视线环境下,基于TOF测距方法是随距离呈线性关系,所以结果会更加精准。我们将发送端发出的数据包和接收回应的时间间记为TTOT,接收端收到数据包和发出回应的时间间隔记为TTAT,那么数据包在空中单向飞行的时间TTOF可以计算为:TTOF=(TTOT-TTAT)/2然后根据TTOF与电磁波传播速度的乘积便可计算出两点间的距离D=CxTTOFTOF测距方法和两个关键侧约束:1、发送设备和接收设备必须始终同步2、接收设备提供信号的传输时间的长短为了实现时钟同步,TOF测距方法采用了时钟偏移量来解决时钟同步问题。但由于TOF测距方法的时间依赖于本地和远程节点,测距精度容易受两端节点中时钟偏移量的影响。为了减少此类错误的影响,这里采用反向测量方法,即远程节点发送数据包,本地节点接收数据包,并自动响应,通过平均在正向和反向所得的平均值,减少对任何时钟偏移量的影响,从而减少测距误差。UWB的定位原理知道了UWB的测距原理,再来了解UWB的室内定位原理就很容易了。UWB的室内定位功能和卫星原理很相似,就是通过室内布置4个已知坐标的定位基站,需要定位的人员或设备携带定位标签,标签按照一定的频率发傻脉冲,不断和4个已知位置的基站进行测距,通过一定的精确算法定出标签的位置!遮挡对UWB室内定位的影响遮挡主要指室内定位UWB基站和定位标签之间存在障碍物,阻碍了信号直接被互相接收,从而影响定位。遮挡对UWB定位的影响主要分以下几种情形:1、实体墙:一堵实体墙的这种遮挡将使得UWB信号衰减60-70%定位精度误差上升30厘米左右,两睹或者两睹以上的实体墙遮挡,将使得UWB无法定位。2、钢板:钢铁对UWB脉冲信号吸收很严重,将使得UWB无法定位。3、玻璃:玻璃遮挡对UWB定位精度没太大影响。4、木板或纸板:一般厚度10厘米左右的木板或纸板对UWB定位精度没太大影响。5、电线杆或树木:电线杆或者树木遮挡时需要看他们之间距离基站或者标签的距离,和基站和标签的相对距离比较是否很小,比如,基站和定位标签距离50米,电线杆或者树木正好在两者中间,25米处,这种遮挡就无大的影响,如离基站或者标签距离很近小于1米,影响就很大。来源整理于网络素材,版权归原作者所有,如有侵权,请联系删除,谢谢。‧‧‧‧‧‧‧‧‧‧‧‧‧‧‧‧  END  ‧‧‧‧‧‧‧‧‧‧‧‧‧‧‧‧关注我的微信公众号,回复“加群”按规则加入技术交流群。欢迎关注我的视频号:点击“阅读原文”查看更多分享,欢迎点分享、收藏、点赞、在看。

    技术让梦想更伟大 测距 UWB

  • 面经 | 2021校招算法岗,劝退还是继续

    “给你 a、b 两个文件,各存放 50 亿条 URL,每条 URL 占用 64 字节,内存限制是 4G,请你编写代码找出 a、b 文件中相同的 URL”,这是某大厂的一道秋招笔试题。 如果面试的人是你,会怎么回答?50 亿条 URL,每个 64 字节,加起来就是 320G,最简单不用动脑筋的方法,就是把他们加载到内存里直接比对,但数据量太大,根本行不通。 进一步分析,你可能会想到对文件做拆分,对 URL 做排序,然后逐步做分析。但数据量这么大,具体怎么做呢?总不能说,我把 50 亿条 URL,按从前到后的顺序,拆分为 2000 个文件,然后挨个对比,多做几次,结果就出来了。 虽然这样能行,但也意味着你会被淘汰。因为这就是面试官要考察的关键点。对这样的题目,面试官想看到的是你能否想到分治法、哈希,或布隆过滤器之类的知识点。换句话说,这其实是一道算法题,而不是程序题。 其实,每家公司在面试时都会重点考察候选者的算法能力,甚至会让你当场写代码。可以说,算法能力直接决定了一个程序员的好坏。用武侠小说做比喻,算法是“内功”,而编程的各种框架,就犹如各种“招式”,内功不扎实,再多招式也只是花拳绣腿。 细数大厂的算法题,一般对应都是 LeetCode 中级模式,想通过面试,就得花时间好好准备。我知道,不少人都有过在 LeetCode 疯狂刷题的经历,但一年下来,发现自己没什么长进,面试官随便换个问法,就支支吾吾答不出来了。 其实,刷题不能盲目,得找对方法。我认识个人,在这方面可谓「专家」,他在同济大学读本科时,就拿到了 ACM 亚洲分区赛金牌,从卡内基梅隆大学毕业后,直接加入了 Facebook。作为 Facebook Messenger Tech Lead,参与了 Facebook App、Facebook Messenger、Facebook Phone 等产品的研发工作。 他就是前 Facebook 工程师覃超,在 Facebook 面试过上百个技术人,熟知算法面试的各项考点。所以,他总结了适合大厂算法面试的 LeetCode 高效解题四步法,手把手带你实战演练,目的是交付给你一套拿来即用的解题方法,帮你从面试中脱颖而出。 负责任地说,只要你认真跟下来,吃透这套算法面试通关方法论,距离离拿到心仪公司的 Offer 就不远了。 秒杀 专属口令「happy2021」立省 ¥80到手仅 ¥119,仅限 50 个名额现在网上的算法资料很多,但内容大多比较碎片化,经常会出现知识点覆盖不足或者研究性内容过多的问题。 而在《算法面试 40 讲》中,覃超梳理了一整套算法题切题的思路,带你现场解题,借此梳理题目背后的考点,并掌握高频算法面试题的解题思路,有效提升算法面试通过率。 课程共计 62 讲,现已经全部更新完毕。发文前看了一眼,订阅 32000 了,口碑也不错,随手截了一些用户留言,你们就知道这课多好了。  话不多说,看看目录吧。 申请了我粉丝的专属优惠: 秒杀 专属口令「happy2021」立省 ¥80,到手仅¥119,仅限 50 个名额 扫码免费试读点击「阅读原文」立省 ¥80 拿下算法

    技术让梦想更伟大

  • Linux中的软链接、硬链接:都用在哪些场合?

    来源 | IOT物联网小镇作者 | 道哥最近,看到很多文章都在介绍 Linux 中的文件系统,其中就包括:inode 节点、软链接、硬链接等重要的概念。于是就有小伙伴私信问我:这些概念我都懂,但是我能利用他们来完成什么工作呢?或者说,在哪些情况下,软链接和硬链接能够提供提供更好的解决方案呢?这篇文章我们就来简单梳理一下,软链接和硬链接的几个使用场景。什么是索引节点什么是硬链接什么是软链接软链接应用之:灵活切换不同版本的目标程序软链接应用之:动态库版本管理软链接应用之:快捷方式硬链接应用之:从不同角度对文件进行分类硬链接应用之:文件多人共享硬链接应用之:文件备份文件和索引节点 inode在 Linux 系统中,我们可以把一个文件看做 3 个组成部分:文件名:从用户角度来描述一个文件;文件内容:也就是文件中存储的那些数据;文件的描述信息:文件的类型、所有者、创建时间等等,可以称之为元信息;可以简单的做一个类比:文件本身的内容,可以看做一个实实在在的人。文件的描述信息,可以看做是派出所里的户籍卡。户籍卡上记录了一个人的姓名、年龄、住址等信息,警察叔叔通过这个户籍卡,就知道这个人的一切描述信息,除了你脑袋里的知识。回到计算机中,文件的所有信息都需要存储在硬盘上,因此就要对硬盘进行区域划分:不同的区域存储不同类型的数据,这就是文件系统的重要作用。在 Linux 系统使用的 ext2/ext3 文件系统中,从硬盘上划分一块区域,用来存放文件本身的内容(数据),这块区域按照一个最小单位:块(block)来进行划分。然后从硬盘上划分出另一块区域,专门用来存放所有文件的描述信息。每一个文件的描述信息,都用一个名为索引节点(inode)的数据结构来表示,所有文件的 inode 就统一放在这块硬盘区域中。就像户籍卡上记录了一个人的住址一样,一个文件的索引节点(inode)中,也记录了这个文件的所有描述信息,包括:文件类型、所有者、创建时间等待,当然也包括文件内容存储在硬盘的哪些块(block)中。当我们调用打开文件 API 函数的时候,操作系统首先根据传入的文件路径,找到这个文件的 inode,然后进行一系列的权限检查操作,最后从 inode 中获得这个文件的内容存储在哪些块(block)中,从而可以对文件的内容进行读取、写入操作。文件名称只是给我们用户来使用的,操作系统只是通过 inode 节点,来对文件进行管理的。当我们创建一个新文件的时候,就同时创建了这个文件对应的 inode 节点。当我们删除一个文件的时候,就同时删除了这个文件对应的 inode 节点。此时,文件本身内容所在的那个块中,数据并不会被抹除掉,因此有些数据恢复软件就是利用这个特点来进行数据找回。一句话总结:索引节点(inode)就像户籍卡,操作系统通过 inode 来管理所有的文件。硬链接刚才已经说到,每一个文件都对应一个 inode 节点。例如有一个文件 a.txt,文件内容长度是 1024 个字节,存放在硬盘上的某个块(block)中,假设就是第 10000 个块吧。那么这个文件对应的 inode 节点中,就会把 10000 这个块记录下来。同时,它还有一个 links 字段,表示:当前这个 inode 对应一个文件,此时 inode.links 的值为 1。此时,如果我们用另一个文件名 a_hard_link.txt,也来表示 a.txt 这个文件。也就是说:虽然我们用了 2 个文件名称,但是本质上指向同一个文件,内容都指向第 10000 个块中存储的文件内容。Linux 系统中提供了硬链接来支持这样的目的,它仅仅是把 inode 节点中的 links 字段的值 加1 即可,也就是 inode.links 的值变成了 2。硬链接的操作指令是:$ ln a.txt b.txt基于硬链接,用户就可以用不同的文件名来访问同一个文件,所有的操作最终修改的都是同一个文件。如果仅仅从用户的角度来看,好像我们是在操作不同的文件,但是这些文件具有自动同步的功能。这个行为有点类似于网盘:在云存储中有一个文件 hello.txt,然后我有两台电脑 A 和 B,这两台电脑会把云端的文件 hello.txt 都创建一个镜像文件在本地,就好像这个文件就在自己的硬盘上一样。当我在电脑 A 上操作 hello.txt 时,电脑 B 中的同名文件会自动更新。因此,从行为上来看,硬链接就相当于是:文件拷贝 自动同步。再来看一下硬链接文件的删除操作。在执行 $ ln a.txt a_hard_link.txt 指令之后,该文件对应的 inode 节点中,links 的值为 2。如果我们删除 a.txt,操作系统会把该文件对应的 inode 中的 links 值减1,结果为 1,操作系统发现不为 0,因此并不会删掉这个 inode。如果我们再删除 a_hard_link.txt,操作系统再次执行 inode.links 减1 动作,发现值变成了 0,于是就把这个 inode 删除了,于是这个文件就彻底不存在了。这就相当于把一个人的户籍卡给注销掉了,从户籍管理角度看,这个人就不存在了。即使存在,也是一个黑户。硬链接存在 2 个限制:不允许用户给目录创建硬链接,即:用户不可以,操作系统可以(想一下每个目录下的 . 和 ..);只有在同一个文件系统中的文件,才能创建硬链接,也就是说:不能跨文件系统;软链接为了克服硬链接的 2 个限制,软链接被引入进来了。软链接也叫符号链接,它是一个独立的文件。软链接文件的内容是一个文本字符串,存储的是目标文件(即:链接到的文件)的路径名。这个路径名可以指向任意一个文件系统的任意文件或者目录,甚至可以指向一个不存在的文件。与创建硬链接不同的是:当我们创建了一个软链接之后,操作系统会创建一个新的 inode 来表示这个软链接文件。例如有一个文件 a.txt,我们创建一个软链接 a_soft_link.txt 来指向它:$ ln -s a.txt a_soft_link.txt此时,a.txt 和 a_soft_link.txt 各自都有自己的 inode 节点。图中的绿色虚线,就表示软链接文件中的文件路径。正因为软链接文件中存储的仅仅是目标文件的路径字符串,所以可以表示任意一个文件系统中的文件,或者是目录。当我们打开文件软链接 a_soft_link.txt 时,操作系统从 a_soft_link.txt 对应的 inode 数据结构中发现:这是一个软链接文件。于是操作系统就根据其中的路径信息,找到 a.txt 的 inode 节点,从而对最终的目标文件进行操作。再来看一下软链接文件的删除操作。如果我们把目标文件 a.txt 删除掉之后,inode 节点会被删除掉,就相当于它的户籍卡被注销掉了。此时再次打开软链接 a_soft_link.txt 时,虽然其中的路径信息仍然存在,但是系统此时却找不到 a.txt 对应的 inode 节点了。因此,软链接就类似于与 Windows 系统中的快捷方式。当真正的目标文件被删除之后,快捷方式也就没有存在的意义了。软链接应用之:灵活切换不同版本的目标程序在开发的过程中,对于同一个工具软件,可能要安装多个不同的版本,例如:Python2 和 Python3, JDK8 和 JDK9 等等。此时就可以通过软链接来指定当前使用哪个版本。例如在我的电脑中:$ ll -l /usr/bin/python*lrwxrwxrwx 1 root root 9 12月 31 08:19 /usr/bin/python -> python2.7*lrwxrwxrwx 1 root root 9 12月 31 08:19 /usr/bin/python2 -> python2.7*-rwxr-xr-x 1 root root 3492624 3月 2 04:47 /usr/bin/python2.7*lrwxrwxrwx 1 root root 9 12月 31 08:19 /usr/bin/python3 -> python3.5*-rwxr-xr-x 2 root root 4456208 1月 27 02:48 /usr/bin/python3.5*当在终端窗口中输入:python 时,启动的是 python2.7 版本。如果有一天我需要使用 python3.5 版本,只需要把软链接 python 指向 python3.5 即可。软链接应用之:动态库版本管理在 Linux 系统的动态库版本管理中,有一个 SONAME 的概念。我们在编译一个动态链接库时,一般使用如下编译命令:$ gcc -fPIC -shared -o libhello.so hello.c在使用这个动态库时,需要链接这个库:-llibhello。简单的 demo 可以这么来写,但是如果遇到一些比较大的项目,需要执行严格的版本管理,那应该怎么来操作呢?Linux 系统已经为我们想到了问题的解决方案,利用 SO-NAME。首先,在编译动态链接库文件时,就指定产生 SO-NAME,它会被存储在动态链接库 ELF 文件中。我们来直接看一个优秀的开源工具 libevent 的例子:$ ll /usr/lib/libevent-2.1.so*lrwxrwxrwx 1 root root 17 Jul 27 2020 /usr/lib/libevent-2.1.so -> libevent-2.1.so.7lrwxrwxrwx 1 root root 21 Jul 27 2020 /usr/lib/libevent-2.1.so.7 -> libevent-2.1.so.7.0.1-rw-r--r-- 1 root root 412016 Jul 27 2020 /usr/lib/libevent-2.1.so.7.0.1此时使用 readelf 命令来查看生成的动态库文件 libevent-2.1.so.7.0.1:$ readelf -a libevent-2.1.so.7.0.1 | grep SONAME 0x000000000000000e (SONAME) Library soname: [libevent-2.1.so.7]它这么做有什么好处呢?Linux 系统在查找动态链接库文件时,会到下面这 3 个默认目录下查找(当然然还有其他目录,比如:当前目录,LD_LIBRARY_PATH 指定的目录)/lib: 存放操作系统最关键和基础的库文件;/usr/lib: 存放一些非系统运行时所需要的关键库文件;/usr/local/lib: 存放用户自己安装的一些第三方库文件;系统中安装的所有动态链接库,借助 ldconfig 这个程序,会自动的创建、更新或者删除对应的 SONAME(它是一个软链接,链接到 实际的库文件),并把这些 SONAME 汇总到一个文件 /etc/ld.so.cache 中缓存起来。这样,当动态库加载器查找动态库文件时,就可以直接在这个缓存文件中进行查找,加快了动态库的查找速度。软链接应用之:快捷方式利用软链接的快捷方式功能就比较好理解了,想一想:我们为什么在 Windows 的桌面上创建很多软件的快捷方式啊?在 Linux 中同样如此!比如:最近一段时间的工作,每次都要打开一个路径很深的文件。如果在资源管理器中,一层一层的点击鼠标,是不是比较浪费时间。此时,就可以在桌面上创建一个软链接,每次直接双击就打开所链接的目标文件了。硬链接之应用:从不同角度对文件进行分类比如我有一个文件夹,存储了10 个G的照片。这些照片中的人物、拍照地点、拍照时间都是不一样的。现在,我既想根据照片中的人物进行分类,也想根据拍照地点进行分类,还想根据拍照时间进行分类,那该怎么办?因为一张照片可能同时属于多个不同的分类,难道每个分类中都复制一张照片?这样也太浪费硬盘空间了!解决方案是:所有的照片仍旧放在一个总的文件夹中,然后创建不同的分类文件夹,在每个分类文件夹中,创建硬链接到目标照片文件。这样的话,不仅对照片进行了分类,而且一点都不占用硬盘空间。硬链接应用之:文件多人共享当很多人同时对同一个文件进行维护的时候,如果大家都直接操作这个文件,万一不小心把文件删除了,大家就都玩完了!此时,可以在每个人自己的私人目录中,创建一个硬链接。每次只需要对这个硬链接文件进行操作,所有的改动会自动同步到目标文件中。由于每个人都是操作硬链接文件,即使不小心删除了,也不会导致文件的丢失。因为删除硬链接文件,仅仅是把该文件的 inode 节点中的 links 值减 1 而已,只要不为 0,就不会真正的删除文件。硬链接之应用:文件备份一些小伙伴有定期备份文件、清理文件的好习惯。在备份的时候,如果是实实在在的拷贝一份,那真的是太浪费磁盘空间,特别是对于我这种只有 256G 硬盘空间的笔记本。此时,就可以利用硬链接功能,既实现文件备份的目的,又节省了大量的硬盘空间,一举两得!很多备份工具利用的就是硬链接的功能,包括 git 工具,当克隆本地的一个仓库时,执行 clone 指令:git clone --reference git 并不会把仓库中的所有文件拷贝到本地,而仅仅是创建文件的硬链接,几乎是零拷贝!来源整理于网络素材,版权归原作者所有,如有侵权,请联系删除,谢谢。‧‧‧‧‧‧‧‧‧‧‧‧‧‧‧‧  END  ‧‧‧‧‧‧‧‧‧‧‧‧‧‧‧‧关注我的微信公众号,回复“加群”按规则加入技术交流群。欢迎关注我的视频号:点击“阅读原文”查看更多分享,欢迎点分享、收藏、点赞、在看。

    技术让梦想更伟大

  • 为啥没有好用的开发单片机的国产IDE?

    关注、星标公众号,直达精彩内容来源 | 综合整理知乎内容编排 | strongerHuang你发现没有,我们买的很多商品都有“made in china”的标志,甚至购买的有些国外品牌也有“made in china”的标志。那么问题来了:为啥没有一款好用的,开发单片机的国产IDE?下面来看看知乎网友评论:网友朱志远:因为不赚钱,国内的大厂都996,程序员哪还有精力搞开源,顶多也就弄弄不费事的。IDE要做好,真的挺费劲的,其他所有的类似的东西都是这样,看看国外的开源多nb?免费的书籍你看都看不完,国内呢??根本原因就是一个是热爱,一个就是为了赚钱,而996也扼杀了一部分原本可以培养出热爱的程序员的热情。匿名网友:因为国内软件公司大部分喜欢赚快钱,这种费力不讨好的事情,互联网大厂都不干。在这方面,用联想的话说就是做不如买,买不如租,不仅IDE是外国的,各种专业软件也都是外国的,中国软件工程师也就用人家外国IDE写app软件,玩一个人工智能机器学习,因为这些都是快速赚钱的,IDE难写而且卖不出去还浪费钱。中国程序员总是自恃工资高,有技术,在这方面大部分程序员的想法就是:我只管用软件工具,造工具?不会造也不想造,造不如买,买不如租。从前有个人A打渔,他为了捞不同的鱼,制作了不同的渔网。后来又有一个人B来打渔,他向A租了渔网打渔,有人问B,你为什么不自己造一个非要租A的渔网?B说:有现成的,为什么要有?后来A没有租给B渔网,B说:不造渔网不是错,不租渔网才是错。人们称B为不争气。匿名涛叔:国产的目的是什么?是自主可控?传统行业怎么可控?只能自主生产。但软件行业完全不一样。现在几乎所有的编程语言及其工具链,主流的工业操作系统都是开放源码。只要你愿意投入人力,这些软件都是自主可控的。既然目的已经达到,国产这个手段就变得可有可无了。再者,作为一个普通的从业者,IDE功能虽多,常用的又能有多少?常用的功能基本都有开源替代品,根本不怕受制于人。总之,IDE只是工具,基本自主可控,没必要为了国产而国产。匿名鹏鹏李李:IDE是配套编程语言的。c#、vb、vc 背后有微软,所以VS活下来了Java是开源的,竞争对手只有eclpis、idea。idea确实做的好,长年一直坚持优化一门新语言的诞生,是IDE竞争的最关键阶段;golang2014年起步,那时候idea只有个go插件,好不好用。国内有个liteIDE,我用了两年,但这个是个人开发的,靠go程序员捐赠维持收入。后来,liteIDE竞争不过goland、vscode。记得好像mono自己做了个IDE,功能还算完善js语言变化也很快,h5builder就是赶上了小程序开发的浪潮,成为了vscode的竞争对手华为鸿蒙,也面向华为的开发者,提供了手机APP 开发的IDE匿名angler:不赚钱的IDE是不存在滴,微软,idea都超级赚钱。但是,小团队做不起,开发周期太长。*** hbuilder就做的很好,很多人在用。大团队不屑于做 因为来钱慢,Ide算是一种企业工具,他们往往不喜欢做这种可以让对手可以受益的事,这也算一种特色吧网上开源ide很多,比如大名鼎鼎的netbeans,vscode, 所以就算真有国外封杀那天,也不怕,现成的代码抄来就好,当然这要腾讯阿里这样的团队来做,收不收费就不好说啦匿名徐欣:因为VS真的是全宇宙最强IDE。匿名方也也:还是国内做通用软件面临的问题,没有钱支持,没法通过搞IDE赚钱,不能靠软件的销售实现开发和赚钱的正反馈,杀人的买卖有人做,赔钱的买卖没人做!来源整理于网络素材,版权归原作者所有,如有侵权,请联系删除,谢谢。‧‧‧‧‧‧‧‧‧‧‧‧‧‧‧‧  END  ‧‧‧‧‧‧‧‧‧‧‧‧‧‧‧‧关注我的微信公众号,回复“加群”按规则加入技术交流群。欢迎关注我的视频号:点击“阅读原文”查看更多分享,欢迎点分享、收藏、点赞、在看。

    技术让梦想更伟大 IDE 开发单片机

  • 一个高效的BootLoader与APP固件合并方法

    1、前言嵌入式固件一般分为BootLoader和App,BootLoader用于启动校验、App升级、App版本回滚等功能,BootLoader在cpu上电第一阶段中运行,之后跳转至App地址执行应用程序。因此,在发布固件的时候,会存在BootLoader固件和App固件;此时我们期望是将BootLoader固件和App固件合并成为一个固件,这样在量产时只需烧录一次即可。2、传统方式一些传统的方法都是“土办法”,没什么毛病,但比较繁琐。项目种类增加,或者版本发布频繁时更加体现出繁琐性,且易出错,操作稍微失误可能导致固件不完整;烧录不完整的固件,机子变“砖头”。烧录两次,分别烧录BootLoader和App固件烧录固件到芯片后,再从芯片读取固件,另存为hex文件手动复制、合并固件BootLoader支持App固件传输功能的,只烧录BootLoader,后期再升级App3、高效方式我们目标是通过自动化脚本合并生成一个发布固件,提高效率和确保固件的完整性。3.1 合并文件Linux下的脚本我们用得很多,其实Windows的脚本也非常优秀,利用Windows的脚本可以快速实现增、删、查、改文件。常用Windows脚本命令如下。合并两个文件:copy /b重命名文件:ren 删除文件:del很显然,我们利用其合并命令,只需一条指令即可将BootLoader和App文件合并。「例子:」假设当前目录存在Boot.bin和App.bin文件,合并后文件命名为Firmware.bin。copy /b .\Boot.bin   .\App.bin Firmware.bin❝注:Windows的目录路径为反斜杠,与Linux不同。❞3.2 bin转hex我们知道,二进制(bin)文件是不存在地址信息的,cpu上电执行并不一定是从地址0开始执行代码,如STM32芯片起始执行地址为0x8000000。因此不能通过串口工具烧录bin文件,只能通过J-link或者ST-link烧录,并且在烧录前指定存储起始地址。因此,将bin文件转换为hex文件是有必要的。「bin转hex方式:」使用jflash工具,把合并后的bin文件,使用jflash打开,另存为hex格式文件将bin文件烧录置芯片,读取出来,另存为hex文件自己动手写一个bin转hex工具借助第三方bin转hex工具前两者太繁琐,效率低下;第三个比较灵活,但需要花点时间;如果使用优秀的现成工具是最快捷的办法。推荐使用“srec_cat.exe”工具,可以结合Windows脚本一起使用。3.2.1 srec_cat工具srec_cat一个功能非常强大的文件合并、转换工具,支持功能众多,包括:文件合并文件分割bin转hexhex转bin数据填充CRC校验此外,还存在srec的系列工具,文件比较工具 srec_cmp.exe和文件信息查看工具 srec_info.exe,可以从文章后面官方网站下载使用。「文件合并」命令格式:srec_cat.exe 例子:srec_cat.exe source0.bin -Binary source1.bin -Binary -o merge.bin -Binarysrec_cat.exe source0.hex -Intel source1.hex -Intel -o merge.hex -Intel如果BootLoader和App生产的文件为hex格式,可以直接使用该命令合并为一个hex文件,注意地址的连续性。「bin转hex」命令格式:srec_cat.exe 例子:将Boot.bin和App.bin合并的Firmware.bin转换为hex格式文件。srec_cat.exe Firmware.bin -Binary -offset 0x8000000 -o Firmware.hex -Intel0x8000000,是STM32的起始执行地址。更多的srec应用和工具下载详见官方网站:❝http://srecord.sourceforge.net/download.html❞3.3 完整示例第一步,在需要生成固件目录新建一个txt文件。第二步,键入如下内容(Boot固件和App固件可以指定目录)。copy /b .\Boot.bin .\App.bin Firmware.binsrec_cat.exe Firmware.bin -Binary -offset 0x8000000 -o Firmware.hex -Inteldel Firmware.bin第三步,重命名txt文件为".bat"后缀文件,即是Windows可执行脚本的文件类型。第四步,双击运行脚本,即可生成目标文件。出现任何目标文件生成失败的情况,检查相关源文件是否存在,路径是否正确。3.4 举一反三以此类比,存在多个App文件的情况,可以通过该方式分别进行合并出一个固件。另外,实际项目中,经常会使用内部flash空闲扇区保存一些设备参数信息,如校准系数、设备地址、序列号等信息。我们可以将参数信息保存为一个bin文件,通过该方式和固件合并,这样量产时将参数和固件一并写入,提高生产效率!来源:https://acuity.blog.csdn.net/article/details/106724373END版权归原作者所有,如有侵权,请联系删除。▍

    嵌入式ARM 固件 Loader

  • USB标准之架构最强梳理!

    [导读] 大家好,我是逸珺。前面写了一些SPI/I2C/RS-485之类的文章,有朋友留言希望能分享一些USB方面的梳理总结,今天就从系统标准层面先来梳理一下。看看有没有朋友喜欢。先从系统层面来梳理。个人学习,习惯于先从整体上摸个大概,然后再对感兴趣的细节逐渐深入。USB是比较复杂的协议栈,如果发现文章中有错误,请帮忙指正。注:本文主要参考USB2.0规范第4章,将标准中个人认为比较重要的一些点尽量条理清晰的总结出来。我感觉很多朋友可能对于阅读英文标准有点轻度抗拒,所以整理此文这也是一个起因,希望对朋友们有所帮助。总线拓扑说到总线拓扑,这张图大家一定都见过:USB标准上说USB总线拓扑是一种分层星形结构,这张拓扑图延申出来的一些要点:所谓星型是针对Hub而言的,一个Hub下面可以挂Hub或者设备,最顶层就是USB主机控制器USB主机控制器一般都伴随有一个根集线器Root Hub。Hub级联最多5层总线上理论最大允许连接127个设备。Hub级联遵循向下兼容,USB2.0主机或者高速Hub可以连接USB1.1Hub。一个USB分层星型结构有且仅有一个USB主机控制器线缆最长不超过5米这个最大7层星形结构,代表的是一条USB总线,一个USB分层星型结构有且仅有一个USB主机控制器,但并不是说一台计算机就只有一个USB总线,比如我的计算机内部就是2个USB主控制器 Root Hub,从Windows设备管理器可以看到:7层拓扑图中的第2层只画了一个Hub,并不意味真正的第2层就只能有一个Hub,但这个图无法显示出更为详细的总线连接关系,用USBTrace软件来看看:第1条总线有4个端口,第2条总线的根集线器具有18个端口,其中8、9、10为计算机内部的USB设备占用了。我这台笔记本对外有两个USB接口,通过分别将U盘插入这两个端口,再利用USBTrace探测可以推断出这两个端口都属于第2条总线的Port 3以及Port 4:那么Hub长什么样呢?比如下面是一个7口的USB Hub示意图,来源于USB2.0标准:物理接口电气概览USB 通过四线电缆传输信号和电源。信令发生在每个点对点网段上的两条线上。VBUS/GND:供电D /D-:USB差分信号线。具有三种数据速率:高速模式 high-speed:480 Mb/s,常缩写为HS模式全速模式 full-speed:12 Mb/s,常缩写为FS模式低速模式 low-speed:1.5 Mb/s,常缩写为LS模式至于数据编码模式,这里先不管它。机械概览机械部分主要定义USB采用什么尺寸的接插件,线缆的颜色定义、线号。线缆的抗拉强度等。主要从以下几个方面去标准化:主要连接器类型规范,主要分A、B系列。定义了公头、母头。线缆规范。高速/全速电缆由信号双绞线、VBUS、GND 和整体屏蔽组成。当高速/全速电缆与低速设备一起使用时,电缆必须满足所有低速设备要求。低速设备可以不使用双绞线。双绞线可有效抵抗共模噪声。连接器机械尺寸及材料要求。连接器脚号信号名线芯颜色1VBUS红色2D-白色3D 绿色4GND黑色外壳屏蔽屏蔽层A/B系列插座规范A/B系列插头规范电缆尺寸材料规范,这里就不罗列了,知道在哪里查就可以了。电气、机械和环境合规性标准接地规范,屏蔽层一定要焊接在插头的外壳接地点。插座PCB尺寸规范。所以对于有绘制接插件需要的,可以参考6.9节的尺寸。Logo位置线芯颜色规范。USB Logo尺寸规范。协议概述USB采用主从通讯模式,是一种轮询总线。所有数据传输都由主机控制器发起。这是USB标准中最难啃的部分,这里先不总结。健壮设计标准关于协议健壮性,又称鲁棒性,做了这几个方面的设计:从信号完整性角度:使用差分驱动器、差分接收器和以及对信号线缆的屏蔽处理。差分收发策略主要在抵抗共模干扰方面效果显著,而屏蔽层则有两方面的作用:其一,有效降低USB线通过无线电波对外界干扰;其二、能有效隔断外界无线干扰对USB信号线、电源线的干扰。CRC报文校验。报文中数据如果出错,可以检测出来,可以做相应的处置。热插拔检测及对相应硬件设备的系统配置管理。这个设计有助于提升用户体验,用户随用随插,而无需关机插拔。利用对数据丢失或数据损坏超时检测实现通讯自恢复机制,以增强协议的健壮性。对流数据的进行流量控制以确保同步以及底层收发硬件缓冲区管理。数据管道和控制管道分离配置管理USB支持热插拔,所谓热插拔就是,设备插入,系统需要正确识别设备加载驱动程序,断开后,系统需要从软件层面识别到设备已经从总线上移除了。这里所谓的系统,有可能是带操作系统的计算机,也有可能是不带操作系统的嵌入式设备。比如下面这个U盘插入移除的过程:连接检测所有的USB设备都必须经过集线器上的USB端口连接到总线。从总线拓扑图可以看出,总线上有一个主控制器,位于金字塔的顶端。上面动图中也可以看出,即便是计算机内部USB设备也是经由Hub的端口连接到总线的,现在的计算机内部很多设备也会采用USB总线进行互联,比如内置的蓝牙设备。主机控制器位于顶端,设备是经过集线器的端口连接到总线,那么主控制器怎么知道设备插入呢?这是由于集线器具有用于报告USB设备在其端口之一上的连接或移除的状态位。主控制器查询集线器检索这些状态位。在设备插入时,主控制器启用端口并通过设备的控制管道在默认地址对USB设备进行寻址。主机为设备分配一个唯一的USB地址,然后确定新连接的USB设备是集线器还是功能设备。主机使用分配的USB地址和端点编号0为USB设备建立其控制管道的端点。如果新连接的USB设备是集线器,如果这个新连接的集线器的端口上有USB 设备,则对每个连接的USB设备执行上述识别操作。如果连接的USB设备是一个功能设备,则连接通知将由适用于该功能的主机软件处理。如果是基于通用操作系统的计算机系统,则由一般驱动程序处理,如果主机控制器是单片机类嵌入式设备,则由单片机程序负责处理。断开检测同样移除时,从标准设计角度来看,也需要考虑两种情况。如果是设备从集线器移除,集线器会禁用该端口并向主机提供设备移除的指示。然后主机控制器从软件层面做相应的移除处理。如果是集线器移除,需要从软件层面移除该集线器所有端口的USB设备或级联集线器,当然如果集线器又连集线器,则有点递归的意思。总线枚举总线枚举是主控制器识别连接到总线的设备并为该设备分配唯一地址的活动。由于USB允许USB设备随时连接到USB总线或从USB总线断开,因此总线枚举是 USB系统软件的持续进行的过程。USB总线枚举还包括移除的检测和处理。传输模式USB利用单向或双向管道在USB主机控制器和USB设备之间进行应用数据和功能数据传输。管道Pipe是一个底层软件及硬件逻辑概念,数据就像水一样从管道中的一侧流向另一侧,就对应着主机控制器与设备的两个端点endpoint。endpoint就像管道的两头,也是软件逻辑概念。将主机与设备侧的端点逻辑关联起来,就是所谓的管道了。当然这管道之所以能传输数据,是因为主端、设备端两侧相应的代码配合芯片内部硬件实现的。数据流经由一侧的端点通过逻辑管道传输到另一侧端点,USB架构包含四种基本类型的数据传输:控制传输批量传输中断传输同步传输下表是这四类传输在不同速度模式下支持的最大包长度,放在这里有一个整体概念:速度模式低速全速高速控制传输88/16/32/6464同步传输不支持10231024中断传输0~80~640~1024批量传输不支持8/16/32/64512控制传输控制传输,主要用于在设备首次连接时配置设备;并可用于其他设备自定义的传输目的,比如控制设备上的其他管道。USB系统软件在设备首次连接时使用控制传输进行设备配置。其他驱动程序软件也可以选择以自定义方式使用控制传输。控制传输主要使用管道0,数据传输是无损的。每个USB设备都必须有控制端点,支持控制传输来进行命令和状态的传输。此外,控制传输是双向传输。批量传输批量传输Bulk Transfer就像其名字一样,数据量相对较大,具有突发性。这种突发性可能来源于用户对USB设备的操作,比如对U盘的读写操作就会触发批量传输。批量传输这种突发性也就意味着是非周期性的,一般用于没有实时性要求、数据量比较大设备。比如用于打印机或扫描仪的数据。批量传输数据是连续的。通过在硬件中使用错误检测并采用前面所说的错误重传机制,可以在硬件级别确保可靠的数据交换。此外,大量数据占用的带宽可能会有所不同,具体取决于其他总线活动,这是由主机控制器动态仲裁管理的。批量传输是单向的,使用单向大容量端点来实现。中断传输一般用于需要实时可靠的传输场景。此类数据可随时由设备传输,并以不低于设备指定的速率进行传输。中断数据通常由一个或多个字节的事件通知、字符或坐标组成。比如USB鼠标就是一个采用中断传输的实例。同步传输占用预先约定好的USB带宽以及传输延迟,也称为流式实时传输。所需要的带宽以及延迟要求,与特定设备的相关数据吞吐率以及端点的缓冲管理有关,比如用于音频会议USB耳麦,对于音频的采样率是有参数要求的。带宽分配USB标准定义了如何允许每种传输类型访问总线的规则。带宽分配主要在从两个方面进行规定:带宽在管道之间分配。当管道建立时,USB会为一些管道分配带宽。USB底层软硬件需要提供数据缓冲,带宽需求大的场景需要提供更大的缓冲区。带宽可以在许多不同的数据流之间分配。从而能实现将各种设备连接到USB总线。此外,可以同时支持具有宽动态范围的不同设备比特率需求。所谓宽动态范围是指其带宽需求是随着使用场景在动态变化的。好了,先总结这么多,继续慢慢啃~。END来源:嵌入式客栈,作者:逸珺版权归原作者所有,如有侵权,请联系删除。▍

    嵌入式ARM USB

  • “不服跑个分?” 是噱头还是实力?

    一、背景:性能之战“不服跑个分”已经沦为手机行业的调侃用语,但是实话实说,在操作系统领域“跑分”确实是最重要的评价方式之一。比如 Linux 内核社区常常以跑分软件得分,来评价一个优化补丁的价值。甚至还有 phoronix 这样专注于 Linux 跑分的媒体。而且今天我还想说一点,让软件跑分高,这是实力的体现,是建立在对内核的深刻理解基础上的。本文的故事就源于一次日常的性能优化分析。我们在评估自动化性能调优软件 tuned 的时候,发现它在服务器场景,对 Linux 内核调度器相关的参数做了一些微小的修改,但是这些修改却很大程度改善了 hackbench 这款跑分软件的性能。是不是很有意思?让我们一起来一探究竟。本文将从几个方面展开,并重点介绍黑体字部分:相关知识简介hackbench 工作模式简介hackbench 性能受损之源双参数优化思考与拓展二、相关知识简介2.1 CFS调度器Linux 中大部分(可以粗略认为是实时任务之外的所有)线程/进程,都由一个叫 CFS(完全公平调度器)的调度器进行调度,它是 Linux 最核心的组件之一。(在Linux中,线程和进程只有细微差别,下文统一用进程表述)CFS 的核心是红黑树,用于管理系统中进程的运行时间,作为选择下一个将要运行的进程的依据。此外,它还支持优先级、组调度(基于我们熟知的 cgroup 实现)、限流等功能,满足各种高级需求。CFS 的详细介绍。2.2 hackbenchhackbench 是一个针对 Linux 内核调度器的压力测试工具,它的主要工作是创建指定数量的调度实体对(线程/进程),并让它们通过 sockets/pipe 进行数据传输,最后统计整个运行过程的时间开销。2.3 CFS 调度器参数本文重点关注以下两个参数,这两个参数也是影响 hackbench 跑分性能的重要因素。系统管理员可以使用 sysctl 命令来进行设置。最小粒度时间:kernel.sched_min_granularity_ns    通过修改 kernel.sched_min_granularity_ns,可以影响 CFS 调度周期(sched period)的时间长短。例如:设置kernel.sched_min_granularity_ns = m,当系统中存在大量可运行进程时,m 越大,CFS 调度周期就越长。如图 1 所示,每个进程都能够在 CPU 上运行且时间各有长短,sched_min_granularity_ns 保证了每个进程的最小运行时间(优先级相同的情况下),sched_min_granularity_ns 越大每个进程单次可运行的时间就越长。图 1:sched_min_granularity_ns 示意图唤醒抢占粒度:kernel.sched_wakeup_granularity_nskernel.sched_wakeup_granularity_ns 保证了重新唤醒的进程不会频繁抢占正在运行的进程,kernel.sched_wakeup_granularity_ns 越大,唤醒进程进行抢占的频率就越小。        如图 2 所示,有 process-{1,2,3} 三个进程被唤醒,因为 process-3 的运行时间大于 curr(正在 CPU 上运行的进程)无法抢占运行,而 process-2 运行时间小于 curr 但其差值小于 sched_wakeup_granularity_ns 也无法抢占运行,只有 process-1 能够抢占 curr 运行,因此 sched_wakeup_granularity_ns 越小,进程被唤醒后的响应时间就越快(等待运行时间越短)。图 2:sched_wakeup_granularity_ns 示意图三、hackbench 工作模式简介hackbench 工作模式分为 process mode 和 thread mode,主要区别就是以创建 process 还是 thread 为基础来进行测试,下面以 thread 来进行介绍。hackbench 会创建若干线程(偶数),均分为两类线程:sender 和 receiver并将其划分为 n 个 group,每个 group 包含 m  对 sender 和 receiver。每个 sender 的任务就是给其所在 group 的所有 receiver 轮流发送 loop 次大小为 datasize 的数据包receiver 则只负责接收数据包即可。同一个 group 中的sender 和 receiver 有两种方式进行通信:pipe 和 local socket(一次测试中只能都是 pipe 或者 socket),不同 group 之间的线程没有交互关系。通过上面 hackbench 模型分析,可以得知同一个 group 中的 thread/process 主要是 I/O 密集型,不同 group 之间的 thread/process 主要是 CPU 密集型。图 3: hackbench 工作模式主动上下文切换:对于 receiver,当 buffer 中没有数据时,receiver 会被阻塞并主动让出 CPU 进入睡眠。对于 sender,如果 buffer 中没有足够空间写入数据时, sender 也会被阻塞且主动让出 CPU。因此,系统中"主动上下文切换"是很多的,但同时也存在“被动上下文切换”。后者会受到接下来我们将要介绍的参数影响。四、hackbench性能影响之源在hackbench-socket 测试中,tuned修改了 CFS 的 sched_min_granularity_ns 和 sched_wakeup_granularity_ns 两个参数,导致了性能的显著区别。具体如下:开关/参数和性能sched_min_granularity_nssched_wakeup_granularity_ns性能关 tuned2.25ms3ms差开 tuned10ms15ms好接下来我们调整这两个调度参数来进行进一步的深入分析。五、双参数优化注:为了简介表达下面会以 m 表示 kernel.sched_min_granularity_ns,w 表示 kernel.sched_wakeup_granularity_ns为了探索双参数对于调度器的影响,我们选择每次固定一个参数,研究另一个参数变化对于性能的影响,并使用系统知识来解释这种现象背后的原理。5.1 固定 sched_wakeup_granularity_ns图 4: 固定 w,调整m在上图中我们固定了参数 w 并根据参数 m 变化趋势其划分为三个部分:区域A(1ms~4ms),区域B(4ms~17ms),区域C(17ms~30ms)。在区域A中四条曲线均呈现一个极速下降的趋势,而在区域B中四条曲线都处于一种震荡状态,波动较大,最后在区域C中四条曲线都趋于稳定。在第二节相关知识中可以知道 m 影响着进程的运行时间,同时也意味着它影响着进程的“被动上下文切换”。对于区域A而言,抢占过于频繁,而大部分抢占都是无意义的,因为对端无数据可写/无缓冲区可用,导致大量冗余的“主动上下文切换“。此时较大的 w 能让 sender/receiver 有更多的时间来写入数据/消耗数据来减少对端进程无意义的“主动上下文切换“。对于区域B而言,随着 m 的增加渐渐满足 sender/receiver 执行任务的时间需求能够在缓冲区写入/读出足够的数据,因此需要较小的 w 来增加唤醒进程的抢占几率,让对端进程能够更快的响应处理数据,减少下一轮调度时的“主动上下文切换”。对于区域C而言,m已经足够大,已经几乎不会有“被动上下文切换”发生,进程会在执行完任务之后进行“主动上下文切换”等待对端进程进行处理,此时 m 对性能的影响就很小了。5.2 固定 sched_min_granularity_ns图 5: 固定 m,调整w在上图中我们固定了参数 m,同样划分了三个区域:在区域A中,同样存在图 4 中的现象,较大 m 受 w 的影响较小,而较小的 m 随着 w 的增大性能会越来越好。在区域B中,中等大小的 m(8ms/12ms)进程还是存在较多“被动上下文切换”,并且其中的进程已经处理了相当一部分数据期望对端进程能够尽快的响应处理,因此较大 w 会严重影响中等大小 m 的性能。在区域C中图5和图4表现一致都是趋于稳定,因为 w 过大时几乎不会发生唤醒抢占,因此这时单纯 w 值的变化对性能的影响并不大,但是过大的 w 对于中等大小的 m 则会造成性能问题(原因同上条)。5.3 性能趋势总览下面是一个实验数据的热力总览图,来直观展示 m 和 w 之间的制约关系,以供需要的同学参考分析。三个区域和图 4、图 5 的区域会略有不同。图 6:总览图5.4 最优双参数(对于 hackbench )从上面两节的分析可知对于 hackbench 这样带有“主动上下文切换”的场景可以选择较大的 m(例如:15~20ms)。在pipe/socket 双向通信的场景中,对端的响应时间会对影响进程的下一次处理,为了让对端进程能够及时响应可以选择一个中等大小的 w(例如:6~8ms)来获取较高的性能。六、思考与扩展在桌面场景中,应用更偏向于交互型,应用的服务质量也更多的体现在应用对于用户操作的响应时间,因此可以选择较小的 sched_wakeup_granularity_ns 来提高应用的交互性。在服务器场景中,应用更偏向于计算处理,应用需要更多的运行时间来进行密集计算,因此可以选择较大的 sched_min_granularity_ns,但是为了防止单个进程独占 CPU 过久同时也为了能够及时处理客户端请求响应,应该选择一个中等大小的 sched_wakeup_granularity_ns。在 Linux 原生内核中 m 和 w 的默认参数被设置为适配桌面场景,Anolis OS的用户,需要根据自己部署的应用的场景,属于桌面型还是服务器型,来选择内核参数,或者使用tuned的推荐配置。而 hackbench 作为一个介于桌面和服务器间的应用,也可以作为配置的参考。参考资料phoronix:https://www.phoronix.com/自动化性能调优软件tuned:https://developer.aliyun.com/article/86750CFS 的详细介绍: http://www.wowotech.net/process_management/451.html在 Linux 原生内核中 m 和 w 的默认参数被设置为适配桌面场景:https://www.kernel.org/doc/Documentation/scheduler/sched-design-CFS.txt关于作者何惟禹(百奎),阿里云操作系统团队实习生,北京邮电大学在读研究生。吴一昊(丁缓),17 年加入阿里云操作系统团队,主要经历有资源隔离、热升级、调度器SLI等。加入组织添加龙蜥社区_小龙微信可入官方社群,也可随时获取最新动态。更多前沿技术尽在OpenAnolis 龙蜥操作系统开源社区,戳原文直达官网哦!

    Linux阅码场

  • 看看毕昇 JDK 团队是如何解决 JVM 中 CMS 的 Crash

    编者按:笔者遇到一个非常典型 JVM 架构相关问题,在 x86 正常运行的应用,在 aarch64 环境上低概率偶现 JVM 崩溃。这是一个典型的 JVM 内部 bug 引发的问题。通过分析最终定位到 CMS 代码存在 bug,导致 JVM 在弱内存模型的平台上 Crash。在分析过程中,涉及到 CMS 垃圾回收原理、内存屏障、对象头、以及 ParNew 并行回收算法中多个线程竞争处理的相关技术。笔者发现并修复了该问题,并推送到上游社区中。毕昇 JDK 发布的所有版本均解决了该问题,其他 JDK 在 jdk8u292、jdk11.0.9、jdk13 以后的版本修复该问题。bug 描述目标进程在 aarch64 平台上运行,使用的 GC 算法为 CMS(-XX: UseConcMarkSweepGC),会概率性地发生 JVM crash,且问题发生的概率极低。我们在 aarch64 平台上使用 fuzz 测试,运行目标进程 50w 次只出现过一次 crash(连续运行了 3 天)。JBS issue:https://bugs.openjdk.java.net/browse/JDK-8248851约束我们对比了 x86 和 aarch64 架构,发现问题仅在 aarch64 环境下会出现。文中引用的代码段取自 openjdk-8u262:http://hg.openjdk.java.net/jdk8u/jdk8u-dev/。读者需要对 JVM 有基本的认知,如垃圾回收,对象布局,GC 线程等,且有一定的 C 基础。背景知识GCGC(Garbage Collection)是 JVM 中必不可少的部分,用于回收不再会被使用到的对象,同时释放对象占用的内存空间。垃圾回收对于释放的剩余空间有两种处理方式:一种是存活对象不移动,垃圾对象释放的空间用空闲链表(free_list)来管理,通常叫做标记-清除(Mark-Sweep)。创建新对象时根据对象大小从空闲链表中选取合适的内存块存放新对象,但这种方式有两个问题,一个是空间局部性不太好,还有一个是容易产生内存碎片化的问题。另一种对剩余空间的处理方式是 Copy GC,通过移动存活对象的方式,重新得到一个连续的空闲空间,创建新对象时总在这个连续的内存空间分配,直接使用碰撞指针方式分配(Bump-Pointer)。这里又分两种情况:将存活对象复制到另一块内存(to-space,也叫 survival space),原内存块全部回收,这种方式叫撤离(Evacuation)。将存活对象推向内存块的一侧,另一侧全部回收,这种方式也被称为标记-整理(Mark-Compact)。现代的垃圾回收算法基本都是分代回收的,因为大部分对象都是朝生夕死的,因此将新创建的对象放到一块内存区域,称为年轻代;将存活时间长的对象(由年轻代晋升)放入另一块内存区域,称为老年代。根据不同代,采用不同回收算法。年轻代,一般采用 Evacuation 方式的回收算法,没有内存碎片问题,但会造成部分空间浪费。老年代,采用 Mark-Sweep 或者 Mark-Compact 算法,节省空间,但效率低。GC 算法是一个较大的课题,上述介绍只是给读者留下一个初步的印象,实际应用中会稍微复杂一些,本文不再展开。CMSCMS(Concurrent Mark Sweep)是一个以低时延为目标设计的 GC 算法,特点是 GC 的部分步骤可以和 mutator 线程(可理解为 Java 线程)同时进行,减少 STW(Stop-The-World)时间。年轻代使用 ParNewGC,是一种 Evacuation。老年代则采用 ConcMarkSweepGC,如同它的名字一样,采用 Mark-Sweep(默认行为)和 Mark-Compact(定期整理碎片)方式回收,它的具体行为可以通过参数控制,这里就不展开了,不是本文的重点研究对象。CMS 是 openjdk 中实现较为复杂的 GC 算法,条件分支很多,阅读起来也比较困难。在高版本 JDK 中已经被更优秀和高效的 G1 和 ZGC 替代(CMS 在 JDK 13 之后版本中被移除)。本文讨论的重点主要是年轻代的回收,也就是 ParNewGC 。对象布局在 Java 的世界中,万物皆对象。对象存储在内存中的方式,称为对象布局。在 JVM 中对象布局如下图所示:对象由对象头加字段组成,我们这里主要关注对象头。对象头包括markOop和_matadata。前者存放对象的标志信息,后者存放 Klass 指针。所谓 Klass,可以简单理解为这个对象属于哪个 Java 类,例如:String str = new String(); 对象 str 的 Klass 指针对应的 Java 类就是 Ljava/lang/String。markOop 的信息很关键,它的定义如下[1]:1.  //  32 bits:2.  //  --------3.  //  hash:25 ------------>| age:4  biased_lock:1 lock:2 (normal object)4.  //  JavaThread*:23 epoch:2 age:4  biased_lock:1 lock:2 (biased object)5.  //  size:32 ------------------------------------------>| (CMS free block)6.  //  PromotedObject*:29 ---------->| promo_bits:3 ----->| (CMS promoted object)7.  //8.  //  64 bits:9.  //  --------10.  //  unused:25 hash:31 -->| unused:1  age:4  biased_lock:1 lock:2 (normal object)11.  //  JavaThread*:54 epoch:2 unused:1  age:4  biased_lock:1 lock:2 (biased object)12.  //  PromotedObject*:61 --------------------->| promo_bits:3 ----->| (CMS promoted object)13.  //  size:64 ----------------------------------------------------->| (CMS free block)14.  //15.  //  unused:25 hash:31 -->| cms_free:1 age:4  biased_lock:1 lock:2 (COOPs 

    Linux阅码场

  • 相同版本 JVM 和 Java 应用,在 x86 和AArch64 平台性能相差30%,何故?

    编者按:目前许多公司同时使用 x86 和 AArch64 2 种主流的服务器。这两种环境的算力相当,内存相同的情况下:相同版本的 JVM 和 Java 应用,相同的 JVM 参数,应用性能在不同的平台中表现相差 30%,x86 远好于 AArch64 平台。本文分析了一个应用在 AArch64 平台上性能下降的例子,发现 JVM 的 CodeCache 大小是引起这个性能问题的根源,进而研究什么导致了不同平台上 CodeCache 大小的不同。最后笔者给出了不同平台中该如何设置参数规避该问题。希望本文能给读者一些启示:当使用不同的硬件平台时需要关注底层硬件对于上层应用的影响。业务在 x86 和 AArch64 上同时部署时(相同的 JDK 和 Java 应用版本),发现 AArch64 平台性能下降严重问题。进一步查看日志,发现在 AArch64 平台中偶有如下情况:这代表 JVM 中的 CodeCache 满了,导致编译停止,未编译的方法只能解释执行,进而严重影响应用性能。那什么是 CodeCache?CodeCache 是什么简单来说,CodeCache 用于存放编译后的方法,主要分为三部分:Non-nmethods:包括运行时 Stub,Adapter 等;Profiled nmethod:包括会采集信息的方法,即分层编译中第 2、3 层的方法;Non-Profiled nmethods:包括不采集信息的方法,即分层编译中第 1、4 层的方法,也包括 JNI 的方法。注:分层编译指的是 JVM 同时存在 C1 和 C2 两种编译器,C1 做一些简单的编译优化,耗时较短,C2 做更多复杂的编译优化,性能较好,编译耗时较多。分层编译的触发在 JVM 内会根据相应的条件进行触发,关于更多分层编译相关知识可以参考相关资料 [1]。在 JDK 9 之后 [2],这些会分配到不同的区域(使用不同区域的优点:查找、回收等),JDK 8 中会分配到同一块区域。JVM 平时会清理一些不可达的方法,例如由于退优化等产生的死方法,另外 UseCodeCacheFlushing 选项(默认开启),还会清理较老以及执行较少的方法。一旦 CodeCache 满了之后,会停止编译,直到 CodeCache 有空间,若关闭了 UseCodeCacheFlushing 选项,则会直接永久停止编译。不同的 JVM 版本以及不同的参数,默认的 CodeCache 大小不同。JDK 11 中默认参数下 CodeCache 大小为 240M,若想获取(确认)默认情况下的 CodeCache 大小,建议使用 - XX: PrintFlagsFinal 选项获取 ReservedCodeCache 的大小。CodeCache 大小主要通过以下选项调节:OptionDescriptionInitialCodeCacheSize初始的 CodeCache 大小(单位字节)ReservedCodeCacheSize预留的 CodeCache 大小,即最大CodeCache 大小(单位字节)CodeCacheExpansionSizeCodeCache 每次扩展大小(单位字节)使用–XX: PrintCodeCache 选项可以打印应用使用的 CodeCache 情况,如下:其中 max_used 表示应用中使用到的 CodeCache 大小,据此可以设置合适的 ReservedCodeCacheSize 值。AArch64 vs x86_64我们都知道 AArch64 和 x86 分别为 RISC 和 CISC 架构,因此代码密度方面存在一定差异,在这篇文章 [3] 中比较了不同指令集下手写汇编的大小,可以看到 AArch64 的代码密度是 RISC 架构中较优的,但相比 x86_64 仍稍差些(其中 RISC 最差,m68k 最好)。另外笔者选用业界通用的 java 测试套 dacapo[4] 比较 AArch64 和 x86_64 下 CodeCache 占用的大小。可以看到,在 AArch64 架构下,CodeCache 均比 x86_64 要大,但根据不同场景,大小差距不同,在 5%-20% 之间。因此在我们发现相同应用在 x86 和 AArch64 上时,CodeCache 大小需要进行相应的调节。除此之外,还需要注意 InlineSmallCode 选项,JVM 只会 inline 代码体积比该值小的方法。JVM 通过 inline 可以触发更多的优化,因此 inline 对于性能提升也很重要。在 JDK 11 中,InlineSmallCode 在 x86 下的默认值为 2000 字节,在 AArch64 下的默认值为 2500 字节。而 JDK 8 中,InlineSmallCode 在 x86 和 AArch64 下默认值均为 2000 字节。因此建议迁移时也相应修改 InlineSmallCode 的值。业务通过对 CodeCache 相关参数的调整,达到助力 JIT 的最佳编译效果。后记如果遇到相关技术问题(包括不限于毕昇 JDK),可以进入毕昇 JDK 社区查找相关资源(点击原文进入官网),包括二进制下载、代码仓库、使用教学、安装、学习资料等。毕昇 JDK 社区每双周周二举行技术例会,同时有一个技术交流群讨论 GCC、LLVM、JDK 和 V8 等相关编译技术,感兴趣的同学可以添加如下微信小助手,回复 Compiler 入群。参考[1]http://cr.openjdk.java.net/~thartmann/talks/2017-hotspot_under_the_hood.pdf[2]https://bugs.openjdk.java.net/browse/jdk-8015774[3]http://web.eece.maine.edu/~vweaver/papers/iccd09/ll_document.pdf[4]http://dacapobench.org/

    Linux阅码场 rc x86

  • AUTOSAR 架构下看门狗的理解

    本文主要介绍AUTOSAR 架构下的WdgDriver WdgIf WdgM 模块,分析模块之间的依赖关系以及运行原理以帮助快速理解。本文以MPC5746R为主控芯片,以FS6500为SBC,DaVinci Configurator 为AUTOSAR工具链。01模块架构与依赖1、模块介绍AUTOSAR架构中的WDG 一共分为三个部分,Wdg Driver Wdg Interface Wdg Manager。其中Wdg Driver分为 External watchdog driver 与 Internal watch driver。其中外部驱动由SBC提供功能,内部驱动由SWT模块提供功能。MPC5746R有三个SWT(Soft Watchdog Timer),SWT0 负责CPU0,SWT1负责CPU1,SWT2可以由CPU0 与CPU1访问。Wdg Driver 提供底层喂狗与设置看门狗模式的驱动函数,由WdgIf抽象成Wdg Device。WdgM获取WdgIf的抽象数据得到底层的配置数据与驱动接口函数。WdgM一共支持三种形式的看门狗检查方式。1.AliveSpuervision 2.DeadlineSupervision 3. LogicalSupervision。AliveSpuervision提供一般的喂狗服务,即监控周期性程序是否能正常执行,保证程序处于Alive状态。DeadlineSupervision提供监督软件在两个状态之间的转换时间。LogicalSupervision提供监督软件执行顺序的正确性。根据不同的检查方式,WdgM可以抽象出不同的CheckPoint,Rte层可以通过函数WdgM_CheckpointReached()来通知WdgM程序正在正确运行。2、模块交互与依赖看门狗模块由WdgM统一管理后对外,这里只围绕WdgM模块分析与其他模块的依赖性。WdgIf模块,WdgM通过WdgIf接口更改WdgDriver的驱动模式,同时通知看门狗触发条件EcuM模块,管理WdgM的Initializing 和DeInitializing状态,在Sleep模式下出发硬件看门狗Mcu模块,在WdgM监控程序失败之后,可以通过Mcu的接口Mcu_PerformReset立即重新ECU单元Det模块,诊断开发中的错误Dem模块,WdgM 在侦测到错误之后,可以通过Dem模块触发EventSchM模块,WdgM 调用SchM模块接口WdgM_GlobalSuspendInterrupts进入临界区,WdgM_GlobalRestoreInterrupts退出临界区Rte模块,Rte通过WdgM_CheckpointReached()接口,监控SWC是否按照设计运行BswM模块,WdgM在监控Spuervised Entity失败后,可以通过BswM模块重启被监控程序OS模块,周期性调度Task通过WdgM_MainFunction()调用WdgM_UpdateTickCount()接口为WdgM提供时间戳具体框图如下:02模块配置1、Wdg Wdg Driver提供三种喂狗模式给WdgM管理,WdgM可以通过Wdg_SetMode接口设置看门狗运行模式WdgSettingFast 快速喂狗WdgSettingOff 关闭看门狗WdgSettingSlow 慢速喂狗Wdg External Trigger Counter :外部定时器,定时调用Cbk函数,检测喂狗条件并喂狗2、Wdg_SbcWdgGptChannelParameterRef :关联Gpt 模块,利用Cbk函数喂狗执行喂狗操作。Q :为何要用定时中断喂狗?A : 一般理解,不在定时中断函数中喂狗是为了方式在主函数异常的情况,中断服务函数还能正常执行,这样喂狗起不到真正的作用。在AUTOSAR中为何在SBC 外部看门狗采用这种方式,因为 Wdg_30_Sbc_SetTriggerCondition 函数只是负责根据配置调整看门狗喂狗的时机,然后Gpt的Cbk函数会在这个时间到达的同时执行喂狗操作。如果,MainFunction没有及时的设置看门狗触发条件,Cbk会因为提前喂狗导致看门狗出错.截取Gpt回调函数如下:3、WdgIf关联Wdg Driver驱动函数,与WdgM直接通过函数接口关联,因此WdgIf没有Lcfg文件.关联的关键函数如下4、WdgMWdgMDemEventParameterRefs :关联Dem模块,在WdgM模块出错时可以Event的方式记录下来。WdgMMode :1)WdgMAliveSupervision :激活监控模式2 ) WdgMDeadlineSupervision :截止监控模式3)WdgMLocalStatusParams :逻辑监控模式4)WdgMSupervisedEntity :监控实体WdgMWatchdog ::管理的Wdg Device,需要关联WdgIf 的Device,通过配置的WdgM_ConfigSet关联不同的Device,可以实现不同的Wdg Device关联不同的Core。1.、关联WdgIf Device2、关联Trigger 的Device3、关联Core3源码分析WdgIf 有两个对外接口:Std_ReturnType WdgIf_SetMode(uint8 DeviceIndex, WdgIf_ModeType WdgMode )                             void WdgIf_SetTriggerCondition( uint8 DeviceIndex, uint16 Timeout )都是通过DeviceIndex 来确定所属的驱动列表,WdgM模块会通过所管理的Device数量来轮询调用接口截取一段WdgM_Init的代码说明:1、内部狗与外部狗的区别WdgM对外部狗与内部狗的管理方式相同,都是通过SetTriggerCondition的方式设置,在通过关联的Gpt模块的Ckb函数来实现喂狗。但是两者在内部实现方式不一致。内部狗通过定时器完成计数,每次触发中断都会关闭定时器,需要SetMode重新开启然后继续计数,最后在Cbk中完成喂狗。设置运行模式,开启定时器设置触发条件,写入计数值Gpt 回调函数检测喂狗条件,并喂狗2、CheckpointWdgM 提供了三种对SupervisedEntity的方式1. Alive 2. Deadline 3. ProgrameFlowWdgM通过接口函数WdgM_CheckpointReached  来表示SpuervisedEntityes的Checkpoint到达,函数接口的详细描述如下。Std_ReturnType WdgM_CheckpointReached( WdgM_SupervisedEntityIdType SEID, WdgM_CheckpointIdType CheckpointID ); 04多核的WdgM管理WdgM支持多核,每个内核互不干扰,每个Core下面都关联了单独的SupervisedEntityes, 每个Core都需要单独单独调用WdgM_MainFunction。1.获取CoreId,根据CoreId获取配置指针2. 更新WdgM节拍数   WdgM提供三种更新节拍数的方式   1. WDGM_INTERNAL_SOFTWARE_TICK , 通过MainFunction调度跟新节拍   2. WDGM_OS_COUNTER ,根据OS节拍数跟新WdgM节拍   3. WDGM_EXTERNAL_TICK ,根据接口WdgM_UpdateTickCounter更新节拍3. 更新SpuervisedEntityes的状态4. 根据Global Spuervised Entityes的状态喂狗版权声明:本文为CSDN博主「ZhouArchie」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明,已获作者转载权限。

    Linux阅码场 看门狗 SAR

  • 如何写好技术文档?

    在大多数软件工程师对编写、使用和维护代码的抱怨中,一个常见的问题是缺乏高质量的文档。缺乏文档有什么副作用呢?当遇到一个bug时,这个缩写是什么意思?这份文件是最新的吗?在整个职业生涯中,每个软件工程师都抱怨过文档的质量、数量或者完全缺乏文档。01为什么需要写文档?高质量文档对工程组织有巨大的好处。代码和api变得更容易理解。当他们的设计目标和团队目标被清楚地陈述时,项目团队会更加专注。当步骤被清晰地列出时,手动流程更容易遵循。如果过程被清楚地记录下来,那么让新成员进入团队或代码库所花费的精力就会少得多。但是,由于文档的好处有一定的滞后性,通常不会给作者带来直接的好处。不像测试,编写完测试用例,跑一遍就有结果。毕竟,你可能只编写了一个文档,但之后它将被阅读数百次,甚至数千次;它的初始成本将摊销给所有未来的读者。文档不仅可以随着时间的推移而扩展,而且它对组织的其他部分的扩展也很关键。它有助于回答以下的问题:为什么会做出这些设计决策?为什么要以这种方式实现这段代码?为什么大多数工程师不喜欢写文档?虽然文档可以带来不少好处,为什么工程师通常认为它是糟糕的?其中一个原因,正如我们提到的,是好处不是立竿见影的,特别是对作者来说。另外还有以下几点:很多工程师习惯将写代码和写作割裂开,不仅仅是在工作上,而且在思想上就认为它们是完全不相关的两项工作,这就导致好多人重代码不重文档。也有很多工程师认为自己不善写作,索性就不写了。这实际是个偷懒的借口,写文档不需要华丽的辞藻、生动的语言,你只需要将问题讲清楚即可。有时候工具不好用也会影响的文档写作。如果没有一个很好的写作工具将写文档嵌入到开发工作流程中的话,写作确实会增加工作的负担。大多数人将写文档看做是工作的额外负担。我代码都没时间写,哪有时间写文档!,这其实是错误的观念,文档虽然前期有投入,但能让你代码的后期维护成本大幅降低,磨刀不误砍柴工这个道理相信大家都还是能理解的。02写文档的重要性另外一个原因是文档被视为需要维护的额外负担,而不是使现有代码的维护更容易的东西。写文档同样对作者也有非常大的好处:它有助于审视API。编写文档是确定API是否有意义的最可靠的方法之一。通常,编写文档本身会使工程师重新评估设计决策。如果你不能解释它,不能定义它,那你可能还没有设计好它。它提供了维护的历史记录。在任何情况下,代码中的技巧都应该避免,但是当你盯着两年前编写的代码,试图找出错误所在时,好的注释将提供很大的帮助。它使你的代码看起来更专业。开发人员自然会认为文档完备的API就是设计得更好的API。虽然情况并非总是如此,但它们通常是高度相关的。虽然这个好处听起来像是表面文章,但事实并非如此:一个产品是否有良好的文档通常是一个很好的指标。这将减少其他用户提出的问题。对于编写文档的人来说,随着时间的推移这可能是最大的好处。如果你必须向某人解释某件事不止一次,那么记录该过程通常是有意义的。03像管理代码一样管理文档软件工程师使用单一的、主要的编程语言来编写程序,他们仍然经常使用不同的语言来解决特定的问题。工程师可能会编写shell脚本或Python来运行命令行任务,或者他们可能会用c 编写大部分后端代码,但用Java编写一些中间件代码,等等。每种语言都是工具箱中的一种工具。文档也应该如此:它是一种工具,用不同的语言编写,以完成特定的任务。编写文档与编写代码没有多大区别。与编程语言一样,它有规则、特定的语法和样式决定,通常是为了实现与代码中类似的目的:加强一致性、提高清晰度和避免理解错误。文档通常与代码紧密耦合,因此应该尽可能地将其视为代码。也就是说,文档也应具有如下的属性:有明确的责任人维护;有统一的内部规范;定期更新;有变更和评审机制;有问题反馈和更新机制;明确文档的读者是谁工程师在编写文档时犯的一个最重要的错误是只为自己编写文档。这样做是很自然的,并且为自己编写代码并非没有价值:毕竟,你可能需要在几年后查看这些代码,并试图弄清楚你曾经的想法。或者说在团队较小的时候,大家的工作交集很大,因此也能看懂你的文档,但是随着组织的发展,问题就逐渐凸显,新人会有这不同背景,团队大了以后,工作内容开始细化,交集减少,这时候之前写的文档对他们来说可能就很难理解了。因此在开始写作之前,应该(正式或非正式地)确定文档需要满足的受众。设计文件可能需要说服决策者。教程可能需要为完全不熟悉代码库的人提供非常明确的说明。API可能需要为该API的任何用户(无论是专家还是新手)提供完整和准确的参考信息。好的文档不需要修饰或完善。工程师在编写文档时所犯的一个错误是认为他们需要成为更好的作者。按照这个标准,很少有软件工程师会写。记住,我们的观众站在你曾经站过的地方,但没有你新的领域知识。所以你不需要成为一个伟大的作家;你只需要找一个像你一样熟悉这个领域的人。04文档类型作为工作的一部分,工程师会编写各种不同类型的文档:设计文档、代码注释、操作文档、项目页面等等。这些都可以算作文档。但重要的是要知道不同的类型,不要混合类型。一般来说,文档应该有一个单一的目的。正如API应该做一件事并且做好一样,避免在一个文档中做几件事。软件工程师经常需要编写几种主要类型的文档:参考文档,包括注释设计文档;教程;概念性文档;1.参考文档参考文档是工程师最常编写的文档类型;事实上,他们经常需要每天写一些参考文档。代码注释是工程师必须维护的最常见的参考文档形式。这些注释可以分为两个基本阵营:API注释和实现注释。这两种用户之间的区别:API注释不需要讨论实现细节或设计决策,也不要假设用户和作者一样精通API。但是实现注释可以假定读者有更多的领域知识,但是要注意不要假设得太多。2.设计文档大多数公司在项目开始之前都需要有设计文档。软件工程师通常使用团队批准的特定设计文档模板来编写建议的设计文档。然后还需要在特定的团队会议上讨论或评论设计的细节。一个好的设计文档应该涵盖:设计目标实现策略利弊权衡和具体决策替代方案各方案的优缺点一份优秀的设计文件,一旦获得批准,不仅可以作为历史记录,还可以作为项目是否成功实现其目标的衡量标准。大多数团队会将他们的设计文档进行归档,以便在后续的时间查看。在产品发布前检查设计文档通常是很有用的,以确保编写设计文档时所陈述的目标在发布时仍然是所陈述的目标(如果不是,那么文件或产品都可以进行相应的调整)。3.教程每个软件工程师,当他们加入一个新的团队时,都会想要尽可能快地跟上进度。拥有一个引导人们完成新项目设置的教程是非常有价值的。通常情况下,编写教程的最佳时机是你第一次加入一个团队的时候。拿个记事本或其他方法做笔记,写下一路上你需要做的所有事情,假设没有领域知识或特殊的设置限制;完成之后,可能会知道在这个过程中所犯的错误和原因,然后可以编辑你的步骤,以获得更精简的教程。重要的是,写下一路上你需要做的一切;尽量不要假定任何特定的设置、权限或领域知识。这类型的文档写作中,要求写作者尽可能站在用户的视角上思考,极力避免出现和用户的认知偏差,力争每个步骤做到明确无歧义,每两个步骤之间做到紧密衔接。4.概念性文档有些代码需要更深入的解释或见解,而不是仅仅通过阅读参考文档就能得到的。在这些情况下,我们需要概念性文档来提供api或系统的概述。概念性文档处理可能是API的库概述、描述服务器中数据生命周期的文档等。概念性文档是用来扩充而不是替换参考文档集的。有时候这和参考文档会有些内容重复,,但主要还是为了更深层次的说明某些问题、解释清楚某个概念。概念性文档没有必要涵盖所有边缘情况。在这种情况下,为了清晰度而牺牲一些准确性是可以接受的。概念性文件的要点在于传达理解。概念文档是最难编写的文档形式。因此,它们通常是软件工程师工具箱中最容易被忽视的文档类型。而且还有另外一个问题,没合适的地方放,参考文档可以写代码里,落地页可以写项目主页里,概念性文档似乎也只能在项目文档里找个不起眼的角落存放了。概念文档需要对广泛的受众有用:无论是专家还是新手。此外,它需要强调清晰性,所以它通常需要牺牲完整性(最好留作参考)和(有时)严格的准确性。这并不是说概念性文件应该故意不准确;这只是意味着它应该关注常见的用法,而将罕见的用法或副作用留作参考文档。05文档Review在一个组织内,光靠个人去维护文档是不行的,必须得借助群体的智慧。在一个组织内部,文档的变更也应该像代码的变更一样,需要被其他人Review,以提前发现其中的问题并提升文档的质量。技术文档得益于三种不同类型的review,每种审查都强调不同的方面:专业的视角来保证准确性:一般由团队里比较资深的人负责,他们关注的核心点是文档写的对不对,专不专业。如果Code Review做的好的话,文档的Review也属于Code Review的一部分。读者视角保证简洁性:一般由不熟悉这个领域的人来Review,比如团队的新人,或者文档的使用者。这部分主要是关注文档是否容易被看懂。写作者视角保证一致性:由写作经验丰富或者相关领域比较资深的人承担,主要是为了保证文档前后是否一致,比如对同一个专业术语的使用和理解是否有歧义。06文档写作的哲学下面的部分更多地是关于技术写作最佳实践的论述。5W法则相信大家已经听的多了,分别是WHO, WHAT, WHEN, WHERE, WHY,这是一个广泛被用在各行各业的法则,写文档当然也不例外。WHO:正如前面所说,文档的针对对象是谁,读者是谁。WHAT:明确文档写作的用途,通常仅需说明文档的用途和目的就能帮你搭建起整个文档的框架。WHEN:明确文档的创建、Review和更新日期。因为文档也有时效性,明确相关日期可以避免阅读者踩坑。WHERE:文档应该放在哪!建议一个组织或者团队有统一的永久文档存放地址,并且有版本控制。最好是方便查找、使用和分享。WHY:为什么要写这篇文档, 你期望读者读完后从文档中获得什么!1.三段式文档所有文档,文档的所有部分都有开头、中间和结尾。尽管这听起来非常愚蠢,但大多数文件通常都应该有这三个部分。不要害怕在文档中添加章节;它们将流程分解为逻辑部分,并为读者提供文档所涵盖内容的路线图。通常开头部分代表问题,中间部分介绍推荐的解决方案,结尾部分总结结论。但这也并不以为着文档应该有三个部分,如果文档内容比较多,可以将其做更细致的拆解,可以适当增加一些冗余的信息帮助读者理解文档内容。虽然很多工程师都讨厌冗余 极力追求简洁,但写文档和写代码不同,适当的冗余反而可以帮助读者理解。2.好文档必备的属性好的文档通常有三个方面:完整性、准确性和清晰度。很少在同一个文档中同时获得这三种信息;例如,当你试图使文档更完整时,清晰度就会开始受到影响。如果您试图记录API的每个可能的用例,您可能会得到一个难以理解的混乱。对于编程语言来说,在所有情况下都做到完全准确(并记录所有可能的副作用)也会影响清晰度。对于其他文件,试图明确一个复杂的主题可能会微妙地影响文件的准确性;例如,您可能决定忽略概念性文档中的一些罕见的副作用,因为文档的目的是让人们熟悉API的用法,而不是提供对所有预期行为的武断概述。在每种情况下,一个好的文档都被定义为正在执行其预期工作的文档。因此,您很少希望文档执行一项以上的工作。对于每个文档(以及每个文档类型),确定其重点并适当调整编写,写概念性文档可能不需要涵盖API的每个部分,写一个参考可能想要这个完整,但可能必须牺牲一些清晰度等。所有这些加起来就是质量,不可否认的是,质量很难精确衡量。07结论公平地说,对文档的处理与对测试的处理并不一定相同。对测试来讲可以进行原子测试(单元测试),并遵循规定的形式和功能。在大多数情况下,文件不能。测试可以自动化,但通常缺乏自动化文档的方案。文件必然是主观的;文档的质量不是由作者来衡量的,而是由读者来衡量的,而且通常是异步的。最后总结下本文几个关键点:随着时间推移,团队的壮大,文档会越来越重要;文档应该是开发人员工作流程的一部分;每篇文档专注于一个目的;文档是给读者看的,而不是你自己写作。参考:本文整理自google的软件文档

    Linux阅码场

  • WSL2:Windows 亲生的 Linux 子系统

    在上一篇文章中,我们主要聊了一下:在 Windows 系统中,安装 WSL 子系统。文章在此:WSL:在 Windows 系统中开发 Linux 程序的又一神器但是 WSL 只是长得像 Linux 而已,虽然在这个子系统中,我们可以执行 Linux 程序,但是到了底层,WSL 调用的还是 win32 API。也就是说:WSL 只是一个适配层。后来,微软终于放出了 WSL2 这个真正的 Linux 子系统,这是一个真正的 Linux 完整内核,与传统的虚拟机 VitualBox、VMWare 一样,但是它与宿主机的互操作性更好。为了完整性,我们就继续把这个流程给走完。在这个过程中遇到的问题、需要的安装软件等资源,已经上传到网盘,在文末有下载地址。安装 WSL2 的前提条件 在开始之前,我们先来看一下目前电脑中的 WSL 的版本。在 Windows 命令行窗口,输入指令: wsl --list --verbose(或者输入简化版本: wsl -l -v),得到结果:可以看到我们上一篇文章中安装的 Ubuntu-18.04,而且 WSL 的版本是 1。如果要升级到 WSL2,Windows 操作系统的内核版本必须满足一定的要求,如下:我们就按照官方的要求,检查一下自己的电脑是否满足需求:选择 Windows 徽标键 Win R,然后键入 winver,选择 “确定”。看来我的系统能满足要求,那就继续下一个步骤!如果你的内核版本不满足这个需求,可以搜索关键字 “WIN10 获取内部预览版本”,就可以找到解决方案。安装 WSL2 继续以管理员身份启动 PowerShell,然后执行指令:dism.exe /online /enable-feature /featurename:VirtualMachinePlatform /all /norestart此时,又到了重启电脑的时候!重启之后,继续在 PowserShell 中,把 Ubuntu-18.04 设置为 WSL2 版本。输入的指令是:wsl --set-version Ubuntu-18.04 2这个步骤可能会遇到这样的问题:解决方法:从微软的官网下载 WSL2 Linux 内核升级包,下载地址是:WSL2 Linux 内核升级包也可以从文末的网盘中下载下载之后,一路 Next 安装即可。此时,在 PowerShell 中重新执行 wsl --set-version Ubuntu-18.04 2 指令(需要等待一段时间,大概 1 分钟左右吧):同时,把 WSL 的默认版本改为 2:wsl --set-default version 2。此时,我们再执行指令 wsl -l -v 来查看一下,就变成 WSL2 版本了:现在,重新启动一下 Ubuntu-18.04 系统,虽然还是黑乎乎的窗口,但是它可是一个完整、纯正的 Linux 操作系统!安装桌面环境 这部分的操作,参考:https://autoize.com/xfce4-desktop-environment-and-x-server-for-ubuntu-on-wsl-2/。关于 Linux 的桌面环境,它与 Windows 桌面环境的理念有一些不一样。在 Linux 系统中,桌面环境是一个独立的应用程序,不属于操作系统的一部分。换句换说,即使没有桌面环境,也可以很愉快的玩 Linux 系统,比如那些服务器之类的设备。Linux 中的桌面由客户端和服务端组成,它们之间通过一个协议来通信,俗称:X11。服务端:负责图形的显示;客户端:根据系统设置的 DISPLAY 环境变量,把显示图形的请求发送给服务端;因此,为了让我们的 Ubuntu 显示图形界面,我们就需要安装一个服务端软件和客户端软件。注意:这里的服务端软件是安装在 Windows 系统中的,客户端是安装在 Ubuntu-18.04 系统中的。安装客户端在 Ubuntu-18.04 的终端窗口中,执行如下指令安装客户端软件 xfce4:sudo apt-get update

    Linux阅码场 Windows 子系统

  • 小学生作文技法和面向对象编程思想之间千丝万缕的联系

    大约在2013年,有一首歌在YouTube广为流传,这首歌里有大量的鸡鸣狗叫的“鬼畜”声音,一点都不严肃:

    Linux阅码场

发布文章