摩登3注册网址_华为徐文伟:5.5G是无线通信产业的下一步

近日,华为董事、战略研究院院长徐文伟受邀在IEEE GLOBECOM 2020大会上发表了主题为“万物智联的未来愿景”的演讲。IEEE GLOBECOM年会是IEEE(电气电子工程师学会)的两个旗舰会议之一,是IEEE在通信领域的全球顶级学术会议,旨在共同探讨并致力于推动通信相关的全方位创新。 徐文伟在会上表示:“5.5G是无线通信行业的下一步愿景,以满足从万物互联到万物智联的需求”,并呼吁坚持学术多样性和开放性,继续开放升级、科研升级、合作升级,产业、学术界和研究机构持续共同协作,推动基础技术的发明和基础理论的突破。 (华为董事、战略研究院院长徐文伟) 5G的发展,短期看投资、中期看生态、长期看技术演进。5G在快速规模商用的过程中,产业和垂直行业的应用和需求,推动5G进一步升级扩展,提供更丰富的联接类型、更高的联接质量,“将人类社会从万物互联带入万物智联”。 “5.5G是无线通信产业的下一步”,徐文伟表示。5.5G愿景,是对5G场景的增强和扩展。首先是继续增强ITU所定义的eMBB、mMTC、URLLC三大标准场景。然后5.5G还要扩展3大新场景,包括上行超宽带UCBC(Uplink Centric Broadband Communication)、宽带实时交互RTBC(Real-time Broadband Communication)和通信感知融合HCS(Harmonized Communication and Sensing),把5G场景的“三角形”变成更为丰富的“六边形”。 徐文伟还阐述了5.5G发展的三个技术诉求,“5.5G来源于5G,需要兼容所有5G设备;5.5G需要实现sub-100GHz全频段按需灵活使用;5.5G需要与AI深度融合,实现Air Interface+AI、Network+AI、Service+AI,应对网络多样性需求、能耗管理等诉求。”

摩登3测速登录地址_简单实用!5分钟学会Vim分屏操作

Vim分屏功能是通过分割窗口来实现的,这 是提高工作效率的一大利器。无论我们想同时显示两个文件,或者同时显示一个文件的两个不同的位置,又或者并排比较两个文件,等等,这些都能通过分屏来实现,这样子很方便代码的比对和复制粘贴。 水平方向分屏打开新文件 :sp linuxmi.py 或者 :split linuxmi.py 这个命令把窗口横向切分为两个窗口,并把光标置于上面的窗口中。 垂直方向分屏打开新文件 :vsp linux.py :vsplit linux.py :sview linux.py ->只读分屏打开文件 另外,要打开窗口编辑一个新的文件时,可以用以下命令: :new 从命令行直接打开多个文件且是分屏 vim -On file1, file2 … ->垂直分屏 vim -on file1, file2 … ->水平分屏 linuxmi@linuxmi:~/www.linuxmi.com$ vim -O3 linux.py linuxmi.py linuxmi.cpp 注:-O垂直分屏,-o水平分屏,n表示分几个屏 实时调整当前窗口的宽度 ctrl-w > //向右加宽,默认值为1ctrl-w N > //向右加宽宽度Nctrl-w < // 同理 横屏/竖屏分屏打开当前文件 ctrl+w sctrl+w v 切换分屏 ctrl+w h,j,k,lctrl+w 上下左右键 crtl+w进行分屏窗口的切换 按完以后再按一个w crtl+w进行分屏窗口的切换 按完以后再按一个r 互换窗口 crtl+w进行分屏窗口的切换 按完以后再按一个c 关闭窗口 关闭分屏 关闭窗口有以下几个个命令: ctrl+W c 关闭当前窗口 ctrl+w q 关闭当前窗口,若只有一个分屏且退出vim :only 仅保留当前分屏:hide 关闭当前分屏 调整分屏的大小(宽度与高度) ctrl+w = 所有分屏都统一高度ctrl+w + 增加高度,默认值为1ctrl+w – 减少高度10 ctrl+w + 增加10行高度ctrl-w N + //当前屏高度加N 使用指定当前屏的调整高度: res[ize] N 示例: :resize 30 移动分屏 ctrl+W H,J,K,L 将屏幕移动到最顶端ctrl-w + K 将屏幕移动到最低端ctrl-w + J 将屏幕移动到最左边ctrl-w + H 将屏幕移动到最右边ctrl-w + L 总结: 由于平时使用Vim比较多,每次都要同时打开多个文件进行操作,打开多个会话又比较麻烦,所以专门学习了一下有关Vim的一些分屏技巧并记录此文章。 END 作者:Linux迷 来源:www.linuxmi.com/vim-fenping.html 免责声明:本文内容由21ic获得授权后发布,版权归原作者所有,本平台仅提供信息存储服务。文章仅代表作者个人观点,不代表本平台立场,如有问题,请联系我们,谢谢!

摩登3注册网站_亚洲硅业开启科创板上市之路,拟募资15亿元扩大多晶硅产能

11月25日,亚洲硅业(青海)股份有限公司(以下简称“亚洲硅业”)申请科创板上市已获受理,平安证券为其保荐机构,亚洲硅业成为青海省第一家冲刺科创板的公司。 据招股书显示,亚洲硅业主营业务为多晶硅材料的研发生产、光伏电站的运营、电子气体的研发制造等,是光伏产业链的上游龙头,其主要客户有隆基股份、晶澳科技、中环股份等知名企业,后者均是A股当下炙手可热的标的。亚洲硅业本次拟发行股份不超过约8854.17万股,计划募集资金15亿元。公司将本次募集资金计划用于60000t/a 电子级多晶硅一期项目。 市场新秀?还是行业龙头? 亚洲硅业是全球领先的高纯多晶硅材料供应商之一,也是我国最早以现代化工艺进行多晶硅材料研发和生产的公司之一。公司主营业务包括多晶硅材料的研发生产、光伏电站的运营、电子气体的研发制造等。高纯多晶硅材料是光伏行业及半导体行业的基础材料,公司多晶硅材料目前主要应用于光伏行业。公司还正在积极进行半导体用多晶硅材料及电子气体的研究开发工作。其中,多晶硅材料的研发生产是公司的主要业务,占据营业收入的比重八成以上。 亚洲硅业的行业地位如何?据招股书,公司是国内首批进行自主工艺包设计并实现首次投产即以闭路循环工艺运行的现代化多晶硅材料技术研发和产品生产的企业。2019年,亚洲硅业多晶硅产量19,358.82吨,占全球多晶硅产量的3.8%,是全球多晶硅产能规模排名前十的企业,排在第8位。 作为国家绿色制造和智能制造双试点企业、国家循环经济试点企业,亚洲硅业通过开展高效节能精馏提纯、四氯化硅净化、还原炉余热利用等多项循环化改造和创新,年电耗降低40%、天然气消耗降低70%、废水排放降低67%、降低成本3.26亿元。在向数字化、智能化迈进的步伐中,亚洲硅业先后入围国家两化融合管理体系贯标试点企业,国家知识产权优势企业,全国“五一劳动”奖状单位。 在亚洲硅业的客户名单中,拥有一系列市场熟悉的公司:包括隆基股份、晶澳科技、中环股份、天合光能等。近年来,光伏领域“大牛股”隆基股份跃升为公司第一大客户。2018年、2019年、今年上半年,公司来自隆基股份的销售收入占比分别为20.09%、40.73%和69.72%。排第二的国网青海省电力公司也是公司的供应商,销售业务主要是光伏发电。 公开资料显示,高纯多晶硅材料是光伏及半导体行业的基础材料,从上述销售对象可看出,亚洲硅业多晶硅材料目前主要应用于光伏行业。当前半导体行业火热,对上游硅材料的需求日益增长,这一市场变化促使亚洲硅业积极开拓半导体用多晶硅材料的研发工作。而本次募投项目便是其在半导体业务方向的加码。但是这样的决定也是有风险的。 2017年至2019年,亚洲硅业的营业收入和净利润有所下滑。公司营收从2017年的16.9亿元下降至2019年的14.2亿元,净利润则从2017年的3.6亿元下滑至2019年的1.1亿元。亚洲硅业称,这主要由于受到多晶硅价格持续下降影响。 今年上半年公司业绩有所回升,营业收入为7.1亿元,净利润为0.6亿元。好消息是,目前多晶硅市场随着高成本、旧产能的逐渐停产,供需逐渐平衡,2020年7月开始价格在快速上扬,今年8月中旬,硅料价格一度达到了10万元/吨。对于多晶硅的价格,多位业内人士表示其波动较为频繁。亚洲硅业坦言,“公司经营业绩和盈利能力受多晶硅价格变动影响较大,虽然今年三季度开始,多晶硅价格上涨幅度较大,但是未来如果多晶硅价格再次大幅下降,公司将面临毛利率下降和经营业绩波动的风险。” 不过,根据中国有色金属工业协会硅业分会最新预测,“多晶硅市场供需关系已呈现逐步扭转的趋势,另考虑到2021年硅料供应增量远不及需求增量,因此根据相对明朗的供不应求预期,预计从12月份开始,下游将陆续进入备货阶段,多晶硅价格则逐步企稳。” 光伏产业是我国具有国际竞争优势的战略性、朝阳性产业。近年来,在政策引导和市场需求双轮驱动下,我国光伏产业快速发展,产业规模迅速扩大,产业链各环节市场占有率多年位居全球首位,已经成为世界上重要的光伏大国。作为国内首批自主工艺包设计并全封闭循环清洁生产的现代化多晶硅企业,亚洲硅业能否成功上市对市场影响较大,我们拭目以待。 免责声明:本文内容由21ic获得授权后发布,版权归原作者所有,本平台仅提供信息存储服务。文章仅代表作者个人观点,不代表本平台立场,如有问题,请联系我们,谢谢!

摩登3测速登陆_智慧需求大幅上涨,LED灯杆屏开启新路灯应用时代

在智慧路灯建设需求巨大的今天,LED灯杆屏的应用热度似乎从不褪减,几乎每一个智慧路灯项目都可见LED灯杆屏,开启全新路灯应用时代。 可以说,LED灯杆屏的出现,突破了路灯应用领域的常规化,在分分合合中实现了在应用领域的进一步深入。LED灯杆屏是安装在灯杆上的显示屏,是LED显示屏市场的一个细分领域。它是集数字化、网络化、信息化于一身的新一代媒体设备,能够为整个智慧路灯项目承载内容传播、碎片化应用和智能管理等要务。 然而,LED灯杆屏越来成为智慧路灯项目中不可缺少的配套应用,但是由于集成、成本等问题,LED灯杆屏价格是项目承包商较为关注的一个问题。 作为智慧灯杆产业中不可缺少并且作为重要配套应用——信息发布的LED灯杆屏,在目前许许多多的试点项目中得到了应用,并且开启了一场智慧显示的新浪潮。LED灯杆屏的配套应用更是为智慧灯杆的智慧管理带来更多的便利和提升更大的应用价值。 实际上,抛开产品本身不谈,红利爆发期对于LED灯杆屏厂家来说同样是一个考验和挑战,如何才能够在发展的洪流中站稳脚跟,聚势前行呢?对LED灯杆屏来说,这是一次重要的产业机遇,通过节点的升级改造以及传输技术的优化升级,LED灯杆屏在路灯领域的应用将会更加智能和节能,把应用体验再提升一个档次。 而让LED灯杆屏趋势席卷另一股重要力量就是5G商用的临近。5G商用时代的到来,助推了智慧灯杆产业的结构升级,无论是从硬件还是功能、业务上都会有很大的改变。LED灯杆屏在大市场、大跨界、大推动力的作用下更是承载更多的智慧价值,以信息化改造为亮点,以智能化升级为契机,以运营收益为“筹码”,进一步支撑智慧城市应用,呈现出新的竞争优势。 另一方面,LED灯杆屏作为智慧显示行业的“续航者”,起到了不可替代的作用,而智慧路灯产业多样化的架构也决定了其对于智慧的需求更具挑战性,LED灯杆屏各方面的演进正好符合这种需求。 免责声明:本文内容由21ic获得授权后发布,版权归原作者所有,本平台仅提供信息存储服务。文章仅代表作者个人观点,不代表本平台立场,如有问题,请联系我们,谢谢!

摩登3注册登录网_加强生产管理,黑湖智造助力企业降本增效

当前全球各国都将制造业放到非常重要的战略位置,智能制造已成为高端制造业竞争的主战场。我国高度重视智能制造发展,随着制造业智能化的升级改造,我国智能制造产业呈现较快的增长,到2020年产值规模将超27000亿元。 在政策的大力支持下,智能制造行业发展前景又将如何?行业难题又该如何解决?为此OFweek维科网有幸采访到了黑湖智造合伙人肖总,为我们深度解读“智造”的奥秘。 智能制造革新者——黑湖智造 黑湖智造的定位是一家致力于为制造业量身打造实时生产监控和通讯协同SaaS工具的企业。为自我革新的制造型企业提供基于移动端的实时数据协同、采集、呈现、监控和分析工具,提高其生产效率、降低制造成本,实现数据实时驱动决策。 黑湖智造在工厂内织了一张网,从上到下,从后到前,都可以使用黑湖智造的产品解决行业问题。举一个最简单的例子:原本的金属加工行业,各个环节间的衔接,通过纸质单据的填写与传递来完成,这样处理容易出现填错、污损或丢失等情况,并且难以快速统计。但基于黑湖的产品,企业则可以实现全生产链路的透明化管理,从客户下单到成品出厂,所有生产数据都可以通过手机或平板,被实时、准确地录入到系统中。 原本存在于工厂各角色间的信息不对称被打破,整体的协作效率也得到大大提升。不仅如此,管理者可通过数据快速定位生产瓶颈,及时调整和优化生产,选择更加适合的生产线,让企业充分利用产能,保障订单及时交期。 对于中大型工厂而言,凭借云端部署的优势,黑湖产品可以实现将数据快速复制到多个工厂,实现异地多厂协同,既能加强企业协调性,也能同步进度、方便管理。 畅快协作不仅仅在工厂内部发生,通过数据对接的方式,黑湖智造整合供应商、外协工厂以及消费端等多维度数据,进一步打通工厂、外协工厂、供应商、物流仓储、消费者之间的信息孤岛。并且将这些整体化、结构化、关系化的数据进行聚合处理,通过多维度数据的叠加分析,使库存、物流、生产等方面的协作更加高效。 在全国,黑湖智造已经服务了近千家大中小型制造业客户,涵盖食品、医药、化学制品、塑料、金属、电器等行业,其中华润集团、欧莱雅、中国兵器等都是黑湖智造的标杆用户。 在助力企业进行智能制造和数字化转型的过程中,黑湖智造帮助了许多客户,其中就有一家全球知名的连锁快餐品牌。作为全球最大的玩具经销商,该品牌每年通过旗下3.5万家门店向外输出超过15亿件玩具,而这些玩具均来自于分布在中国各地和越南的13家玩具厂商。 随着营销节奏的加快和品牌联动的日益流行,该品牌为保证每一批玩具能够准时上线全球门店,所以对供应商的交付能力特别看重。并且作为一个主打“儿童”市场的产品,它对玩具的安全和质量也提出了严格的要求。 在大多数工厂里,生产信息多以纸单和Excel表格的形式被记录,这就导致生产记录不准确成为玩具工厂内部管理遇到的第一个难题。人工填写的数据,天然存在错填和造假的风险,可靠性存疑。如果没有可靠的生产记录,厂家既无法准确掌握生产进度,也无法提供可追溯的生产信息。 另外,电子化生产信息记录的缺乏给产品追溯和质量管理方面也造成了一定的困难。在车间里,各工序质检人员通常采用手工纸单的方式记录检验数据,每天要写200多张纸质报告,之后由文员在第二天早上将几百份质检报告数据再录入到Excel中统计。这种方式既耗时耗力,又容易出错误,并且当遇到质量问题时,也很难定位问题产品出自哪家工厂的哪条产线,无法采取有针对性的措施。 该玩具厂对数字化升级的价值有较高认同基础,希望借助信息化部署加强生产管理。在新形势下多批次、小订单的个性化产品生产中,做到更严格的质量把控,同时降低自身成本。在深入了解不同类型的生产管理系统之后,玩具厂坚定不移地选择拥抱黑湖智造“云端制造协同系统”。 首先,黑湖智造“云端制造协同系统”拥有更好的用户体验和界面友好性。不同于定制化软件交付后不再更新的模式,黑湖智造“云端制造协同系统”通过持续更新迭代,以适配计算机操作系统、浏览器软件和 OFFICE 等办公软件的不断更新。系统页面能保持友好美观的同时,基本上不会发生由于不兼容而导致的按钮功能不正常,系统页面显示不正常等问题,大大减轻了信息化部门的维护成本。 在时间、经济和人力成本方面,黑湖智造“云端制造协同系统”性价比远远高于传统MES。凭借成熟的产品模块,黑湖智造“云端制造协同系统”可以在需求分析结束后直接进入搭建与测试阶段,省去了过去最为耗时费力的设计和代码编写工作,做到快速搭建、快速部署、快速上线。 由于有产品化功能模块和可视化系统搭建平台的存在,整个项目实施过程中基本不需要编程,并不需要具备很强技术功底的开发人员参与其中,降低了整个项目建设的人力成本。得益于可视化、配置化的系统搭建平台的理念,黑湖智造“云端制造协同系统”的90%日常维护都可以由企业信息化系统管理员自主完成,大大减少相应费用。 针对长期发展中出现的新的管理需求,企业信息化人员也可以在黑湖智造“云端制造协同系统”内自主实现,,大大节省了维护成本。一方面,企业信息化人员经过培训后,可以自主完成对大多数流程和表单内容的调整,节省了很大一部分由需求变更所产生的开发费用。另一方面,黑湖智造“云端制造协同系统”会定期对产品功能进行更新迭代,如果已购买了此模块的功能,后期可免费享受系统更新迭代带来的红利。 免责声明:本文内容由21ic获得授权后发布,版权归原作者所有,本平台仅提供信息存储服务。文章仅代表作者个人观点,不代表本平台立场,如有问题,请联系我们,谢谢!

摩登3娱乐登录地址_从Servlet入手带你看架构和框架设计的套路

以下代码相信大家都很熟悉,大学时学 Java Web 都写过这样的代码。 从第一次接触 Servlet 到之后的很长一段时间内,我都没理解 Servlet 是个什么玩意? 为什么要有 Servlet ? 为什么要有 Servlet 容器? 啥又是 Web 容器、HTTP 服务器? 今儿咱们就来盘盘,并且从中来看看架构和框架的设计套路。 看完之后可能对接口、抽象会有进一步的认识。 来,上车! 正文 首先浏览器发起 HTTP 请求,像早期的时候只会请求一些静态资源,这时候需要一个服务器来处理 HTTP 请求,并且将相应的静态资源返回。 这个服务器叫 HTTP 服务器。 简单点说就是解析请求,然后得知需要服务器上面哪个文件夹下哪个名字的静态文件,找到返回即可。 而随着互联网的发展,交互越发得重要,单纯的静态文件满足不了需求。 业务变得复杂,需要我们编写代码来处理诸多业务。 需要根据 HTTP 请求调用不同的业务逻辑来响应,但是我们的业务代码不能跟 HTTP 服务器耦合起来。 总不能在 HTTP 服务器的具体实现里面来做判断到底需要调用哪个业务类吧? 这就把非业务和业务强相关了。 所以需要做一层抽象,将 HTTP 的解析和具体的业务隔离。 本质上的需求就是根据 HTTP 请求找到对应的业务实现类然后执行逻辑再返回。 而业务千千万,所以需要规定一个接口,所以业务类都实现这个接口这样才好对接。 这就是接口的含义,就像 USB。 这个接口就是 Servlet,当然这是最狭义的解释。 Servlet 其实是 Server Applet,全称 Java Servlet,指的是用Java 编写的服务端程序。 其实指代的是实现 Servlet 接口的那些业务类。 这就是 Servlet 的由来。 而 Servlet 容器其实就是用来管理和加载这些 Servlet 类的,根据 HTTP 请求找到对应的 Servlet 类这就是 Servlet 容器要做的事情。 看到这是不是觉得还能再抽一层?因为这好像也和具体的业务实现没关系? 是的,还能抽一层。 没必要把 Servlet 容器做的事情和具体的业务耦合起来,业务反正照着 Servlet 接口实现就行,这样 Servlet 容器就可以加载它和管理它。 把请求和哪个 Servlet 对应关系也抽象出来,就是 web.xml 了,咱们在配置里面告诉 Servlet 容器对应关系即可。 我图中的业务实现其实对应的就是我们平常的 war 包,这就是业务和 Servlet 容器的解耦。 想必你也听过 Servlet 规范,其实 Servlet 接口和 Servlet 容器这一整套包括目录命名啊啥的合起来就叫 Servlet 规范。 所有相关的中间件按照 Servlet 规范实现,我们也按 Servlet 规范来实现业务代码,这样我们就能在不同场景选择不同的 Web 中间件。 反正规范的目的就是为了对接方便,减少对接成本。 至此 HTTP 服务器、Servlet 、Servlet 容器想必都清晰了。 而 Web…

摩登三1960_为了追求更快,CPU、内存、I/O都做了哪些努力?

背景 曾经,我面试的时候有两个最怕的。一怕问算法,二怕问高并发。 算法这个,刷了不少LeetCode,发现还是有套路可循的,虽不敢说算法能力有多强,至少没有以前那么怕了(才怪)。 而第二个,高性能高并发技术,感觉有好多技术要学,东学一点,西学一点,不成体系。直到有一次面试,遇到了一个大牛,问到了这方面,结果被虐的体无完肤。幸运的是,这位大牛不但技术一流,还认真跟我交流了学习心得,怎么样去有体系的系统去梳理这方面的技术知识,而不是瞎学。 CPU 不管什么样的编程语言,什么样的代码框架,最终都是由CPU去执行完成的(当然这么说不太准确,也有GPU、TPU、协处理器等其他情况,当然这不是本文探讨的重点)。 所以要想提高性能,提高并发量,首要问题就是如何让CPU跑的更快? 这个问题,也是一直以来CPU厂商一直在努力追求的方向。 如何让CPU更快?CPU厂商做了两个方面的努力: 加快指令执行的速度 加快CPU读取数据的速度 对于第一个方向,CPU执行指令的快慢,是跟CPU的主频紧密相关的,如何更快的取指令、指令译码、执行,缩短CPU的指令周期,提升主频在相当长一段时间里都是非常有效的办法。 从几百MHz,到如今到几GHz,CPU主频有了长足的进步,相同时间里能够执行的指令数变的更多了。 对于第二个方向,如何提升CPU读取数据的速度,答案就是加缓存,利用局部性原理将内存中经常会访问的数据搬运到CPU中,这样大大提升了存取速度。 从一级缓存,到二级缓存,乃至三级缓存,CPU缓存的层级和容量也在不断提升,读写数据的时间省了不少。 但随着时间到推移,尤其进入21世纪之后,处理器厂商发现,进一步提升主频变得越来越困难了,CPU的缓存也很难进一步扩容。 怎么办呢?既然一个人干活的速度已经很难再提升,那何不多找几个人一起干?于是,多核技术来了,一个CPU里面有多个核心,众人划桨开大船,CPU的速度再一次腾飞~ 甚至,让一个核在“闲暇时间”,利用“闲置资源”去执行另外的线程,诞生了让一个核“同时”执行两个线程的超线程技术。 上面简单交代了为了提升性能,CPU所做的努力。但是光是CPU快是没用的,还需要我们更好的去利用开发,否则就是对CPU算力的浪费。 上面提到了线程,是的,如何提高性能,提高并发量?使用多线程技术当然是一个非常好的思路。 但多线程的引入,就不得不提到两个跟线程有关的话题: 线程同步 线程阻塞 多个线程协同工作,必然会引入同步的问题,常规解决方案是加锁,加锁的线程一般会进入阻塞。 线程遇到阻塞了,就需要切换,而切换是有一定的成本开销的,不仅是系统调度的时间开销,还可能有CPU缓存失效的损失。 如果线程频频加锁,频频阻塞,那这个损失就相当可观了。为了提升性能,无锁编程技术就出现了,利用CPU提供的机制,提供更轻量的加锁方案。 同时,为了让切换后的线程仍然能够在之前的CPU核心上运行,降低缓存损失,线程的CPU亲和性绑定技术也出现了。 现代操作系统都是以时间片的形式来调度分配给多个线程使用。如果时间片还没用完就因为这样或那样的原因将执行机会拱手相让,那线程也太亏了。 于是,有人提出要充分利用CPU,别让线程阻塞,交出执行权,自己在应用层实现多个执行流的调度,这里阻塞了,就去执行那里,总之要把时间片充分用完,这就诞生了协程技术,阻塞了不要紧,我还能干别的,不要轻易发生线程切换。 内存 与CPU工作相关的第一亲密伙伴就是内存了,二者协作才能唱好一出戏。 提升内存访问的速度,同样是高性能开发话题重要组成部分! 那如何提升呢?硬件层面程序员是很难改变的,咱们只好从软件层面下功夫。 内存的管理经历了从实地址模式到分页式内存管理,如今的计算机中,CPU拿的的地址都是虚拟地址,这中间就会涉及到地址的转换,在这里就有文章可做,有两个方向可以努力: 减少缺页异常 使用大页技术 现代操作系统,基本上都会使用一个叫换页/交换文件的技术:内存空间有限,但进程越来越多,对内存空间的需求越来越大,用完了怎么办?于是在硬盘上划分一块区域出来,把内存中很久不用的数据转移到这块区域上来,等程序用到的时候,触发访问异常,再在异常处理函数中将其从硬盘读取进来。 可以想象,如果程序访问的内存老是不在内存中,而是被交换到了硬盘上,就会频繁触发缺页异常,那程序的性能肯定大打折扣,所以减少缺页异常也是提升性能的好办法。 从虚拟地址寻址真实的物理内存,这个过程是CPU完成的,具体来说,就是通过查表,从页表->一级页目录->二级页目录->物理内存。 页目录和页表是存在内存中的,毫无疑问,内存寻址是一个非常非常高频的事情,时时刻刻都在发生,而多次查表势必是很慢的,有鉴于此,CPU引入了一个叫TLB(Translation Look- aside buffer)的东西,使用缓存页表项的方式来减少内存查表的操作,加快寻址速度。 默认情况下,操作系统是以4KB为单位管理内存页的,对于一些需要大量内存的服务器程序(Redis、JVM、ElascticSearch等等),动辄就是几十个G,按照4KB的单位划分,那得产生多少的页表项啊! 而CPU中的TLB的大小是有限的,内存越多,页表项也就越多,TLB缓存失效的概率也就越大。所以,大页内存技术就出现了,4KB太小,就弄大点。大页内存技术的出现,减少了缺页异常的出现次数,也提高了TLB命中的概率,对于提升性能有很大的帮助。 在一些高配置的服务器上,内存数量庞大,而CPU多个核都要通过内存总线访问内存,可想而知,CPU核数上去以后,内存总线的竞争势必也会加剧。于是NUMA架构出现了,把CPU核心划分不同的分组,各自使用自己的内存访问总线,提高内存的访问速度。 I/O CPU和内存都够快了,但这还是不够。我们的程序日常工作中,除了一些CPU密集型的程序(执行数学运算,加密解密,机器学习等等)以外,相当一部分时间都是在执行I/O,如读写硬盘文件、收发网络数据包等等。 所以,如何提升I/O的速度,是高性能开发技术领域一个重要的话题。 因为I/O会涉及到与外设(硬盘、网卡等)的交互,而这些外设又通常是非常慢(相对CPU执行速度)的,所以正常情况下,线程执行到I/O操作时难免会阻塞,这也是前面在CPU部分提到过的。 阻塞以后那就没办法干活了,为了能干活,那就开多个线程。但线程资源是很昂贵的,没办法大量使用,况且线程多了,多个线程切换调度同样是很花时间的。 那可不可以让线程执行I/O时不阻塞呢?于是,新的技术又出现了: 非阻塞I/O I/O多路复用 异步I/O 原来的阻塞I/O是一直等,等到I/O的完成,非阻塞I/O一般是轮询,可以去干别的事,过一会儿就来问一下:好了没有? 但每个线程都去轮询也不是个事儿啊,干脆交给一个线程去专门负责吧,这就是I/O多路复用,通过select/poll的方式只用一个线程就可以处理多个I/O目标。再然后,再改进一下,用epoll,连轮询也不用了,改用内核唤醒通知的机制,同时处理的I/O目标还更多了。 异步I/O就更爽了,设置一个回调函数,自己干别的事去了,回头操作系统叫你来收数据就好了。 再说回到I/O本身,会将数据在内存和外设之间传输,如果数据量很大,让CPU去搬运数据的话,既耗时又没有技术含量,这是对CPU算力的很大浪费。 所以,为了将CPU从中解放出来,又诞生了一门技术:直接内存访问DMA,把数据的传输工作外包出去,交由DMA控制器来完成,CPU只在背后发号施令即可。 有了DMA,再也不用麻烦CPU去执行数据的搬运工作。但对于应用程序而言,想要把文件通过网络发送出去,还是要把数据在内核态空间和用户态空间来回折腾两次,这两步还得CPU出马去复制拷贝,属于一种浪费,为了解决这个问题,提升性能,又进一步产生了零拷贝技术,彻底为CPU减负。 算法架构 CPU、内存、I/O都够快了,单台计算机的性能已经很难提升了。不过,现在的服务器很少是单打独斗了,接下来就要把目光转移到算法、架构上来了。 一台服务器搞不定,那就用硬件堆出性能来,分布式集群技术和负载均衡技术就派上用场了。 这年头,哪个后端服务没有数据库?如何让数据库更快?该轮到索引技术上了,通过给数据库建立索引,提升检索速度。 但数据库这家伙的数据毕竟是存在硬盘上的,读取的时候势必会慢,要是大量的数据请求都怼上来,这谁顶得住?于是基于内存的Redis、Memcached应运而生,毕竟,访问内存比从数据库查询快得多。 算法架构这一块的技术实在太多了,也是从一个普通码农通往架构师的必经之路,咱们下回再聊。 高性能、高并发是后端开发永恒追求的话题。 每一项技术都不是凭空出现的,一定是为了解决某个问题而提出。我们在学这些技术的时候,掌握它出现的原因,和其他技术之间的关联,在自己的大脑中建立一座技术知识层级图,一定能事半功倍。 长按订阅更多精彩▼ 免责声明:本文内容由21ic获得授权后发布,版权归原作者所有,本平台仅提供信息存储服务。文章仅代表作者个人观点,不代表本平台立场,如有问题,请联系我们,谢谢!

摩登3测速代理_一个工程师的“噩梦”:刚分清CPU和GPU,却发现还有……

01 先了解什么是异构并行计算 同构计算是使用相同类型指令集和体系架构的计算单元组成系统的计算方式。 而异构计算主要是指使用不同类型指令集和体系架构的计算单元组成系统的计算方式,常见的计算单元类别包括CPU、GPU、DSP、ASIC、FPGA等。 异构计算用简单的公式可以表示为“CPU+XXX”。举例来说,AMD着力发展的APU就属于异构计算,用公式表示就是CPU+GPU。 由于术业有专攻,CPU、GPU、DSP、ASIC、FPGA各有所长,在一些场景下,引入特定计算单元,让计算系统变成混合结构,就能让CPU、GPU、DSP、FPGA执行自己最擅长的任务。 异构计算(Heterogeneous Computing)在80年代中期就已产生,其定义更加宽泛。异构计算主要是指使用不同类型指令集和体系架构的计算单元组成系统的计算方式。常见的计算单元类别包括CPU、GPU等协处理器、DSP、ASIC、FPGA等。一个异构计算平台往往包含使用不同指令集架构(ISA)的处理器。   从软件的角度来讲,异构并行计算框架是让软件开发者高效地开发异构并行的程序,充分使用计算平台资源。从硬件角度来讲,一方面,多种不同类型的计算单元通过更多时钟频率和内核数量提高计算能力;另一方面,各种计算单元通过技术优化(如GPU从底层架构支持通用计算,通过分支预测、原子运算、动态并行、统一寻址、NIC直接访问显存等能力)提高执行效率。  正是因为异构计算在理论上有着诸多的优势,一些媒体将“CPU+XXX”称为下一代处理器。 异构计算在理论上相对于同构计算拥有很多优势——HSA能够简化芯片结构设计、降低应用编程门槛、缩短项目研发周期、显著提升芯片性能、广泛共享软件生态。 有厂家甚至宣传异构计算可以实现任何程序都不必费心考虑不同处理器内核之间的存储差异。但在现阶段,异构计算除了在超算上取得了明显成绩,在其他领域优势并不大。 即便异构计算目前还存在这样或那样的一些问题,但却是非常具有发展潜力的技术。 随着技术的进步,电子消费品市场对于高性能并行计算的需求正在爆发性增长,特别是在机器视觉、人工智能、云计算、AR/VR、软件定义无线电以及其他新兴领域,都对异构计算系统有着非常大的需求。 而HSA在系统编程方式上的迈进使得一个复杂片上系统能够协调在并行计算上比CPU更高效、更低功耗的GPU、DSP以及硬件加速器等计算单元承担大部分繁重的计算工作任务,在上述新兴领域能发挥较理想的作用。 也正是因此,Parmance公司计划与华夏芯在ML-HSA项目上进行合作——该项目面向机器学习和深层神经网络,并针对华夏芯此前发起的开源gccbrig项目进行优化,gccbrig项目为任何支持GCC的平台提供编译(终结转换)功能。 国外巨头也一直着力发展异构计算系统——Intel在去年以167亿美元收购阿尔特拉,发展CPU+FPGA,AMD着力发展的APU也属于异构计算,像Imagination、MTK等一些厂商也在异构计算领域积极布局。可以说,异构计算的市场前景还是值得期待。 02 再看看CPU与GPU的区别 随着GPU的可编程性不断增强,GPU的应用能力已经远远超出了图形渲染任务,利用GPU完成通用计算的研究逐渐活跃起来,将GPU用于图形渲染以外领域的计算成为GPGPU(General Purpose computing on graphics processing units,基于GPU的通用计算)。与此同时,CPU则遇到了一些障碍,CPU为了追求通用性,将其中大部分晶体管主要用于构建控制电路(比如分支预测等)和Cache,只有少部分的晶体管来完成实际的运算工作。 CPU + GPU 是一个强大的组合,因为 CPU 包含几个专为串行处理而优化的核心,而 GPU 则由数以千计更小、更节能的核心组成,这些核心专为提供强劲的并行性能而设计。程序的串行部分在 CPU 上运行,而并行部分则在 GPU上运行。GPU 已经发展到成熟阶段,可轻松执行现实生活中的各种应用程序,而且程序运行速度已远远超过使用多核系统时的情形。未来计算架构将是并行核心 GPU 与多核 CPU 共同运行的混合型系统。 1、CPU即中央处理器 CPU( Central Processing Unit, 中央处理器)就是机器的“大脑”,也是布局谋略、发号施令、控制行动的“总司令官”。 CPU的结构主要包括运算器(ALU, Arithmetic and Logic Unit)、控制单元(CU, Control Unit)、寄存器(Register)、高速缓存器(Cache)和它们之间通讯的数据、控制及状态的总线。 简单来说就是:计算单元、控制单元和存储单元,架构如下图所示: 图:CPU微架构示意图 从字面上我们也很好理解,计算单元主要执行算术运算、移位等操作以及地址运算和转换;存储单元主要用于保存运算中产生的数据以及指令等;控制单元则对指令译码,并且发出为完成每条指令所要执行的各个操作的控制信号。 所以一条指令在CPU中执行的过程是这样的:读取到指令后,通过指令总线送到控制器(黄色区域)中进行译码,并发出相应的操作控制信号;然后运算器(绿色区域)按照操作指令对数据进行计算,并通过数据总线将得到的数据存入数据缓存器(大块橙色区域)。 过程如下图所示: 图:CPU执行指令图 CPU遵循的是冯诺依曼架构,其核心就是:存储程序,顺序执行。 在这个结构图中,负责计算的绿色区域占的面积似乎太小了,而橙色区域的缓存Cache和黄色区域的控制单元占据了大量空间。 因为CPU的架构中需要大量的空间去放置存储单元(橙色部分)和控制单元(黄色部分),相比之下计算单元(绿色部分)只占据了很小的一部分,所以它在大规模并行计算能力上极受限制,而更擅长于逻辑控制。 另外,因为遵循冯诺依曼架构(存储程序,顺序执行),CPU就像是个一板一眼的管家,人们吩咐的事情它总是一步一步来做。但是随着人们对更大规模与更快处理速度的需求的增加,这位管家渐渐变得有些力不从心。 于是,能不能把多个处理器放在同一块芯片上,让它们一起来做事,这样效率不就提高了吗?GPU便由此诞生了。 2、GPU即图形处理器。 GPU全称为Graphics Processing Unit,中文为图形处理器,就如它的名字一样,GPU最初是用在个人电脑、工作站、游戏机和一些移动设备(如平板电脑、智能手机等)上运行绘图运算工作的微处理器。 为什么GPU特别擅长处理图像数据呢?这是因为图像上的每一个像素点都有被处理的需要,而且每个像素点处理的过程和方式都十分相似,也就成了GPU的天然温床。 GPU简单架构如下图所示: 图:GPU微架构示意图 从架构图我们就能很明显的看出,GPU的构成相对简单,有数量众多的计算单元和超长的流水线,特别适合处理大量的类型统一的数据。 但GPU无法单独工作,必须由CPU进行控制调用才能工作。CPU可单独作用,处理复杂的逻辑运算和不同的数据类型,但当需要大量的处理类型统一的数据时,则可调用GPU进行并行计算。 注:GPU中有很多的运算器ALU和很少的缓存cache,缓存的目的不是保存后面需要访问的数据的,这点和CPU不同,而是为线程thread提高服务的。如果有很多线程需要访问同一个相同的数据,缓存会合并这些访问,然后再去访问dram。 再把CPU和GPU两者放在一张图上看下对比,就非常一目了然了: GPU的工作大部分都计算量大,但没什么技术含量,而且要重复很多很多次。 借用知乎上某大神的说法,就像你有个工作需要计算几亿次一百以内加减乘除一样,最好的办法就是雇上几十个小学生一起算,一人算一部分,反正这些计算也没什么技术含量,纯粹体力活而已;而CPU就像老教授,积分微分都会算,就是工资高,一个老教授顶二十个小学生,你要是富士康你雇哪个? GPU就是用很多简单的计算单元去完成大量的计算任务,纯粹的人海战术。这种策略基于一个前提,就是小学生A和小学生B的工作没有什么依赖性,是互相独立的。 有一点需要强调,虽然GPU是为了图像处理而生的,但是我们通过前面的介绍可以发现,它在结构上并没有专门为图像服务的部件,只是对CPU的结构进行了优化与调整,所以现在GPU不仅可以在图像处理领域大显身手,它还被用来科学计算、密码破解、数值分析,海量数据处理(排序,Map-Reduce等),金融分析等需要大规模并行计算的领域。 所以,GPU也可以认为是一种较通用的芯片。 从根本上说CPU和GPU它们的目的不同,且有不同侧重点,也有着不同的性能特性,在某些工作中CPU执行得更快,另一工作中或许GPU能更好。当你需要对大量数据做同样的事情时,GPU更合适,当你需要对同一数据做很多事情时,CPU正好。 然而在实际应用中,后一种情形更多,也就是CPU更为灵活能胜任更多的任务。GPU能做什么?关于图形方面的以及大型矩阵运算,如机器学习算法、挖矿、暴力破解密码等,GPU会大幅提高计算效率。 Cache, local memory:CPU > GPU Threads(线程数): GPU > CPU Registers: GPU > CPU 多寄存器可以支持非常多的Thread,thread需要用到register,thread数目大,register也必须得跟着很大才行。 SIMD Unit(单指令多数据流,以同步方式,在同一时间内执行同一条指令): GPU > CPU。 简单地说,CPU擅长分支预测等复杂操作,GPU擅长对大量数据进行简单操作。一个是复杂的劳动,一个是大量并行的工作。 其实GPU可以看作是一种专用的CPU,专为单指令在大块数据上工作而设计,这些数据都是进行相同的操作,要知道处理一大块数据比处理一个一个数据更有效,执行指令开销也会大大降低,因为要处理大块数据,意味着需要更多的晶体管来并行工作,现在旗舰级显卡都是百亿以上的晶体管。 CPU呢,它的目的是尽可能快地在单个数据上执行单个指令。由于它只需要使用单个数据单条指令,因此所需的晶体管数量要少得多,目前主流桌面CPU晶体管都是十亿以下,和顶级GPU相差十倍以上,但它需要更大的指令集,更复杂的ALU(算术逻辑单元),更好的分支预测,更好的虚拟化架构、更低的延迟等等。 另外,像我们的操作系统Windows,它是为x86处理器编写的,它需要做的任务执行的进程,在CPU上肯定更为高效,你想每个线程的任务并不相同,基本上难以并行化,完全发挥不了GPU的长处。 总而言之,CPU和GPU因为最初用来处理的任务就不同,所以设计上有不小的区别。CPU的运算速度取决于请了多么厉害的教授。教授处理复杂任务的能力是碾压小学生的,但是对于没那么复杂的任务,还是顶不住人多。当然现在的GPU也能做一些稍微复杂的工作了,相当于升级成初中生高中生的水平。但还需要CPU来把数据喂到嘴边才能开始干活,最终还是靠CPU来管的。 03 CPU+GPU并行计算的好处 一 、CPU多核转到GPU并行化(适合算术密集型) 虽然GPU并不适用于所有问题的求解,但是我们发现那些对运算力量耗费巨大的科学命题都具备天然的特色。这类程序在运行时拥有极高的运算密度、并发线程数量和频繁地存储器访问,无论是在音频处理、视觉仿真还是到分子动力学模拟和金融风险评估领域都有大量涉及。这种问题如果能够顺利迁移到GPU为主的运算环境中,将为我们带来更高效的解决方案。 传统意义上的GPU不善于运行分支代码,但是ATI和NVIDIA经过长期改进其内部架构已经使得GPU可以较为高效地运行分支、循环等复杂代码。同时因为GPU属于并行机范畴,相同的运算可以应用到每个数据元素的时候,它们可以达到最好的性能。在CPU编程环境中,写出每个输入数据元素有不同数量的输入的程序很容易,但在GPU这种并行机上还是有不少麻烦。 通用的数据结构正是GPU编程的最大困难之一。CPU程序员经常使用的数据结构如列表和树在GPU身上并不容易实现。GPU目前还不允许任意存储器访问,而且GPU运算单元的设计为主要操作是在表现位置和颜色的四维向量上。 不过这些并不能阻挡GPU编程的加速发展,因为GPU不是真的为通用计算而设计的,需要一些努力才能让GPU高速地服务通用计算程序。这些努力前些年是程序员而单独实现的,而随着ATI和NVIDIA开始看到高性能计算市场的硬件需求,我们看到无论是Fermi架构添加全能二级缓存和统一定址还是RV870架构不断优化LDS并放大并发线程数,这些都是GPU自身硬件体系为了适应未来的运算环境而做出的变革。 二、并行化编程优点 在GPU并行编程过程中,OpenCL是一个不错的选择。OpenCL是Open Computing…

摩登3主管554258:_面试必备!常见的C语言字符串操作

#字符串倒序输出 实现逻辑,通过strlen获取字符串长度,然后通过 len/2 进行交叉赋值,这里需要注意,不需要考虑len是奇数还是偶数的问题。 如果len是奇数,最后一个字符就不需要倒序,如果是偶数,最后两个字符就倒序。 #include "stdio.h"void rechange_str(char *str){ int i, len; char tmp; if (NULL == str) {  return ; } len = strlen(str); for (i = 0; i < len/2; i ++) {  tmp = str[i];  str[i] = str[len-i-1];  str[len-i-1] = tmp; }}int main(void){ char str[20] = "hello,world"; printf("%s\n",str); rechange_str(str); printf("%s\n",str); return (0);}  程序输出 hello,worlddlrow,olleh--------------------------------Process exited after 0.02841 seconds with return value 0请按任意键继续. . . #整型转字符串 实现逻辑,每个整数看其转换进制,从个位到十位百位都可以通过%操作加上/操作获得,再用一个字符数组保存0-F。 用个位数对应值转为字符,注意转换出的字符串是反向的,还要考虑传入的若是负数如何处理,再用翻转字符串完成最后整个操作 下面这段代码需要好好研究一下,最好自己运行试试。 #include "stdio.h"char *sky_itoa(int value, char *str, unsigned int radix){ char list[] = "0123456789ABCDEF"; unsigned int tmp_value; int i, j, k; if (NULL == str) {  return NULL; } if (2 != radix && 8 != radix && 10 != radix && 16 != radix) {  return NULL; } i = 0; k = 0; if (radix == 10 && value < 0) {  tmp_value = (unsigned int)(0 - value);  str[i++] = '-';  k = 1; } else {  tmp_value = (unsigned int)value; } do {  str[i++] = list[tmp_value%radix];  tmp_value /= radix; } while(tmp_value); str[i] = '\0'; //翻转 char tmp; for (j = k; j < (i+k)/2; j++) {  tmp = str[j];  str[j] = str[i+k-j-1];  str[i+k-j-1] = tmp; } return str;}int main(void){ int a = 1254545; char str[100] ={0}; printf("%s\n",sky_itoa(a,str,2)); printf("%s\n",sky_itoa(a,str,8)); printf("%s\n",sky_itoa(a,str,10)); printf("%s\n",sky_itoa(a,str,16)); return (0);}  程序输出 10011001001001001000146222211254545132491--------------------------------Process exited after 0.02963 seconds with return value 0请按任意键继续. . . #字符串复制 实现逻辑,逐个赋值直到遇到’\0’停止即可 #include "stdio.h"char *sky_strcpy(char *dst, const char *str){ if (NULL == dst ||  NULL == str) {  return NULL; } char *ret = dst; while (*str != '\0') {  *dst ++ = *str ++; } return ret; } int main(void){ char str_1[100] = "hello,world"; char str[100] ={0}; sky_strcpy(str,str_1); printf("str_1:%s\n",str_1); printf("str:%s\n",str); return (0);}  程序输出 str_1:hello,worldstr:hello,world--------------------------------Process exited after 0.03334 seconds with return value 0请按任意键继续. . . #字符串比较 1、正常比较是否相同 实现逻辑,判断字符串长度是否相同,若相同逐个比较字符是否相同 #include "stdio.h"int sky_strcmp(char *dst, char *str){ int i, len; if (NULL == dst || NULL == str) {  return 0; } if (strlen(dst) != strlen(str)) {  return 0; } len = strlen(dst); for (i = 0; i < len; i++) {  if (*dst++ != *str++) {   return 0;  } } return 1;} int main(void){ char str_1[100] = "hello,world"; char str_2[100] = "hello,world"; char str[100] = "adfs";  printf("%d\n",sky_strcmp(str_1,str)); printf("%d\n",sky_strcmp(str_1,str_2)); return (0);}  程序输出 01--------------------------------Process exited after 0.02802 seconds with return value 0请按任意键继续. . . 2、忽略大小写字符串比较 实现逻辑,在比较字符时可以将其统一转换为大写或小写,然后再进行比对即可,和正常对比无其他不同  #include "stdio.h"  #define CONVERT(c) (((c) >= 'A' && (c) <= 'Z') ? ((c) - 'A' + 'a') : (c)) int sky_strcmp(char *dst, char *str) {  int i, len;  if (NULL == dst || NULL == str) {   return 0;  }  if (strlen(dst) != strlen(str)) {   return 0;  }  len = strlen(dst);  for (i = 0; i < len; i++) {   if (CONVERT(*dst) != CONVERT(*str)) {    return 0;   }   dst ++;   str ++;  }  return 1; }   int main(void) {  char str_1[100] = "heLlo,world";  char str_2[100] = "hello,world";  char str[100] = "adfs";    printf("%d\n",sky_strcmp(str_1,str));  printf("%d\n",sky_strcmp(str_1,str_2));  return (0); }  程序输出 01--------------------------------Process exited after 0.04624 seconds with return value 0请按任意键继续. . . #memcpy函数实现 实现逻辑,主要就是逐个赋值即可完成  1、不考虑拷贝覆盖问题 #include "stdio.h"#include "string.h"void *sky_memecpy(void *dst, const void *str, int n){ if (NULL == dst || NULL == str || n <= 0) {  return NULL; } char *pdst = (char *)dst; char *pstr = (char *)str; while (n --) {  *pdst ++ = *pstr ++; } return dst;}int main(void){ char str_1[100] = "heLlo,world"; char str_2[100] = "sdfsdfs"; sky_memecpy(str_2,str_1,strlen(str_1)); printf("%s\n",str_2); return (0);}  程序输出 heLlo,world--------------------------------Process exited after 0.02516 seconds with return value 0请按任意键继续. . . 2、考虑拷贝覆盖问题 拷贝覆盖是我们在拷贝字符串的时候需要注意的一个问题,我下面的示例程序,使用第一个函数的时候,就出现了问题,使用第二个函数就没有出现问题。 原因是,我们源字符串和目的字符串的地址都是一样的,我们希望把字符串往后移动一个位置,但是实际上出现了问题。  #include "stdio.h" #include "string.h"  void *sky_memecpy_1(void *dst, const void *str, int n) {  if (NULL == dst || NULL == str || n <= 0) {   return NULL;  }  char *pdst = (char *)dst;  char *pstr = (char *)str;  while (n --) {   *pdst ++ = *pstr ++;  }  return dst; }  void *sky_memecpy(void *dst, const void *str, int n) {  if (NULL == dst || NULL == str || n <= 0) {   return NULL;  }  char *pdst = (char *)dst;  char *pstr = (char *)str;     if (pdst > pstr && pdst < pstr + n) {   pdst = pdst + n - 1;   pstr = pstr + n - 1;   while (n --) {    *pdst -- = *pstr --;   }  } else {   while (n --) {    *pdst ++ = *pstr ++;   }  }  return dst; }  int main(void) {  char str_1[100] = "heLlo,world";  char str_2[100] = "heLlo,world";  sky_memecpy_1(str_1+1,str_1,strlen(str_1));  printf("%s\n",str_1);    sky_memecpy(str_2+1,str_2,strlen(str_2));  printf("%s\n",str_2);  return (0); }  程序输出 hhhhhhhhhhhhhheLlo,world--------------------------------Process exited after 0.02773 seconds with return value 0请按任意键继续. . . 针对上面的拷贝覆盖问题,单独写了一个测试程序 #include "stdio.h"#include "string.h"void *sky_memecpy(void *dst, const void *str, int n){ if (NULL == dst || NULL == str || n <= 0) {  return NULL; } char *pdst = (char *)dst; char *pstr = (char *)str; while (n --) {  printf("dst:%c--->str:%c\n",*pdst,*pstr);  *pdst ++ = *pstr ++; } return dst;}int main(void){ char str_1[100] = "heLlo,world"; sky_memecpy(str_1+1,str_1,strlen(str_1)); printf("%s\n",str_1); return (0);}  程序输出 dst:e--->str:hdst:L--->str:hdst:l--->str:hdst:o--->str:hdst:,--->str:hdst:w--->str:hdst:o--->str:hdst:r--->str:hdst:l--->str:hdst:d--->str:hdst: --->str:hhhhhhhhhhhhh--------------------------------Process exited after 0.02575 seconds with return value 0请按任意键继续. . . 初始的时候,dst指向 e 字符,str 指向h 字符,然后每次都是dst先移动,str再移动,就出现了dst被h字符所覆盖。 好了,就这些内容,希望大家好好消化,这些代码对面试很有帮助。 END 来源:嵌入式Linux 免责声明:本文内容由21ic获得授权后发布,版权归原作者所有,本平台仅提供信息存储服务。文章仅代表作者个人观点,不代表本平台立场,如有问题,请联系我们,谢谢!

摩登3注册平台官网_嵌入式裸机编程中使用malloc、free会怎样?

在嵌入式裸机编程中,作为一名初级的CODER。经常要与CPU、内存等打交道。CPU作为系统的动力源,其重要程度不言而喻。 但是,在裸机编程中,对内存的管理也不容忽视。如果稍微不注意,轻则,可能造成内存泄漏,重则造成内存访问异常。导致系统死机。 嵌入式产品,对稳定性要求及其严格。动不动就死机,那可就麻烦大了。以下,是我本人对嵌入式系统裸机编程的内存管理的一些简介。 1、尽量不使用库自带的malloc和free。 malloc和free在PC编程中是很好用的一种内存分配手段。但是,其在嵌入式中,就未必好用了。由于嵌入式裸机编程中,无MMU,即内存管理单元。无法实现对内存进行动态映射(不明白什么叫动态映射的同学,可以参考网上的资料)。 也就是说,实际上,malloc和free并不能实现动态的内存的管理。这需要在启动阶段专门给其分配一段空闲的内存区域作为malloc的内存区。如STM32中的启动文件startup_stm32f10x_md.s中可见以下信息: Heap_Size EQU 0x00000800 AREA HEAP, NOINIT, READWRITE, ALIGN=3__heap_baseHeap_Mem SPACE Heap_Size__heap_limit 其中,Heap_Size即定义一个宏定义。数值为 0x00000800。Heap_Mem则为申请一块连续的内存,大小为 Heap_Size。简化为C语言版本如下: #define Heap_Size 0x00000800unsigned char Heap_Mem[Heap_Size] = {0}; 在这里申请的这块内存,在接下来的代码中,被注册进系统中给malloc和free函数所使用: __user_initial_stackheapLDR R0, = Heap_Mem ; 返回系统中堆内存起始地址LDR R1, =(Stack_Mem + Stack_Size)LDR R2, = (Heap_Mem + Heap_Size); 返回系统中堆内存的结束地址LDR R3, = Stack_MemBX LR 就如上面分析的那样,其实,在裸机编程的时候,对堆内存的管理。并非是智能化的,并非你想申请多少就多少。而是使用一块固定的内存用作堆内存的分配。这在设计的时候,往往不是最佳的方案。这块内存,如果被多次按照不同的大小进行申请,就会造成内存碎片。最终导致无法申请到足够的内存。导致系统运行出错。这在原本内存就已经很少的嵌入式系统中,更是不能接受的。所以,建议是把那个Heap_Size设置成 0 吧。放弃其使用吧。 而更为致命的是,有些malloc,free函数,由于工程人员的偷懒。实现甚至可能如下: unsigned char mem_buffer[512];unsigned char *mem_offset = & mem_buffer;void *malloc(int size){    unsigned char *tmp = mem_offset;    mem_offset += size;    return (void *)tmp;}void free(void *mem){ mem_offset = mem;} 2、不用malloc、free的原因 一般单片机的内存都比较小,而且没有MMU,malloc 与free的使用容易造成内存碎片。而且可能因为空间不足而分配失败,从而导致系统崩溃,因此应该慎用,或者自己实现内存管理。如:《一个简单而强大的单片机内存管理器》 在函数中使用malloc,如果是大的内存分配,而且malloc与free的次数也不是特别频繁,使用malloc与free是比较合适的,但是如果内存分配比较小,而且次数特别频繁,那么使用malloc与free就有些不太合适了。 因为过多的malloc与free容易造成内存碎片,致使可使用的堆内存变小。尤其是在对单片机等没有MMU的芯片编程时,慎用malloc与free。如果需要对内存的频繁操作,可以自己实现一个内存管理。 使用动态内存分配,应分不同的应用场合。 对于在操作系统上运行的程序,实际的物理内存分配与释放使用操作系统来实现的,即使程序调用了 malloc和free物理内存并不会马上变化。物理内存的变化,直到系统的内存管理操作时才发生。 对于裸机跑在MCU上的程序,分配与释放内存都会造成实际物理内存的变化。因为此时物理内存的分配是由自己实现的,而内存管理我们自己并没有去做。这样,盲目的使用malloc与free恰恰并不好,反而会造成内存的不恰当使用。甚至于内存溢出。 所以,动态内存的使用前提是有一套好的内存管理方法,这样动态内存的使用才会合理使用内存。如果没有合适的内存管理代码,还是用静态内存好一些。 3、 更好的替代方案:内存池。 可能有些同学,觉得:内存池,这是什么东西? 内存池,简洁地来说,就是预先分配一块固定大小的内存。以后,要申请固定大小的内存的时候,即可从该内存池中申请。用完了,自然要放回去。注意,内存池,每次申请都只能申请固定大小的内存。这样子做,有很多好处: (1)每次动态内存申请的大小都是固定的,可以有效防止内存碎片化。(至于为什么,可以想想,每次申请的都是固定的大小,回收也是固定的大小) (2)效率高,不需要复杂的内存分配算法来实现。申请,释放的时间复杂度,可以做到O(1)。 (3)实现简单,易用。 (4)内存的申请,释放都在可控的范围之内。不会出现以后运行着,运行着,就再也申请不到内存的情况。 内存池,并非什么很厉害的技术。实现起来,其实可以做到很简单。只需要一个链表即可。在初始化的时候,把全局变量申请来的内存,一个个放入该链表中。在申请的时候,只需要取出头部并返回即可。在释放的时候,只需要把该内存插入链表。以下是一种简单的例子(使用移植来的linux内核链表,对该链表的移植,以后有时间再去分析): #define MEM_BUFFER_LEN  5    //内存块的数量#define MEM_BUFFER_SIZE 256 //每块内存的大小//内存池的描述,使用联合体,体现穷人的智慧。就如,我一同学说的:一个字节,恨不得掰成8个字节来用。typedef union mem {struct list_head list;unsigned char buffer[MEM_BUFFER_SIZE];}mem_t;static union mem gmem[MEM_BUFFER_LEN];LIST_HEAD(mem_pool);//分配内存void *mem_pop(){    union mem *ret = NULL;    psr_t psr;    psr = ENTER_CRITICAL();    if(!list_empty(&mem_pool)) { //有可用的内存池         ret = list_first_entry(&mem_pool, union mem, list);        //printf("mem_pool = 0x%p  ret = 0x%p\n", &mem_pool, &ret->list);        list_del(&ret->list); } EXIT_CRITICAL(psr); return ret;//->buffer;}//回收内存void mem_push(void *mem){    union mem *tmp = NULL;     psr_t psr;    tmp = (void *)mem;//container_of(mem, struct mem, buffer);    psr = ENTER_CRITICAL();    list_add(&tmp->list, &mem_pool);    //printf("free = 0x%p\n", &tmp->list);    EXIT_CRITICAL(psr);}//初始化内存池void mem_pool_init(){    int i;    psr_t psr;    psr = ENTER_CRITICAL();    for(i=0; i         list_add(&(gmem[i]. list), &mem_pool);          //printf("add mem 0x%p\n", &(gmem[i].list));  }  EXIT_CRITICAL(psr); } 整理来自: 1、https://blog.csdn.net/chenyuwen789/category_5823163.html 2、https://blog.csdn.net/c12345423/article/details/53004465 免责声明:本文来源网络,免费传达知识,版权归原作者所有。如涉及作品版权问题,请联系我进行删除。 最后 以上就是本次的分享,如果觉得文章不错,转发、在看,也是我们继续更新的动力。 猜你喜欢: 免责声明:本文内容由21ic获得授权后发布,版权归原作者所有,本平台仅提供信息存储服务。文章仅代表作者个人观点,不代表本平台立场,如有问题,请联系我们,谢谢!