• 深度剖析不一樣的Redis架構設計

    -      01、不一樣的Redis    - 提到Redis,大家一定會想到的幾個點是什麼呢? 高併發、KV存儲、內存數據庫、豐富的數據結構、單線程(版本6之前)等。 那麼,接下來,上面提到的這些,都會一一給大家解答,帶大家系統剖析一下Redis的架構設計魅力! -      02、為什麼會出現緩存?    - 一般情況下,數據都是在數據庫中,應用系統直接操作數據庫。當訪問量上萬,數據庫壓力增大,這個時候,怎麼辦呢? 有小夥伴會説,分庫分表、讀寫分離。的確,這些確實是解決比較高的訪問量的解決辦法,但是,如果訪問量更大,10萬,100萬呢?怎麼分似乎都不解決問題吧,所以我們需要用到其他辦法,來解決高併發帶來的數據庫壓力。 這個時候,緩存出現了,顧名思義,就是先把數據緩存在內存中一份,當訪問的時候,我們會先訪問內存的數據,如果內存中的數據不存在,這個時候,我們再去讀取數據庫,之後把數據庫中的數據再備份一份到內存中,這樣下次讀請求過來的時候,還是會直接先從內存中訪問,訪問到內存的數據了之後就直接返回了。這樣做就完美的降低了數據庫的壓力,可能十萬個請求進來,全部都訪問了內存中備份的數據,而沒有去訪問數據庫,或者説只有少量的請求訪問到了數據庫,這樣真的是大大降低了數據庫的壓力,而且這樣做也提高了系統響應,大家想一下,內存的讀寫速度是遠遠大於硬盤的讀寫速度的,一個請求進來讀取的內存可以比讀取硬盤快很多很多,用户的體驗也會很高。 -      03、什麼是緩存?    - 緩存原指CPU上的一種高速存儲器,它先於內存與CPU交換數據,速度很快。 現在泛指存儲在計算機上的原始數據的複製集,便於快速訪問。 在互聯網技術中,緩存是系統快速響應的關鍵技術之一。 -      04、緩存的三種讀寫模式    - 1、Cache Aside Pattern(常用) Cache Aside Pattern(旁路緩存),是最經典的緩存+數據庫讀寫模式。 讀的時候,先讀緩存,緩存沒有的話,就讀數據庫,然後取出數據後放入緩存,同時返回響應。 更新的時候,先更新數據庫,然後再刪除緩存。 為什麼是刪除緩存,而不是更新緩存呢? 1、緩存的值是一個結構,hash、list等更新數據需要遍歷; 2、懶加載,使用的時候才更新緩存,也可以採用異步的方式填充緩存。 高併發髒讀的三種情況: 1、先更新數據庫,在更新緩存; update與commit之間,更新緩存,commit失敗,則DB與緩存數據不一致。 2、先刪除緩存,再更新數據庫 update與commit之間,有新的讀,緩存空,讀DB數據到緩存,數據是舊的數據; commit後DB為新的數據; 則DB與緩存數據不一致。 3、先更新數據庫,再刪除緩存(推薦) update與commit之間,有新的讀,緩存空,讀DB數據到緩存,數據是舊的數據; commit後DB為新的數據; 則DB與緩存數據不一致; 採用延時雙刪策略。 2、Read/Write Through Pattern 應用程序只操作緩存,緩存操作數據庫; Read-Through(穿透讀模式/直讀模式):應用程序讀緩存,緩存沒有,由緩存回源到數據庫,並寫入緩存; Write-Through(穿透寫模式/直寫模式):應用程序寫緩存,緩存寫數據庫。該種模式需要提供數據庫的handler,開發較為複雜。 3、Write Behind Caching Pattern 應用程序只更新緩存; 緩存通過異步的方式將數據批量或合併後更新到DB中,不能時時同步,甚至會丟數據。 -      05、Redis又是什麼?    - Redis是一個高性能的開源的,C語言寫的NoSQL(非關係型數據庫)也叫做緩存數據庫,數據保存在內存中。Redis是以key-value形式存儲,和傳統的關係型數據庫不一樣。不一定遵循傳統數據庫的那些基本要求。比如,不遵循SQL標準、事務、表結構等。Redis有非常豐富的數據類型,比如String,list,set,zset,hash等。 -      06、Redis可以做什麼?    - 1、減輕數據庫壓力,提高併發量,提高系統響應時間 2、做Session分離 傳統的Session是由自己的tomcat進行維護和管理的,在集羣和分佈式情況下,不同的tomcat要管理不同的session,只能在各個tomcat之間,通過網絡和IO進行session複製,極大的影響了系統的性能。 Redis解決了這一個問題,將登陸成功後的session信息,存放在Redis中,這樣多個tomcat就可以共享Session信息。 3、做分佈式鎖 一般Java中的鎖都是多線程鎖,是在一個進程中的,多個進程在併發的時候也會產生問題,也要控制時序性,這個時候Redis可以用來做分佈式鎖,使用Redis的setnx命令來實現。 4、電商購物車: 1、以用户id為key 2、商品id為field 3、商品數量為value 電商購物車操作: 1、添加商品:hset cart:1001 10088 1 2、增加數量:hincrby cart:1001 10088 1 3、商品總數:hlen cart:1001 4、刪除商品:hdel cart:1001 10088 5、獲取購物車所有商品:hgetall cart:1001 5、zset集合操作實現排行榜 1、點擊新聞 ZINCRBY hotNews:20190819 1 守護香港 2、展示當日排行前十 ZREVRANGE hotNews:20190819 0 9 WITHSCORES 3、七日搜索榜單計算 ZUNIONSTORE hotNews:20190813-20190819 7 hotNews:20190813 hotNews:20190814... hotNews:20190819 4、展示七日排行前十 ZREVRANGE hotNews:20190813-201908109 0 9 WITHSCORES 用Redis做緩存,有這麼有多優點,那麼,缺點是不是也會對應的有很多呢? 1、額外的硬件支出 緩存是一種軟件系統中以空間換時間的技術,需要額外的磁盤空間和內存空間來存儲數據。 2、高併發緩存失效 在高併發的情況下,會出現緩存失效(緩存穿透,緩存雪崩,緩存擊穿等問題)造成瞬間數據庫訪問量增大,甚至崩潰,所以這些問題是一定要去解決。 3、緩存與數據庫數據同步 緩存與數據庫無法做到數據的實時同步。 4、緩存併發競爭 多個Redis客户端同時對一個key進行set值的時候由於執行順序引起的併發的問題。 -      07、Redis高性能設計    - 1、Redis是單線程的麼? Redis的單線程主要是指Redis的網絡IO和鍵值對讀寫是由一個線程來完成的,這也是Redis對外提供鍵值存儲服務的主要流程。但Redis的其他功能,比如持久化,異步刪除,集羣數據同步等,都是由額外的線程執行的。 2、Redis單線程為什麼還能這麼快? 這裏我們在本地測試一下Redis支持的併發。 執行這條命令:./redis-benchmark get結果:============ get ==========100000 requests completed in 1.02 seconds50 parallel clients3 bytes payloadkeep alive: 1host configuration "save": 900 1 300 10 60 10000host configuration "appendonly": nomulti-thread: no0.00%

    時間:2020-11-22 關鍵詞: Redis 嵌入式

  • 技術分享:某公司用6年的通用的權限管理系統設計方案

    作者:PioneerYi  來源:juejin.im/post/6850037267554287629 一個系統,如果沒有安全控制,是十分危險的,一般安全控制包括身份認證和權限管理。用户訪問時,首先需要查看此用户是否是合法用户,然後檢查此用户可以對那些資源進行何種操作,最終做到安全訪問。身份認證的方式有很多種,最簡單的就是直接用户名密碼,還有業內比較通用的方式CAS方式登陸等;授權的框架也很多,比如OAuth2,Shiro等。本文首先會講解一下CAS的概念,以及基於角色的權限管理模型(RBAC)的概念,接着進行數據表的設計,最後講解如何利用Shiro進行權限管理。 一、CAS身份認證 集中式認證服務(英語:Central Authentication Service,縮寫CAS)是一種針對萬維網的單點登錄協議。它的目的是允許一個用户訪問多個應用程序,而只需提供一次憑證。 1.1、名詞概念 CAS的核心就是其Ticket,及其在Ticket之上的一系列處理操作。CAS的主要票據有TGT、ST、PGT、PGTIOU、PT,其中TGT、ST是CAS1.0(基礎模式)協議中就有的票據,PGT、PGTIOU、PT是CAS2.0(代理模式)協議中有的票據。這些票據誰生成得了?肯定是有相關服務的,主要服務有:KDC,AS,TGS。兩者媒介肯定也是有的:TGC。 1.1.1、CAS的Ticket 1)TGT(Ticket Granting Tieckt) TGT是CAS(具體為 KDC 的 AS 發放)為用户簽發的登錄票據,擁有了TGT,用户就可以證明自己在CAS成功登錄過。TGT封裝了Cookie值以及此Cookie值對應的用户信息。用户在CAS認證成功後,CAS生成cookie,寫入瀏覽器,同時生成一個TGT對象,放入自己的緩存,TGT對象的ID就是cookie的值。當HTTP再次請求到來時,如果傳過來的有CAS生成的cookie,則CAS以此cookie值為key查詢緩存中有無TGT ,如果有的話,則説明用户之前登錄過,如果沒有,則用户需要重新登錄。 簡而言之,即獲取這樣一張票據後,以後申請各種其他服務票據 (ST) 便不必再向 KDC 提交身份認證信息 ( 準確術語是 Credentials) 。 2)ST(Service ticket) ST是CAS(由 KDC 的 TGS 發放)為用户簽發的訪問某一service的票據。 用户訪問service時,service發現用户沒有ST,則要求用户去CAS獲取ST。用户向CAS發出獲取ST的請求,如果用户的請求中包含cookie,則CAS會以此cookie值為key查詢緩存中有無TGT,如果存在TGT,則用此TGT簽發一個ST,返回給用户。用户憑藉ST去訪問service,service拿ST去CAS驗證,驗證通過後,允許用户訪問資源。 任何一台 Workstation 都需要擁有一張有效的 Service Ticket 才能訪問域內部的應用 (Applications) 。如果能正確接收 Service Ticket ,説明在 CASClient-CASServer 之間的信任關係已經被正確建立起來。 3)PGT(Proxy Granting Ticket) Proxy Service的代理憑據。用户通過CAS成功登錄某一Proxy Service後,CAS生成一個PGT對象,緩存在CAS本地,同時將PGT的值(一個UUID字符串)回傳給Proxy Service,並保存在Proxy Service裏。Proxy Service拿到PGT後,就可以為Target Service(back-end service)做代理,為其申請PT。 4)PT(Proxy Ticket) PT是用户訪問Target Service(back-end service)的票據。如果用户訪問的是一個Web應用,則Web應用會要求瀏覽器提供ST,瀏覽器就會用cookie去CAS獲取一個ST,然後就可以訪問這個Web應用了。如果用户訪問的不是一個Web應用,而是一個C/S結構的應用,因為C/S結構的應用得不到cookie,所以用户不能自己去CAS獲取ST,而是通過訪問proxy service的接口,憑藉proxy service的PGT去獲取一個PT,然後才能訪問到此應用。 TGT、ST、PGT、PT之間關係的總結 1:ST是TGT簽發的。用户在CAS上認證成功後,CAS生成TGT,用TGT簽發一個ST,ST的ticketGrantingTicket屬性值是TGT對象,然後把ST的值redirect到客户應用。 2:PGT是ST簽發的。用户憑藉ST去訪問Proxy service,Proxy service去CAS驗證ST(同時傳遞PgtUrl參數給CAS),如果ST驗證成功,則CAS用ST簽發一個PGT,PGT對象裏的ticketGrantingTicket是簽發ST的TGT對象。 3:PT是PGT簽發的。Proxy service代理back-end service去CAS獲取PT的時候,CAS根據傳來的pgt參數,獲取到PGT對象,然後調用其grantServiceTicket方法,生成一個PT對象。 1.1.2、CAS的服務 CAS的主要服務有: KDC(Key Distribution Center ); AS(Authentication Service)它索取 Crendential,發放 TGT; TGS(Ticket  Granting Service),索取 TGT ,發放 ST。 1.1.3、CAS的媒介 TGC(Ticket Granting Cookie) 存放用户身份認證憑證的cookie,在瀏覽器和CAS Server間通訊時使用,並且只能基於安全通道傳輸(Https),是CAS Server用來明確用户身份的憑證。 1.2、CAS工作原理 CAS的單點登錄的認證過程,所用應用服務器受到應用請求後,檢查ST和TGT,如果沒有或不對,轉到CAS認證服務器登錄頁面,通過安全認證後得到ST和TGT,再重新定向到相關應用服務器,在回話生命週期之內如果再定向到別的應用,將出示ST和TGT進行認證,注意,取得TGT的過程是通過SSL安全協議的。 從網上找了一個比較專業又比較詳細的CAS工作原理流程圖: CAS流程 專業版可能比較晦澀難懂,來個通俗版。通俗形象地説就是:相當於用户要去遊樂場,首先要在門口檢查用户的身份 ( 即 CHECK 用户的 ID 和 PASS), 如果用户通過驗證,遊樂場的門衞 (AS) 即提供給用户一張門卡 (TGT)。 這張卡片的用處就是告訴遊樂場的各個場所,用户是通過正門進來,而不是後門偷爬進來的,並且也是獲取進入場所一把鑰匙。 現在用户有張卡,但是這對用户來不重要,因為用户來遊樂場不是為了拿這張卡的而是為了遊覽遊樂項目,這時用户摩天樓,並想遊玩。 這時摩天輪的服務員 (client) 攔下用户,向用户要求摩天輪的 (ST) 票據,用户説用户只有一個門卡 (TGT), 那用户只要把 TGT 放在一旁的票據授權機 (TGS) 上刷一下。 票據授權機 (TGS) 就根據用户現在所在的摩天輪,給用户一張摩天輪的票據 (ST), 這樣用户有了摩天輪的票據,現在用户可以暢通無阻的進入摩天輪裏遊玩了。 當然如果用户玩完摩天輪後,想去遊樂園的咖啡廳休息下,那用户一樣只要帶着那張門卡 (TGT). 到相應的咖啡廳的票據授權機 (TGS) 刷一下,得到咖啡廳的票據 (ST) 就可以進入咖啡廳 當用户離開遊樂場後,想用這張 TGT 去刷打的回家的費用,對不起,用户的 TGT 已經過期了,在用户離開遊樂場那刻開始,用户的 TGT 就已經銷燬了 。 二、基於角色的權限管理模型 在業界接受度較高的權限模型是RBAC(Role-Based Access Control),基本的概念是將“角色”這個概念賦予用户,在系統中用户通過分配角色從而獲得相應的權限,一個用户可以有多個角色,一個角色可以有多個權限,從而實現權限的靈活配置。 2.1、基本的RBAC模型 最基本的RBAC模型,就是由“用户”,“角色”以及“權限”這三個主體組成,一個用户可以有多個角色,一個角色可以有多個權限,他們之間的關係可以是多對一關係,也可以是多對多關係。 用户角色權限關係 2.2、引入用户組的RBAC模型 如果用户數量比較龐大,新增一個角色時,需要為大量用户都重新分配一遍新的角色,工程量巨大,此時可以引入用户組的概念。如果部分用户的使用場景是相對一致和基礎的,可以把這些用户打包成一個組,基於這個組的對象進行角色和權限的賦予。最終用户擁有的所有權限 = 用户個人擁有的權限+該用户所在用户組擁有的權限。 2.3、角色分級的RBAC模型 在一些業務場景中,上層角色需要繼承下層角色的全部權限,此時則需要使用角色繼承的RBAC模型。此時除了對角色進行定義,還需要管理角色間的關係,通過關係來體現角色的層級關係,從而達到繼承權限的效果。角色的繼承關係主有兩種:樹形圖和有向無環圖。 角色繼承模式 繼承關係常常來源於公司團隊的組織架構,此時常常將角色與組織結構進行關聯達到繼承角色模型的效果。 2.4、角色限制的RBAC模型 在一些產品或系統中,部分角色可能是需要隔離的、不允許被同時賦予一個人的,比如不能既是運動員又是裁判員。因此,有些角色存在互拆關係。此外,限制還可能是數量上的,比如某個產品組中有且只有一個管理員,不允許刪除或再分配其他管理員。 根據不同的業務需求,限制的形式很多,需要注意的是不能僅僅依賴後段限制,而是要在前端展示清晰的規則和恰當的限制,避免用户出錯。 2.5、權限管理的基本元素 權限管理的基本元素為:用户,角色,資源,操作,權限。 1、用户 應用系統的具體操作者,用户可以自己擁有權限信息,可以歸屬於0~n個角色,可屬於0~n個組。他的權限集是自身具有的權限、所屬的各角色具有的權限、所屬的各組具有的權限的合集。它與權限、角色、組之間的關係都是n對n的關係。 2、用户組(可選) 為了更好地管理用户,對用户進行分組歸類,簡稱為用户分組。組也具有上下級關係,可以形成樹狀視圖。在實際情況中,我們知道,組也可以具有自己的角色信息、權限信息。 3、角色 為了對許多擁有相似權限的用户進行分類管理,定義了角色的概念,例如系統管理員、管理員、用户、訪客等角色。角色具有上下級關係,可以形成樹狀視圖,父級角色的權限是自身及它的所有子角色的權限的綜合。父級角色的用户、父級角色的組同理可推。 4、資源 權限管理的一個單元實體對象,我們廣義的稱之為資源,可以是一個人,也可以是一個產品,一個文件,一個頁面 等等。5、操作 對資源進行的實際操作,比如讀、寫、編輯等等。 6、權限 資源+操作,構成一個權限控制點。 對象間的關係包括: 是否關係 繼承關係 限制關係(互斥、範圍限制、邊界限制、字段限制) 三、數據表設計 按照RBAC模型,數據庫可以這樣設計: 1、產品表(t_product_info) 字段名稱 字段 類型 備註 產品Id pro_id int(11) 自增 產品名稱(英) name_en varchar(50) not null 產品名層(中) name_ch varchar(50) not null 創建人 creator varchar(50) not null 所屬人 owner varchar(50) not null 描述 description varchar(255) 創建時間 create_time timestamp not null 2、產品成員表(t_product_member) 字段名稱 字段 類型 備註 記錄ID id int(11) 自增 產品ID pro_id int(11) fk:t_produck_info 成員ID member_id int(11) not null 創建時間 create_time timestamp not null 3、用户信息表(t_user_info) 字段名稱 字段 類型 備註 用户ID user_id int(11) not null 英文名 nike_name varchar(10) not null 中文名 real_name varchar(10) not null 創建時間 create_time timestamp not null 更新時間 update_time timestamp not null 4、用户角色表(t_user_role) 字段名稱 字段 類型 備註 記錄ID id int(11) 自增 用户ID user_id int(11) not null 角色ID role_id varchar(50) not null 創建時間 create_time timestamp not null 5、角色表(t_role) 字段名稱 字段 類型 備註 記錄ID id int(11) 自增 角色ID role_id varchar(50) not null,比如:A~USER 角色名稱 role_name varchar(50) not null 對象 object varchar(50) not null 對象ID object_id varchar(50) not null 角色備註 comment varchar(255) 創建時間 create_time timestamp not null 6、基礎角色表(t_role_base) 字段名稱 字段 類型 備註 記錄ID id int(11) 自增 角色ID role_id varchar(50) not null,比如:A~USER 角色名稱 role_name varchar(50) not null 角色備註 comment varchar(255) 權限 permission varchar(255) not null 創建時間 create_time timestamp not null 7、角色權限表(t_role_permission) 字段名稱 字段 類型 備註 記錄ID id int(11) 自增 角色ID role_id varchar(50) fk:t_role->role_id 權限 permission varchar(255) not null 基礎角色ID role_base_id varchar(50) fk:t_role_base->role_id 創建時間 create_time timestamp not null 8、用户組表(t_user_group,可選) 字段名稱 字段 類型 備註 組ID id int(11) 自增 組名稱 group_name varchar(50) not null 組描述 group_desc varchar(255) not null 創建時間 create_time timestamp not null 9、組角色表(t_user_group_role,可選) 字段名稱 字段 類型 備註 記錄ID id int(11) 自增 組ID role_id varchar(50) not null 角色ID role_id varchar(255) not null 創建時間 create_time timestamp not null 10、用户權限表(t_user_permission,可選) 字段名稱 字段 類型 備註 記錄ID id int(11) 自增 用户ID role_id varchar(50) not null 權限 permission varchar(255) not null 創建時間 create_time timestamp not null 四、角色及權限點設計 權限控制的整個過程可以描述為:“誰”對“什麼”進行什麼”操作",從而,引出我們需要做的工作有:角色設計,資源定義,以及對資源的操作定義。再詳細描述下,鑑權就是根據用户身份(角色)獲得其對那些資源,可以進行什麼操作,其中對資源的操作做為一個獨立的權限體。 4.1、定義系統中的用户角色 一般是採用“通用角色+實例角色”的模式,實例角色可繼承通用角色,從而擁有通用角色的權限。 常見的通用角色定義:ADMIN、MANAGER、MEMBER、GUEST 常見角色權限分配:1)SUPER_ADMIN,具有系統一切權限 1)產品ADMIN,具有當前產品所有權限;2)產品MANAGER,不具備刪除權限,可修改,添加成員等 3)產品MEMEBER,可查看,修改信息,不可添加成員;4)產品GUEST,只可查看 實例角色:實例角色一般可以這樣定義:“資源點+通用角色+資源ID” 注:其中資源可能是產品,可能是頁面,也可能是菜單等 4.2、定義系統中的資源以及操作 一般系統中的最常見資源就是:產品(P) 一般對資源的主要操作包括:增加(CREATE)、刪除(DELETE)、修改(EDIT)、查看(VIEW) 當然,系統中的資源肯定不止產品,同時產品這個粒度有些太粗,還可以更細化控制,當然一切都根據實際業務需求情況定義相應的資源點和操作。 4.3、權限體策略 權限控制策略採用五元組,如下: @Overrideprotected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token){}複製代碼 上面這個接口,參數AuthenticationToken,它可以自定義子類實現,框架提供的有:UsernamePasswordToken,CasToken。 如果是採用用户名密碼方式登陸,那麼就構造一個UsernamePasswordToken,然後取數據查詢用户名密碼是否有效;如果是採用的CAS方式登陸,那麼就通過ticket構造一個CasToken,然後與CAS服務交互驗證ticket是否有效。如果驗證通過會生成一個AuthenticationInfo。此時身份認證完成。 最簡單的用户密碼登陸身份校驗代碼 CAS方式驗證首先得有CAS系統,這裏就不説明CAS方式怎麼驗證了,説一下怎麼用用户密碼登陸進行身份校驗,認證流程都一樣 自定義一個AuthenticatingRealm: //獲取用户身份信息,Authorization前需要先獲取用户身份信息@Overrideprotected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token){}//獲取用户權限信息@Overrideprotected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection){}複製代碼 權限信息查詢過程一般為:1)從數據庫中讀區用户自身所配權限;2)從數據庫中讀取用户角色所用擁有的權限(角色包含實例角色和BASE角色) 3)用户最終的權限:用户自身權限+用户角色權限 2、第二步:權限校驗 1)如果通過角色校驗,則調用hasRole,與傳入的角色比較即可; 2)如果通過權限體校驗,則調用isPermitted(...),與傳入的權限進行比較即可。 shiro內部邏輯如下:首先通過PermissionResolver將權限字符串轉換成相應的Permission實例,默認使用WildcardPermissionResolver,即轉換為通配符的WildcardPermission;接着調用Permission.implies(Permission p)逐個與傳入的權限比較,如果有匹配的則返回true,否則false。 六、參考資料 //shiro.apache.org/ //github.com/apache/shiro //jinnianshilongnian.iteye.com/blog/2018398 //my.oschina.net/bochs/blog/2248956 //blog.itning.top/posts/Essays/20190408-A-brief-explanation-of-single-sign-on-SSO-and-centralized-authentication-service-CAS- 長按訂閲更多精彩▼如有收穫,點個在看,誠摯感謝 免責聲明:本文內容由21ic獲得授權後發佈,版權歸原作者所有,本平台僅提供信息存儲服務。文章僅代表作者個人觀點,不代表本平台立場,如有問題,請聯繫我們,謝謝!

    時間:2020-11-22 關鍵詞: 計算機 嵌入式

  • 編寫 if 時不帶 else,你的代碼會更好!

    來源:翻譯自:Nicklas Millard的文章《Better Software Without If-Else》 設計更好的軟件,替換If-Else的5種方法。入門到高級示例 “ 讓我直接説這句話:If-Else通常是一個糟糕的選擇。 ” 它導致設計複雜,代碼可讀性差,並且可能導致重構困難。 但是,If-Else已成為事實上的代碼分支解決方案,這確實是有道理的。這是向所有有抱負的開發人員講授的第一件事。不幸的是,許多開發人員從來沒有前進到更合適的分支策略。 有些人的口頭禪是:If-Else是一把錘子,一切都是釘子。 無法區分何時使用更合適的方法是區分大三學生和大三學生的原因之一。 我將向您展示一些技巧和模式,這些技巧和模式將終結這種可怕的做法。 每個示例的難度都會增加。 1 完全不必要的Else塊 這也許是那些初級開發人員最負罪的之一。下面的示例很好地説明了當您被認為If-Else很棒時會發生什麼。 > Simple if-else 只需刪除else`塊即可簡化此過程。 > Removed else 看起來更專業吧? 您會經常發現,實際上根本不需要其他塊。像在這種情況下一樣,您想要在滿足特定條件的情況下執行某些操作並立即返回。 2 價值分配 如果您要根據提供的某些輸入為變量分配新值,請停止If-Else廢話-一種更具可讀性的方法。 > Value assignment with if-else 儘管很簡單,但它卻很糟糕。首先,If-Else很容易在這裏被開關取代。但是,我們可以通過完全刪除else來進一步簡化此代碼。 > If statements with fast return 如果不使用else,則我們將剩下乾淨的可讀代碼。請注意,我也將樣式更改為快速返回而不是單返回語句-如果已經找到正確的值,繼續測試一個值根本沒有意義。 3 前提條件檢查 通常,我發現,如果方法提供了無效的值,則繼續執行是沒有意義的。假設我們從以前就有了DefineGender方法,要求提供的輸入值必須始終為0或1。 > Method without value checks 在沒有價值驗證的情況下執行該方法沒有任何意義。因此,在允許方法繼續執行之前,我們需要檢查一些先決條件。 應用保護子句防禦性編碼技術,您將檢查方法的輸入值,然後繼續執行方法。 > Check preconditions with guard clauses 至此,我們確保僅在值落在預期範圍內時才執行主邏輯。 現在,IF也已被三元代替,因為不再需要在結尾處默認返回"未知"。 4 將If-Else轉換為字典—完全避免If-Else 假設您需要執行一些操作,這些操作將根據某些條件進行選擇,我們知道以後必須添加更多操作。 也許有人傾向於使用久經考驗的If-Else。如果添加新操作,則只需簡單地添加其他內容即可。很簡單 但是,就維護而言,這種方法不是一個好的設計。 知道我們以後需要添加新的操作後,我們可以將If-Else重構為字典。 可讀性已大大提高,並且可以更輕鬆地推斷出該代碼。 注意,僅出於説明目的將字典放置在方法內部。您可能希望從其他地方提供它。 5 擴展應用程序—完全避免使用If-Else 這是一個稍微高級的示例。 通過用對象替換它們,知道何時甚至完全消除If。 通常,您會發現自己不得不擴展應用程序的某些部分。作為初級開發人員,您可能會傾向於通過添加額外的If-Else(即else-if)語句來做到這一點。 舉這個説明性的例子。在這裏,我們需要將Order實例顯示為字符串。首先,我們只有兩種字符串表示形式:JSON和純文本。在此階段使用If-Else並不是什麼大問題,如果我們可以輕鬆替換其他,只要如前所述即可。 知道我們需要擴展應用程序的這一部分,這種方法絕對是不可接受的。 上面的代碼不僅違反了"打開/關閉"原則,而且閲讀得不好,還會引起可維護性方面的麻煩。 正確的方法是遵循SOLID原則的方法-我們通過實施動態類型發現過程(在本例中為策略模式)來做到這一點。 重構這個混亂的過程的過程如下: 使用公共接口將每個分支提取到單獨的策略類中 動態查找實現通用接口的所有類 根據輸入決定執行哪種策略 替換上面示例的代碼如下所示。是的,這是更多代碼的方式。它要求您瞭解類型發現的工作原理。但是動態擴展應用程序是一個高級主題。 我只顯示將替換If-Else示例的確切部分。如果要查看所有涉及的對象,請查看此要點。 讓我們快速瀏覽一下代碼。 方法簽名保持不變,因為調用者不需要了解我們的重構。 首先,獲取實現通用接口IOrderOutputStrategy的程序集中的所有類型。然後,我們建立一個字典,格式化程序的displayName的名稱為key,類型為value。 然後從字典中選擇格式化程序類型,然後嘗試實例化策略對象。 最後,調用策略對象的ConvertOrderToString。 作者介紹 Nicklas Millard在丹麥的四大諮詢公司之一中擔任高級技術顧問。他主要擔任客户項目的首席開發人員和解決方案架構師。 他一直在為商業客户和政府機構開發軟件,例如國防部,教育部,丹麥環境與食品部,國家警察,丹麥勞動力市場和招聘局以及rstad。 在LinkedIn上連接 “ (本文翻譯自Nicklas Millard的文章《Better Software Without If-Else》,參考://medium.com/swlh/5-ways-to-replace-if-else-statements-857c0ff19357) ” 特別推薦一個分享架構+算法的優質內容,還沒關注的小夥伴,可以長按關注一下: 長按訂閲更多精彩▼如有收穫,點個在看,誠摯感謝 免責聲明:本文內容由21ic獲得授權後發佈,版權歸原作者所有,本平台僅提供信息存儲服務。文章僅代表作者個人觀點,不代表本平台立場,如有問題,請聯繫我們,謝謝!

    時間:2020-11-22 關鍵詞: 代碼 嵌入式

  • 面試求職必勝法寶

    大家好,我是光城,目前研三,在秋招提前批中拿到不低於5個offer,有3個是ssp的offer,幾個sp的,當然還有一些是白菜,同時也有很多是失敗的,在找工作期間,沒有太多的時間去反思與成長,我的秋招開始於5/6月份字節最早那一批,那時候還在鵝廠實習,在整個秋招提前批階段根本沒有太多的時間去做充足的準備,説的比較直白一點大部分廠的面試是裸面的,所以一些通過的算是之前的實力+運氣成分吧,沒有通過的有自己實力不足與其他方面原因。 找工作,我們可以在做好相關的備戰工作,以至於不會在找工作的時候無頭蒼蠅,不知道複習什麼,該怎麼準備等。舉個很現實的例子:我第一次面試字節的時候,根本不知道怎麼準備,例如:算法?考啥算法?操作系統?深度是什麼樣子呢?像這些問題如果準備的很短,其實面試官是可以直戳淚點的,相信一些失敗的同學肯定遇到過。下面我將聊一聊自己面試中的一些經驗談吧。 1.早投遞,佔據時間優勢 這一點是非常重要的,當大家看到跟自己的崗位匹配時,應當先思考一下自己的實力,覺得不行就可以放棄了,其實我想説,如果這次投遞不走系統,可以直接投遞了,例如:走朋友或同學內推渠道,那麼你可以不走系統面試,不管通過不通過,這一點來説你已經比很多人佔據了時間優勢。今年因為疫情影響,大多公司採用線上面試,不同公司的秋招提前批有時候等價於以往的提前批,如果錯過了,那就有可能放棄了進入這家公司的機會!如果自己實力不算太差,有記錄的情況下可以直接投遞了,如果自己實力很差,沒記錄情況下也可以直接投遞了,佔據投遞時間因素也是一種機會。 2. 針對性複習 在面試過60+場的面試中,我個人覺得算法+計算機基礎便可以拿到不錯的工作,面試當中記錄一些不會的或者知識盲點,針對性的複習,效果往往很好,算法也是這樣,不同公司算法是有重合度的,大部分來自劍指offer與leetcode,leetcode300道絕對夠面試了,面試的時候套算法思路,跟面試官交流互動,當然還有一些實踐性的代碼,例如:手撕線程安全的單例、手撕多線程安全代碼、手撕智能指針等等,像這些實則考察你的代碼基本功,如果提前準備好,那面試可謂是送offer的。 認真把握每場面試機會,多記錄與總結,同時可以在牛客上看到自己面試公司的題目,可以認真刷一下,例如:我當時去面字節,就看了別人整理的字節題目,簡直牛逼爆炸,面試基本原題。 3.溝通的技巧 相信很多人會給求職者傳遞面試就是運氣成分,可是你知道運氣來自於哪裏嗎,其中之一在於溝通的技巧,在跟面試官溝通問題以及説話口吻方面要學習一下,不然很容易涼涼的,例如:一些問題自己不會要謙虛的請教或者學習等等的態度,讓面試官認為你是一個熱愛學習積極主動的人,而不是一個浮躁不堪的人。 4.簡歷 簡歷是一個人的門面,裏面應該比較針對性的應對自己應聘的崗位,而不是五穀雜糧,例如:你要應聘C++開發,你寫了一堆Python項目,一堆測試項目,凸顯不出自己的實力與能力,針對性的項目寫在簡歷上,簡歷是一個很大的話題,後面將會詳細闡述。 最後,許久不見,沒有來跟大家及時分享一些學習及成長心得,期待大家督促及相互交流。 往期推薦 免責聲明:本文內容由21ic獲得授權後發佈,版權歸原作者所有,本平台僅提供信息存儲服務。文章僅代表作者個人觀點,不代表本平台立場,如有問題,請聯繫我們,謝謝!

    時間:2020-11-22 關鍵詞: 互聯網 程序員

  • 全國大學生智能汽車競賽組委會技術研討會在山東大學召開

    今天,全國大學生智能車競賽祕書處與來自全國二十多位長期參與競賽組織、指導隊伍參賽的一線高校教師在山東大學舉行了為期一天的智能車競賽技術研討會。 在會上就即將開始的第十六屆全國大學生智能汽車競賽相關賽事組織工作進行了深入的探討和交流。這為下週末舉行的第十六屆全國大學生智能汽車競賽第一次擴大會議相關議題做好準備。 會議一開始聽取了競賽祕書處就新的一屆競賽籌備情況的介紹,包括競賽賽題內容就緒情況、承辦學校落實情況、贊助企業合作情況等。 接着展開了對新一屆賽題內容的討論。賽題分為競速組別和人工智能提高組別兩大類。 競速組別本着面向本科工科學生知識範圍,賽題內容難易有度、繼承與創新兼顧,分別從車模平台、賽道元素、比賽任務、嵌入式控制平台等方面進行設計更新。 與會專家與競賽祕書處逐一對於各項賽題細節進行討論,完善賽題內容,去除不合理的要求,理清每個賽題考察的知識點和技能要求,使的賽題各具特色,適合不同階段學生參賽。 相比於往屆比賽,今年賽題最大的不同就是嵌入式微控制器平台可選的的種類更加的豐富。這也要求參賽同學在嵌入式系統學習與設計實踐中,能夠到達舉一反三、融會貫通,根據系統具體進行硬軟件的合理剪裁,這也體現了迅速發展的電子技術的特點。 為了幫助參賽同學更好的掌握核心單片機開發技術,競賽組委會也將與合作企業技術人員做好相關硬件設計工具和軟件開發平台的準備。彙總相關開發文檔資料,製作技術學習視頻課程,組織線上線下培訓活動等,減少參賽同學在單片機學習中的壁壘。 在討論競速賽題之後,大家對AI創意組別的軟件和硬件平台的選定進行了討論。由於這個競賽大類涉及到的算法複雜度、硬件平台的價格都較高。如何做到普及與提高相融、公平與激勵兼顧等難題,大家積極獻策,為競賽祕書處與合作贊助企業之間更好的合作提供相應的方案。 會議還對競賽組織流程的優化,比賽獎項的設置,競賽相關培訓課程開發,專科學校學生參賽賽題設置,留學生組隊進入國賽條件進行了討論。 特別是,參會老師還對近些年來競賽實踐過程中出現的一些不好的現象,比如存在個別弄虛作假現象等進行了梳理,並給出了相應對策建議,最大限度保障大賽的公平公正。競賽組委會將會在下週公佈競速組比賽規則草案,在吸收老師同學們反饋意見之後,於下週末在第十六屆智能車競賽第一次擴大會議中進行討論,隨後進行正式公佈。人工智能提高組別的規則將會再晚幾周進行公佈。 最後,感謝這個週末參與競賽技術研討的各位老師辛勤的工作。 免責聲明:本文內容由21ic獲得授權後發佈,版權歸原作者所有,本平台僅提供信息存儲服務。文章僅代表作者個人觀點,不代表本平台立場,如有問題,請聯繫我們,謝謝!

    時間:2020-11-22 關鍵詞: 汽車電子 AI 自動駕駛

  • 猜猜看,你的健康狀況監護儀中有些啥?

    免責聲明:本文內容由21ic獲得授權後發佈,版權歸原作者所有,本平台僅提供信息存儲服務。文章僅代表作者個人觀點,不代表本平台立場,如有問題,請聯繫我們,謝謝!

    時間:2020-11-22 關鍵詞: 醫療電子 AI

  • 最新!中移合作伙伴大會現場圖集

    11月19-21日,中國移動2020全球合作伙伴大會在廣州拉開帷幕。 今年正值中國移動成立20週年,本屆大會以"5G融入百業 數智引領未來"為主題,邀請行業專家及合作伙伴齊聚廣州,為世界描繪出一幅全方位、立體化、實景化的5G+時代藍圖。 小棗君今年首次參加該會議,按照慣例,給大家奉上現場的最新照片。 —— The End —— 照片全部由小棗君現場拍攝,保留權利 免責聲明:本文內容由21ic獲得授權後發佈,版權歸原作者所有,本平台僅提供信息存儲服務。文章僅代表作者個人觀點,不代表本平台立場,如有問題,請聯繫我們,謝謝!

    時間:2020-11-22 關鍵詞: 通信 5G

  • 創建屬於自己的系統信息,往proc中留下一個腳印

    點擊上方「嵌入式大雜燴」,「星標公眾號」第一時間查看嵌入式筆記! 上一篇:《文件系統有很多,但這幾個最為重要》介紹了procfs(進程文件系統的縮寫),包含一個偽文件系統(啓動時動態生成的文件系統),用於通過內核訪問進程信息。這個文件系統通常被掛載到 /proc 目錄, /proc中不僅僅放了進程相關信息,也存放着很多系統相關的信息。 這些信息都是內核開放給用户的,/proc 就是用户與內核直接交互的一個入口。從內核的角度看,內核是通過怎麼樣的方式把這些信息暴露給用户呢?這篇筆記我們來學習一下: 內核創建proc節點的例子 我們先來看一個例子(Linux-4.9.88\fs\proc\cpuinfo.c): 這就是創建/proc下cpuinfo這個節點的相關代碼,有了cpuinfo節點,我們就可以通過訪問這個節點來得到cpu的一些信息: 從以上代碼中,我們可以看到,其用proc_create這個函數來創造相關節點的,這個函數是一個內聯函數,存放在Linux-4.9.88\include\linux\proc_fs.h下: static inline struct proc_dir_entry *proc_create( const char *name, umode_t mode, struct proc_dir_entry *parent, const struct file_operations *proc_fops){ return proc_create_data(name, mode, parent, proc_fops, NULL);} 知識點:什麼是內聯函數? 內聯函數簡單來説就是編譯器將指定的函數體插入並取代每一處調用該函數的地方上下文,從而節省了每次調用函數帶來的額外時間開支。 一般用於能夠快速執行的函數,因為在這種情況下函數調用的時間消耗顯得更為突出。這種方法對於很小的函數也有空間上的益處,並且它也使得一些其他的優化成為可能。 這麼一看,似乎與宏有點相似?與宏有何不同? 宏調用並不執行類型檢查,甚至連正常參數也不檢查,但是函數調用卻要檢查。 C語言的宏使用的是文本替換,可能導致無法預料的後果,因為需要重新計算參數和操作順序。 在宏中的編譯錯誤很難發現,因為它們引用的是擴展的代碼,而不是程序員鍵入的。 許多結構體使用宏或者使用不同的語法來表達很難理解。內聯函數使用與普通函數相同的語言,可以隨意的內聯和不內聯。 內聯代碼的調試信息通常比擴展的宏代碼更有用。 以上介紹摘選自百度百科,關於內聯更詳細的介紹可自行查閲。 接着上面,proc_create函數有四個參數,分別為: name:要創建的文件名。 mode:文件的訪問權限。 parent:父文件夾的proc_dir_entry指針。 proc_fops:改文件的操作函數。 看到這個函數,有沒有感到很熟悉?我們在學習驅動基礎的時候,有用到了device_create函數來創建節點: device_create創建的設備節點存放於/dev目錄下,而proc_create函數創建的與系統信息相關的節點存放於/proc目錄下。既然它們這麼相似,下面我們就模仿編寫驅動的方式來編寫我們關於proc的測試代碼。 proc實踐 我們模仿字符設備驅動的編寫方式,來編寫基於proc的“驅動”。首先需要創建一個文件操作結構體hello_proc_operations,創建一些hello_proc_open、hello_proc_close、hello_proc_read、hello_proc_write填到這個操作表裏: 左右滑動查看全部代碼>>> #include #include #include #include #include static char kernel_buf[1024];#define MIN(a, b) (a  _initcall  ->  device_initcall ->  _define_initcall ② fs_initcall-> _define_initcall 為了方便,我們直接用module_init 。 Makefile文件: 左右滑動查看全部代碼>>> KERN_DIR = /home/book/100ask_imx6ull-sdk/Linux-4.9.88#  -C 表示將當前的工作目錄切換到指定目錄中,M 表示模塊源碼目錄, modules 表示編譯模塊all: make -C $(KERN_DIR) M=`pwd` modules clean: make -C $(KERN_DIR) M=`pwd` modules clean rm -rf modules.order# obj-m 表示將 proc_test.c 這個文件編譯為 proc_test.ko 模塊obj-m += proc_test.o 編譯: 傳到板子裏測試: 可以看到,我們已經成功地在proc中留下了一個hello_proc小腳印。可以看到,我們創建的基於/proc下的“驅動”與創建基於/dev下的真實的設備驅動的思路及套路是很相似的。這些都是屬於內核的範疇,都是屬於內核的東西,內核把想給我們能直接使用的東西(文件)都放於/proc、/dev等目錄下,我們在應用端就可以很方便地訪問這些文件開發我們的應用。 如果文章中有錯誤,歡迎指出。 碼字不易,如果覺得文章有用,期待你的點贊、在看與轉發,給小編更多的動力分享更多的東西。 猜你喜歡 Linux下應用開發基礎 【遞四方速遞】LED驅動實驗(總線設備驅動模型) 【遞四方速遞】設備樹實例分析 學習STM32的一些經驗分享 我的單片機轉嵌入式Linux之路 STM32的map文件學習筆記 基於RT-Thread的智慧路燈案例實驗分享 C語言、嵌入式中幾個非常實用的宏技巧 1024G 嵌入式資源大放送!包括但不限於C/C++、單片機、Linux等。在公眾號聊天界面回覆1024,即可免費獲取! 免責聲明:本文內容由21ic獲得授權後發佈,版權歸原作者所有,本平台僅提供信息存儲服務。文章僅代表作者個人觀點,不代表本平台立場,如有問題,請聯繫我們,謝謝!

    時間:2020-11-22 關鍵詞: 計算機 嵌入式

  • 虛擬內存模型和malloc內部原理

    成功是急不來的。不計較眼前得失,將注意力真正着眼於正在做的事情本身,持續付出努力,才能一步步向前邁進,逐漸達到理想的目標。不着急,才能從容不迫,結果自會水到渠成。 大家好,我是程序喵! 攤牌了,不裝了,其實我是程序喵辛苦工作一天還要回家編輯公眾號到大半夜的老婆,希望各位大哥能踴躍轉發,完成我一千閲讀量的KPI(夢想),謝謝! 咳咳,有點跑題,以下是程序喵的廢話,麻煩給個面子劃到最後點擊在看或者贊,證明我比程序喵人氣高,謝謝! 通過/proc文件系統探究虛擬內存 我們會通過/proc文件系統找到正在運行的進程的字符串所在的虛擬內存地址,並通過更改此內存地址的內容來更改字符串內容,使你更深入瞭解虛擬內存這個概念!這之前先介紹下虛擬內存的定義! 虛擬內存 虛擬內存是一種實現在計算機軟硬件之間的內存管理技術,它將程序使用到的內存地址(虛擬地址)映射到計算機內存中的物理地址,虛擬內存使得應用程序從繁瑣的管理內存空間任務中解放出來,提高了內存隔離帶來的安全性,虛擬內存地址通常是連續的地址空間,由操作系統的內存管理模塊控制,在觸發缺頁中斷時利用分頁技術將實際的物理內存分配給虛擬內存,而且64位機器虛擬內存的空間大小遠超出實際物理內存的大小,使得進程可以使用比物理內存大小更多的內存空間。 在深入研究虛擬內存前,有幾個關鍵點: 每個進程都有它自己的虛擬內存 虛擬內存的大小取決於系統的體系結構 不同操作管理有着不同的管理虛擬內存的方式,但大多數操作系統的虛擬內存結構如下圖: virtual_memory.png 上圖並不是特別詳細的內存管理圖,高地址其實還有內核空間等等,但這不是這篇文章的主題。從圖中可以看到高地址存儲着命令行參數和環境變量,之後是棧空間、堆空間和可執行程序,其中棧空間向下延申,堆空間向上增長,堆空間需要使用malloc分配,是動態分配的內存的一部分。 首先通過一個簡單的C程序探究虛擬內存。 #include #include #include /** * main - 使用strdup創建一個字符串的拷貝,strdup內部會使用malloc分配空間, * 返回新空間的地址,這段地址空間需要外部自行使用free釋放 * * Return: EXIT_FAILURE if malloc failed. Otherwise EXIT_SUCCESS */int main(void){    char *s;    s = strdup("test_memory");    if (s == NULL)    {        fprintf(stderr, "Can't allocate mem with malloc\n");        return (EXIT_FAILURE);    }    printf("%p\n", (void *)s);    return (EXIT_SUCCESS);}編譯運行:gcc -Wall -Wextra -pedantic -Werror main.c -o test; ./test輸出:0x88f010 我的機器是64位機器,進程的虛擬內存高地址為0xffffffffffffffff, 低地址為0x0,而0x88f010遠小於0xffffffffffffffff,因此大概可以推斷出被複制的字符串的地址(堆地址)是在內存低地址附近,具體可以通過/proc文件系統驗證.ls /proc目錄可以看到好多文件,這裏主要關注/proc/[pid]/mem和/proc/[pid]/maps mem & maps man proc/proc/[pid]/mem    This file can be used to access the pages of a process's memory through open(2), read(2), and lseek(2)./proc/[pid]/maps    A  file containing the currently mapped memory regions and their access permissions.          See mmap(2) for some further information about memory mappings.              The format of the file is:       address           perms offset  dev   inode       pathname       00400000-00452000 r-xp 00000000 08:02 173521      /usr/bin/dbus-daemon       00651000-00652000 r--p 00051000 08:02 173521      /usr/bin/dbus-daemon       00652000-00655000 rw-p 00052000 08:02 173521      /usr/bin/dbus-daemon       00e03000-00e24000 rw-p 00000000 00:00 0           [heap]       00e24000-011f7000 rw-p 00000000 00:00 0           [heap]       ...       35b1800000-35b1820000 r-xp 00000000 08:02 135522  /usr/lib64/ld-2.15.so       35b1a1f000-35b1a20000 r--p 0001f000 08:02 135522  /usr/lib64/ld-2.15.so       35b1a20000-35b1a21000 rw-p 00020000 08:02 135522  /usr/lib64/ld-2.15.so       35b1a21000-35b1a22000 rw-p 00000000 00:00 0       35b1c00000-35b1dac000 r-xp 00000000 08:02 135870  /usr/lib64/libc-2.15.so       35b1dac000-35b1fac000 ---p 001ac000 08:02 135870  /usr/lib64/libc-2.15.so       35b1fac000-35b1fb0000 r--p 001ac000 08:02 135870  /usr/lib64/libc-2.15.so       35b1fb0000-35b1fb2000 rw-p 001b0000 08:02 135870  /usr/lib64/libc-2.15.so       ...       f2c6ff8c000-7f2c7078c000 rw-p 00000000 00:00 0    [stack:986]       ...       7fffb2c0d000-7fffb2c2e000 rw-p 00000000 00:00 0   [stack]       7fffb2d48000-7fffb2d49000 r-xp 00000000 00:00 0   [vdso]              The address field is the address space in the process that the mapping occupies.          The perms field is a set of permissions:                   r = read                   w = write                   x = execute                   s = shared                   p = private (copy on write)              The offset field is the offset into the file/whatever;          dev is the device (major:minor); inode is the inode on that device.   0  indicates              that no inode is associated with the memory region,          as would be the case with BSS (uninitialized data).              The  pathname field will usually be the file that is backing the mapping.          For ELF files, you can easily coordinate with the offset field              by looking at the Offset field in the ELF program headers (readelf -l).              There are additional helpful pseudo-paths:                   [stack]                          The initial process's (also known as the main thread's) stack.                   [stack:] (since Linux 3.4)                          A thread's stack (where the  is a thread ID).              It corresponds to the /proc/[pid]/task/[tid]/ path.                   [vdso] The virtual dynamically linked shared object.                   [heap] The process's heap.              If the pathname field is blank, this is an anonymous mapping as obtained via the mmap(2) function.          There is no easy  way  to  coordinate              this back to a process's source, short of running it through gdb(1), strace(1), or similar.              Under Linux 2.0 there is no field giving pathname. 通過mem文件可以訪問和修改整個進程的內存頁,通過maps可以看到進程當前已映射的內存區域,有地址和訪問權限偏移量等,從maps中可以看到堆空間是在低地址而棧空間是在高地址.  從maps中可以看到heap的訪問權限是rw,即可寫,所以可以通過堆地址找到上個示例程序中字符串的地址,並通過修改mem文件對應地址的內容,就可以修改字符串的內容啦,程序: #include #include #include #include /**               * main - uses strdup to create a new string, loops forever-ever *                 * Return: EXIT_FAILURE if malloc failed. Other never returns */int main(void){     char *s;     unsigned long int i;     s = strdup("test_memory");     if (s == NULL)     {          fprintf(stderr, "Can't allocate mem with malloc\n");          return (EXIT_FAILURE);     }     i = 0;     while (s)     {          printf("[%lu] %s (%p)\n", i, s, (void *)s);          sleep(1);          i++;     }     return (EXIT_SUCCESS);}編譯運行:gcc -Wall -Wextra -pedantic -Werror main.c -o loop; ./loop輸出:[0] test_memory (0x21dc010)[1] test_memory (0x21dc010)[2] test_memory (0x21dc010)[3] test_memory (0x21dc010)[4] test_memory (0x21dc010)[5] test_memory (0x21dc010)[6] test_memory (0x21dc010)... 這裏可以寫一個腳本通過/proc文件系統找到字符串所在位置並修改其內容,相應的輸出也會更改。 首先找到進程的進程號 ps aux | grep ./loop | grep -v grepzjucad    2542  0.0  0.0   4352   636 pts/3    S+   12:28   0:00 ./loop 2542即為loop程序的進程號,cat /proc/2542/maps得到 00400000-00401000 r-xp 00000000 08:01 811716                             /home/zjucad/wangzhiqiang/loop00600000-00601000 r--p 00000000 08:01 811716                             /home/zjucad/wangzhiqiang/loop00601000-00602000 rw-p 00001000 08:01 811716                             /home/zjucad/wangzhiqiang/loop021dc000-021fd000 rw-p 00000000 00:00 0                                  [heap]7f2adae2a000-7f2adafea000 r-xp 00000000 08:01 8661324                    /lib/x86_64-linux-gnu/libc-2.23.so7f2adafea000-7f2adb1ea000 ---p 001c0000 08:01 8661324                    /lib/x86_64-linux-gnu/libc-2.23.so7f2adb1ea000-7f2adb1ee000 r--p 001c0000 08:01 8661324                    /lib/x86_64-linux-gnu/libc-2.23.so7f2adb1ee000-7f2adb1f0000 rw-p 001c4000 08:01 8661324                    /lib/x86_64-linux-gnu/libc-2.23.so7f2adb1f0000-7f2adb1f4000 rw-p 00000000 00:00 07f2adb1f4000-7f2adb21a000 r-xp 00000000 08:01 8661310                    /lib/x86_64-linux-gnu/ld-2.23.so7f2adb3fa000-7f2adb3fd000 rw-p 00000000 00:00 07f2adb419000-7f2adb41a000 r--p 00025000 08:01 8661310                    /lib/x86_64-linux-gnu/ld-2.23.so7f2adb41a000-7f2adb41b000 rw-p 00026000 08:01 8661310                    /lib/x86_64-linux-gnu/ld-2.23.so7f2adb41b000-7f2adb41c000 rw-p 00000000 00:00 07ffd51bb3000-7ffd51bd4000 rw-p 00000000 00:00 0                          [stack]7ffd51bdd000-7ffd51be0000 r--p 00000000 00:00 0                          [vvar]7ffd51be0000-7ffd51be2000 r-xp 00000000 00:00 0                          [vdso]ffffffffff600000-ffffffffff601000 r-xp 00000000 00:00 0                  [vsyscall] 看見堆地址範圍021dc000-021fd000,並且可讀可寫,而且021dc000\0\1\0\0\0P\t\2\0\0\0\0\0"..., 832) = 832fstat(3, {st_mode=S_IFREG|0755, st_size=1868984, ...}) = 0mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f37720f9000mmap(NULL, 3971488, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7f3771b27000mprotect(0x7f3771ce7000, 2097152, PROT_NONE) = 0mmap(0x7f3771ee7000, 24576, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x1c0000) = 0x7f3771ee7000mmap(0x7f3771eed000, 14752, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x7f3771eed000close(3)                                = 0mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f37720f8000mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f37720f7000arch_prctl(ARCH_SET_FS, 0x7f37720f8700) = 0mprotect(0x7f3771ee7000, 16384, PROT_READ) = 0mprotect(0x600000, 4096, PROT_READ)     = 0mprotect(0x7f3772116000, 4096, PROT_READ) = 0munmap(0x7f37720fa000, 111450)          = 0write(1, "BEFORE MALLOC\n", 14BEFORE MALLOC)         = 14brk(NULL)                               = 0x781000brk(0x7a2000)                           = 0x7a2000write(1, "AFTER MALLOC\n", 13AFTER MALLOC)          = 13fstat(1, {st_mode=S_IFCHR|0620, st_rdev=makedev(136, 3), ...}) = 0write(1, "0x781010\n", 90x781010)               = 9fstat(0, {st_mode=S_IFCHR|0620, st_rdev=makedev(136, 3), ...}) = 0 最後幾行的輸出可知,malloc主要調用brk系統調用來操作堆內存. man brk...       int brk(void *addr);       void *sbrk(intptr_t increment);...DESCRIPTION       brk() and sbrk() change the location of the program  break,  which  defines       the end of the process's data segment (i.e., the program break is the first       location after the end of the uninitialized data segment).  Increasing  the       program  break has the effect of allocating memory to the process; decreas‐       ing the break deallocates memory.       brk() sets the end of the data segment to the value specified by addr, when       that  value  is  reasonable,  the system has enough memory, and the process       does not exceed its maximum data size (see setrlimit(2)).       sbrk() increments the program's data space  by  increment  bytes.   Calling       sbrk()  with  an increment of 0 can be used to find the current location of       the program break. 程序中斷是虛擬內存中程序數據段結束後的第一個位置的地址,malloc通過調用brk或者sbrk,增加程序中斷的值就可以創建新空間來動態分配內存,首次調用brk會返回當前程序中斷的地址,第二次調用brk也會返回程序中斷的地址,可以發現第二次brk返回地址大於第一次brk返回地址,brk就是通過增加程序中斷地址的方式來分配內存,可以看出現在的堆地址範圍是0x781000-0x7a2000,通過cat /proc/[pid]/maps也可以驗證,此處就不貼上實際驗證的結果啦。 多次malloc 如果多次malloc會出現什麼現象呢,代碼如下: #include #include #include /** * main - many calls to malloc * * Return: EXIT_FAILURE if something failed. Otherwise EXIT_SUCCESS */int main(void){    void *p;    write(1, "BEFORE MALLOC #0\n", 17);    p = malloc(1024);    write(1, "AFTER MALLOC #0\n", 16);    printf("%p\n", p);    write(1, "BEFORE MALLOC #1\n", 17);    p = malloc(1024);    write(1, "AFTER MALLOC #1\n", 16);    printf("%p\n", p);    write(1, "BEFORE MALLOC #2\n", 17);    p = malloc(1024);    write(1, "AFTER MALLOC #2\n", 16);    printf("%p\n", p);    write(1, "BEFORE MALLOC #3\n", 17);    p = malloc(1024);    write(1, "AFTER MALLOC #3\n", 16);    printf("%p\n", p);    getchar();    return (EXIT_SUCCESS);}編譯運行:gcc test.c -o 5; strace ./5摘要輸出結果如下:write(1, "BEFORE MALLOC #0\n", 17BEFORE MALLOC #0)      = 17brk(NULL)                               = 0x561605c7a000brk(0x561605c9b000)                     = 0x561605c9b000write(1, "AFTER MALLOC #0\n", 16AFTER MALLOC #0)       = 16fstat(1, {st_mode=S_IFCHR|0620, st_rdev=makedev(136, 0), ...}) = 0write(1, "0x561605c7a260\n", 150x561605c7a260)        = 15write(1, "BEFORE MALLOC #1\n", 17BEFORE MALLOC #1)      = 17write(1, "AFTER MALLOC #1\n", 16AFTER MALLOC #1)       = 16write(1, "0x561605c7aa80\n", 150x561605c7aa80)        = 15write(1, "BEFORE MALLOC #2\n", 17BEFORE MALLOC #2)      = 17write(1, "AFTER MALLOC #2\n", 16AFTER MALLOC #2)       = 16write(1, "0x561605c7ae90\n", 150x561605c7ae90)        = 15write(1, "BEFORE MALLOC #3\n", 17BEFORE MALLOC #3)      = 17write(1, "AFTER MALLOC #3\n", 16AFTER MALLOC #3)       = 16write(1, "0x561605c7b2a0\n", 150x561605c7b2a0)        = 15fstat(0, {st_mode=S_IFCHR|0620, st_rdev=makedev(136, 0), ...}) = 0 可以發現並不是每次調用malloc都會觸發brk系統調用,首次調用malloc,內部會通過brk系統調用更改程序中斷地址,分配出一大塊內存空間,後續再調用malloc,malloc內部會優先使用之前分配出來的內存空間,直到內部內存空間已經不夠再次分配給外部時才會再次觸發brk系統調用. 0x10 那丟失的16字節是什麼 上面分析可以看見程序第一次調用malloc返回的地址並不是heap段的首地址,而是相差了0x10個字節,那這16個字節究竟是什麼,可以通過程序打印出這前16個字節的內容. 編譯運行:gcc test.c -o test;./test輸出:0x5589436ce260bytes at 0x5589436ce250:00 00 00 00 00 00 00 00 11 04 00 00 00 00 00 000x5589436cea80bytes at 0x5589436cea70:00 00 00 00 00 00 00 00 11 08 00 00 00 00 00 000x5589436cf290bytes at 0x5589436cf280:00 00 00 00 00 00 00 00 11 0c 00 00 00 00 00 000x5589436cfea0bytes at 0x5589436cfe90:00 00 00 00 00 00 00 00 11 10 00 00 00 00 00 000x5589436d0eb0bytes at 0x5589436d0ea0:00 00 00 00 00 00 00 00 11 14 00 00 00 00 00 000x5589436d22c0bytes at 0x5589436d22b0:00 00 00 00 00 00 00 00 11 18 00 00 00 00 00 000x5589436d3ad0bytes at 0x5589436d3ac0:00 00 00 00 00 00 00 00 11 1c 00 00 00 00 00 000x5589436d56e0bytes at 0x5589436d56d0:00 00 00 00 00 00 00 00 11 20 00 00 00 00 00 000x5589436d76f0bytes at 0x5589436d76e0:00 00 00 00 00 00 00 00 11 24 00 00 00 00 00 000x5589436d9b00bytes at 0x5589436d9af0:00 00 00 00 00 00 00 00 11 28 00 00 00 00 00 00 可以看出規律:這16個字節相當於malloc出來的地址的頭,包含一些信息,目前可以看出它包括已經分配的地址空間的大小,第一次malloc申請了0x400(1024)字節,可以發現11 04 00 00 00 00 00 00大於0x400(1024),這8個字節表示數字 0x 00 00 00 00 00 00 04 11 = 0x400(1024) + 0x10(頭的大小16) + 1(後面會説明它的含義),可以發現每次調用malloc,這前8個字節代表的含義都是malloc字節數+16+1. 可以猜測,malloc內部會把這前16個字節強轉成某種數據結構,數據結構包含某些信息,最主要的是已經分配的字節數,儘管我們不瞭解具體結構,但是也可以通過代碼操作這16個字節驗證我們上面總結的規律是否正確,注意代碼中不調用free釋放內存. #include #include #include /**                                                                 * pmem - print mem                                                 * @p: memory address to start printing from                        * @bytes: number of bytes to print                                 *                                                                  * Return: nothing */void pmem(void *p, unsigned int bytes){    unsigned char *ptr;    unsigned int i;    ptr = (unsigned char *)p;    for (i = 0; i 

    時間:2020-11-22 關鍵詞: 虛擬內存 嵌入式

  • MOS管及其擴展的知識

    關注+星標公眾號,不錯過精彩內容 作者 | 記得誠 轉自 | 記得誠電子設計 今天的文章簡單總結一下MOS管,如下是本文目錄。 ▉ 場效應管分類 場效應管分為結型(JFET)和金屬-氧化物-半導體型(MOSFET)兩種類型。 JFET的英文全稱是Junction Field-Effect Transistor,也分為N溝道和P溝道兩種,在實際中幾乎不用。 MOSFET英文全稱是Metal-Oxide-Semiconductor Field-Effect Transistor,應用廣泛,MOSFET一般稱MOS管。 MOSFET有增強型和耗盡型兩大類,增強型和耗盡型每一類下面都有NMOS和PMOS。 增強型MOS管的英文為Enhancement MOS或者EMOS,耗盡型MOS管的英文為Depletion MOS或者DMOS。 一般主板上使用最多的是增強型MOS管,NMOS最多,一般多用在信號控制上,其次是PMOS,多用在電源開關等方面,耗盡型幾乎不用。 ▉ N和P區分 如下紅色箭頭指向G極的為NMOS,箭頭背向G極的為PMOS。 ▉ 寄生二極管 由於生產工藝,一般的MOS管會有一個寄生二極管,有的也叫體二極管。 紅色標註的為體二極管 從上圖可以看出NMOS和PMOS寄生二極管方向不一樣,NMOS是由S極→D極,PMOS是由D極→S極。 寄生二極管和普通二極管一樣,正接會導通,反接截止,對於NMOS,當S極接正,D極接負,寄生二極管會導通,反之截止;對於PMOS管,當D極接正,S極接負,寄生二極管導通,反之截止。 某些應用場合,也會選擇走體二極管,以降低DS之間的壓降(體二極管的壓降是比MOS的導通壓降大很多的),同時也要關注體二極管的過電流能力。 當滿足MOS管的導通條件時,MOS管的D極和S極會導通,這個時候體二極管是截止狀態 ,因為MOS管的導通內阻極小,一般mΩ級別,流過1A級別的電流,也才mV級別,所以D極和S極之間的導通壓降很小,不足以使寄生二極管導通,這點需要特別注意。 ▉ 導通條件 MOS管是壓控型,導通由G和S極之間壓差決定。 對NMOS來説,Vg-Vs>Vgs(th),即G極和S極的壓差大於一定值,MOS管會導通,但是也不能大太多,否則燒壞MOS管,開啓電壓和其他參數可以看具體器件的SPEC。 對PMOS來説,Vs-Vg>Vgs(th),即S極和G極的壓差大於一定值,MOS管會導通,同樣的,具體參數看器件的SPEC。 ▉ 基本開關電路 NMOS管開關電路 當GPIO_CTRL電壓小於MOS管開啓電壓時,MOS管截止,OUT通過R1上拉到5V,OUT=5V。 當GPIO_CTRL電壓大於MOS管開啓電壓時,MOS管導通,D極電壓等於S極電壓,即OUT=0V。 PMOS管開關電路 PMOS管最常用在電源開關電路中,下圖所示,當GPIO_CRTL=0V時,S和G極壓差大於MOS管開啓電壓時,MOS管導通,5V_VOUT=5V_VIN。 ▉ 與三極管的區別 三極管是電流控制,MOS管是電壓控制,主要有如下的區別: 1,只容許從信號源取少量電流的情況下,選用MOS管;在信號電壓較低,有容許從信號源取較多電流的條件下,選用三極管。 2,MOS管是單極性器件(靠一種多數載流子導電),三極管是雙極性器件(既有多數載流子,也要少數載流子導電)。 3,有些MOS管的源極和漏極可以互換運用,柵極也可正可負,靈活性比三極管好。 4,MOS管應用普遍,可以在很小電流和很低電壓下工作。 5,MOS管輸入阻抗大,低噪聲,MOS管較貴,三極管的損耗大。 6,MOS管常用來作為電源開關,以及大電流開關電路、高頻高速電路中,三極管常用來數字電路開關控制。 ▉ G和S極串聯電阻的作用 MOS管的輸入阻抗很大,容易受到外界信號的干擾,只要少量的靜電,就能使G-S極間等效電容兩端產生很高的電壓,如果不及時把靜電釋放掉,兩端的高壓容易使MOS管產生誤動作,甚至有可能擊穿G-S極,起到一個固定電平的作用。 ▉ G極串聯電阻的作用 MOS管是壓控型,有的情況下,為什麼還需要在G極串聯一個電阻呢? 1,減緩Rds從無窮大到Rds(on)。 2,防止震盪,一般單片機的I/O輸出口都會帶點雜散電感,在電壓突變的情況下,可能與柵極電容形成LC震盪,串聯電阻可以增大阻尼減小震盪效果。 3,減小柵極充電峯值電流。 ▉ MOS管的米勒效應 關於MOS管的米勒效應,可以閲讀文章: 臭名昭著的MOS管米勒效應 ▉ 選型要點 1.電壓值 關注Vds最大導通電壓和Vgs最大耐壓,實際使用中,不能超過這個值,否則MOS管會損壞。 關注導通電壓Vgs(th),一般MOS管都是用單片機進行控制,根據單片機GPIO的電平來選擇合適導通閾值的MOS管,並且儘量留有一定的餘量,以確保MOS可以正常開關。 2.電流值 關注ID電流,這個值代表了PMOS管的能流過多大電流,反應帶負載的能力,超過這個值,MOS管也會損壞。 3.功率損耗 功率損耗需要關注以下幾個參數,包括熱阻、温度。熱阻指的是當有熱量在物體上傳輸時,在物體兩端温度差與熱源的功率之間的比值,單位是℃/W或者是K/W,熱阻的公式為ThetaJA = (Tj-Ta)/P,和功率和環境温度都有關係。 4.導通內阻 導通內阻關注PMOS的Rds(on)參數,導通內阻越小,PMOS管的損耗越小,一般PMOS管的導通內阻都是在mΩ級別。 5.開關時間 MOS作為開關器件,就會有開關時間概念,在高速電路中,儘可能選擇輸入、輸出電容Ciss&Coss小、開關時間Ton&Toff短的MOS管,以保證數據通信正常。 參數解釋推薦閲讀文章:帶你讀懂MOS管參數「熱阻、輸入輸出電容及開關時間」 6.封裝 根據PCB板的尺寸,選擇合適的PMOS管尺寸,在板載面積有限的情況下,儘可能選擇小封裝;儘量選擇常見封裝,以備後續選擇合適的替代料。 ------------ END ------------ 推薦閲讀: C語言實現面向對象的原理 淺談Makefile、Kconfig和.config文件 手把手教你在Keil MDK中使用GCC編譯器 關注 微信公眾號『嵌入式專欄』,底部菜單查看更多內容,回覆“加羣”按規則加入技術交流羣。 點擊“閲讀原文”查看更多分享,歡迎點分享、收藏、點贊、在看。 免責聲明:本文內容由21ic獲得授權後發佈,版權歸原作者所有,本平台僅提供信息存儲服務。文章僅代表作者個人觀點,不代表本平台立場,如有問題,請聯繫我們,謝謝!

    時間:2020-11-22 關鍵詞: 元器件 MOS管

  • 如何判斷電容好壞?

    來源 | 電子電路 怎樣測量小容量電容的好壞? 1、檢測10pF以下的小電容,因10pF以下的固定電容器容量太小,如果用指針式用萬用表進行測量,只能定性的檢查其是否有漏電,內部短路或擊穿現象。 測量時,可選用萬用表R×10k擋,用兩電錶金屬測棒分別任意接電容的兩個接腳,阻值應為無窮大。若測出阻值(指標向右擺動)為零,則説明電容漏電損壞或內部擊穿。 2、對於0.01μF以上的固定電容,可用指針式萬用表的R×10k擋直接測試電容器有無充電過程以及有無內部短路或漏電,並可根據指針向右擺動的幅度大小估計出電容器的容量。 如何檢測電解電容器好壞? 1、因為電解電容的容量較一般固定電容大得多,所以,測量時,應針對不同容量選用合適的量程。根據經驗,一般情況下,1~47μF間的電容,可用R×1k擋測量,大於47μF的電容可用R×100擋測量。 2、將萬用表紅電錶金屬測棒接負極,黑電錶金屬測棒接正極,在剛接觸的瞬間,萬用表指針即向右偏轉較大偏度(對於同一電阻擋,容量越大,擺幅越大),接着逐漸向左迴轉,直到停在某一位置。 此時的阻值便是電解電容的正向漏電阻,此值略大於反向漏電阻。實際使用經驗表明,電解電容的漏電阻一般應在幾百kΩ以上,否則,將不能正常工作。 在測試中,若正向、反向均無充電的現象,即錶針不動,則説明容量消失或內部斷路;如果所測阻值很小或為零,説明電容漏電大或已擊穿損壞,不能再使用。 3、對於正、負極標誌不明的電解電容器,可利用上述測量漏電阻的方法加以判別。即先任意測一下漏電阻,記住其大小,然後交換電錶金屬測棒再測出一個阻值。 兩次測量中阻值大的那一次便是正向接法,即黑電錶金屬測棒接的是正極,紅電錶金屬測棒接的是負極。 4、使用萬用表電阻擋,採用給電解電容進行正、反向充電的方法,根據指針向右擺動幅度的大小,可估測出電解電容的容量。 5、測電容放電的速度 用一個數字萬用表搭在電容兩端先充電然後開路量測電壓掉下來的速度因為電容是開路的唯一會耗電的就是漏電流了如果你量測出電壓跟時間的曲線就可以反推出漏電流了記得用好一點的數字萬用表,因為萬用表本身的輸入阻抗再大也是有限的,如果是質量較好的電容漏電流本來就不大,那麼輸入阻抗稍小的數字萬用表就不準了。 6、注意!測量較大容量電容時如需要正負來回測量,要將電容短路放電,以免打壞表頭。 根據經驗,在高頻電路,開關電源電路有很多小電容是普通萬用表無法正確判斷出好壞的,有的電容量還有可能出現增加的可能。強烈建議用專用數字電容表測量。 ------------ END ------------ 關注 點擊“”查看更多分享,歡迎點分享、收藏、點贊、在看。 免責聲明:本文內容由21ic獲得授權後發佈,版權歸原作者所有,本平台僅提供信息存儲服務。文章僅代表作者個人觀點,不代表本平台立場,如有問題,請聯繫我們,謝謝!

    時間:2020-11-22 關鍵詞: 電容 電源設計

  • 圖説STM32硬件CRC外設,及CRC原理應用

    關注+星標公眾號,不錯過精彩內容 作者 | 逸珺 來源 | STM32 在嵌入式產品應用中,常常需要應對系統數據在存儲或者傳輸過程中的完整性問題。 所謂完整性是指數據在其生命週期中的準確性和一致性。這些數據可能存儲在EEPROM/FLASH裏,或者基於通信協議進行傳輸,它們有可能因為外界干擾或者程序錯誤,甚至系統入侵而導致被破壞。如果這些數據在使用前不做校驗,產品功能可能失效。在一些特定領域,嚴重時可能會危及用户財產甚至生命安全。 本文就來聊聊使用較為廣泛的循環冗餘校驗技術,以及在STM32中的一些具體使用體會。 所謂循環冗餘校驗(CRC:Cyclic Redundancy Check)是一種錯誤檢測算法,通常在通信協議中或存儲設備中用於檢測原始數據的意外變動。可以簡單理解成對有用數據按照一定的算法進行計算後,提取出一個特徵值,並附加在有用數據後。在應用中將有用數據按照特定的算法提取特徵值與預先存儲的特徵值進行比對,如相等則校驗通過,反之校驗失敗,從而識別出數據是否異常。 為何要校驗數據完整性(Data Integrity)? 數據在存儲以及傳輸的過程中可能發生異動。以數據通信應用場景為例,常見的錯誤大致有兩種失效模式: 單個位錯誤(Single Bit Error):僅僅某一個數據位出現錯誤,如圖: 突發錯誤(BurstError):兩個或更多個數據位在碼流中出現錯誤,如圖: 為什麼可能會出現這些位錯誤呢?對於電子系統通信,它涉及到物理層、鏈路層、通信介質等,其中物理層主要將原始二進制數據利用一定的編解碼原理對其進行調製,然後經由發送電路將調製信號輸送至傳輸介質,接收端利用接收電路進行接收並解調,將信息還原成二進制碼流。在這個過程中介質有可能被幹擾,接收電路、發送電路、調製電路、解調電路都可能由於某些干擾原因導致工作失效而出現誤碼。此時,如果沒有一個很好的機制去確保數據的正確性,比如一個飛控系統中某些控制命令、車輛系統中CAN報文數據,系統直接使用這些錯誤數據去控制被控對象(比如電機、發動機等),嚴重的時候就會造成難以估量的生命財產災難。 存儲系統中的數據也是一樣。一般來説,系統在上電運行時會從物理存儲介質裝載系統參數,比如一些校準數據。如果由於介質的某些位被破壞,或者軟件bug導致數據被誤操作了,而沒有數據完整性檢測,這樣的數據直接被應用於系統控制,一樣會造成安全隱患。 所以,對於數據完整性檢測的重要性不言而喻。常見的數據完整性算法有很多種,比如簡單的異或校驗、CRC循環冗餘校驗、FEC前向糾錯算法等等。而循環冗餘校驗在嵌入式系統中應用非常廣泛,在通信協議制定、數據存儲、壓縮解壓算法等都有廣泛的應用。 循環冗餘校驗使用二進制除法作為算法原理,具有強大的錯誤檢測機制。對於二進制除法使用少量的硬件邏輯電路就可實現。至於軟件代碼實現,有查表法和移位計算兩種思路及策略。查表法以空間換時間,移位計算法以時間換空間。 何為循環冗餘校驗? 循環冗餘校驗的核心數學算法原理基於循環碼,在不增加原始數據的信息基礎上擴展了信息,以極小的存儲代價存儲其冗餘特徵。該算法是W. Wesley Peterson 於1961年發明的。 這裏的n位二進制數據為有效信息載荷。(可能是傳輸或存儲的有用信息) 根據CRC算法計算出m位冗餘碼,即根據該CRC校驗多項式結合CRC算法從前面有效數據中提取出特徵冗餘碼,這就是冗餘的真實含義。 實際傳輸或者存儲的就是n+m位二進制數據。 這裏引出一個概念:多項式,在CRC校驗算法中多項式可做如下理解及表示: 其本質就是多進制的數學表示法,這裏是二進制,故X為2。 其基本的算法處理過程示意如下: 假定待發送有效數據為二進制多項式M(x),而校驗多項式P(x)為收發雙方約定好了的,雙方已知,這裏介紹一下幾個多項式表示的意思及相關處理流程: 接收方接收到數據後進行CRC校驗。餘數為0,校驗通過。 其實CRC的本質就是二進制多項式除法求取冗餘碼的計算過程,無論軟件的查表法、移位計算法,還是純硬件邏輯電路實現,本質都是一樣的。對於數字邏輯電路利用移位計算則更具優勢,因為幾乎不佔用CPU時間。 常見的CRC校驗多項式 常見的CRC校驗多項式算子有哪些? 不同的校驗多項式,除了複雜度有差異外,從應用角度看有什麼差異呢?從應用角度看主要體現在錯誤診斷率。不妨看看CRC-16以及CRC-CCITT的錯誤檢測效果: 可完全檢測出單bit及雙bit錯誤 奇數個位錯誤 能檢測出16位長度及小於16的突發錯誤 能以99.997%的概率檢測出長度為17位及以上的錯誤 選擇不同的校驗多項式算子,其位錯誤診斷成功率是不一 樣的,當然其計算開銷也不一樣。我們來查查權威的IEC標準看看。下圖截自《IEC61508-7》。 由上文可見,CRC-8可診斷出99.6%的位錯誤概率,而CRC-16則提高至99.998%的位錯誤概率。 注:IEC61508是國際電工委員會功能安全標準(Functional safety of electrical/electronic/programmable electronicsafety-related systems)。 技術發展至今,已有大量不同的校驗多項式生成器被各行各業使用。下面是來自wikipedia截圖,供大家參考: STM32的CRC硬件外設 如下圖,STM32內置了一個CRC-32硬件計算單元,實現了一個固定多項式0x4C11DB7(16進製表示),可應用於以太網報文校驗碼計算。 STM32 全系列產品都具有 CRC 外設,對 CRC 的計算提供硬件支持,節省了應用代碼存儲空間。CRC 校驗值既可以用於傳輸中的數據正確性驗證,也可用於數據存儲時的完整性檢查。在 IEC60335 中,也接受通過 CRC 校驗對 FLASH 的完整性進行檢查。在對 FLASH 完整性檢查的應用中,需要事先計算出整個 FLASH 的 CRC 校驗值(不包括最後保存CRC 值的字節),放在FLASH 的末尾。在程序啓動或者運行的過程中重新用同樣的方法計算整個 FLASH 的 CRC 校驗值,然後與保存在 FLASH 末尾地址空間的 CRC 值進行比較。 EWARM 從 v5.5 版本之後開始支持 STM32 芯片的 CRC計算。計算整個 FLASH的 CRC 校驗值並保存在 FLASH末尾的過程,可以在 IAR 中完成。通過配置EWARM 的 CRC 計算參數,自動對整個 FLASH 空間進行 CRC 計算,並將計算結果放到 內部FLASH空間 的末尾。 或許你會問,這有什麼應用價值呢?不妨以基於MCU程序的升級為例。在代碼升級過程中,如果不對bootloader升級接口傳入的二進制程序文件做校驗,就無法及時發現升級過程中發生的代碼錯誤。相反,如果原始代碼添加了校驗碼,升級程序在接受到升級文件後做校驗計算,並與待升級文件末尾的校驗碼進行比對,如果不匹配則放棄升級,這樣就不至於將無效的甚至有安全隱患的代碼寫進芯片。 修改 Link 文件,指定 checksum 在FLASH 中的存儲位置,在 Link 文件中增加下面語句。 place at end of ROM_region { ro section .checksum };     該語句指定將 CRC 的值放在 FLASH 空間的末尾位置。是整個 FLASH 空間的末尾,不是應用程序的代碼末尾。這樣,CRC 值的位置就是固定的,不會隨代碼大小而變化。  配‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍置 Checksum 頁面的參數 IAR Checksum 頁説明(v6.4 及以上) IAR 的 checksum 頁面分為兩個部分: 紅線圈出的部分:定義了FLASH 中需要計算 CRC 的範圍和空閒字節填充值。 checksum 計算參數的設定部分:Checksum size :選擇 checksum 的大小(字節數) Alignment:指定 checksum 的對齊方式。不填的話默認 2 字節對齊。Algorithm:選擇 checksum 的算法Complement:是否需要進行補碼計算。選擇“As is”就是不進行補碼計算。Bit order:位輸出的順序。MSB first,每個字節的高位在前。LSB first,每個字節的低位在前。Reverse byte order within word:對於輸入數據,在一個字內反轉各個字節的順序。Initial value:checksum 計算的初始化值 Checksum unit size :選擇進行迭代的單元大小,按 8-bit,16-bit 還是 32-bit 進行迭代。 STM32 CRC 外設使用默認配置時 IAR 的配置 STM32CRC 外設的配置: POLY= 0x4C11DB7(CRC32)  Initial_Crc = 0Xffffffff  輸入/輸出數據不反轉  輸入數據:0x08000000~0x0801FFFB。(最後 4 個字節用來放計算出的 CRC 值) 在實驗的過程發現, ”Alignment ”似乎對計算出的 CRC 值沒有影響。但“Reverse byte order within word ”與“Checksumunit size ”這兩項的配置有一定關係。如果後者選擇 32-bit,則不能勾選前者;反之如果後者選擇 8-bit,則一定要勾選上“ Reverse byte order within word ”。也可以參照下圖進行設置: 對於IAR v6.4 以下版本,沒有”Checksum unit size”選項。參考配置如下: 代碼怎麼寫? 如前文 描述,這 個應用可以用於對 Flash 中數據進行校驗,參考代碼如下: /*-1- 配置CRC外設 */ CrcHandle.Instance = CRC; /* 默認二進制多項式使能 */ CrcHandle.Init.DefaultPolynomialUse = DEFAULT_POLYNOMIAL_ENABLE; /* 默認初值設置 */ CrcHandle.Init.DefaultInitValueUse = DEFAULT_INIT_VALUE_ENABLE; /* 輸入數據不反轉 */ CrcHandle.Init.InputDataInversionMode = CRC_INPUTDATA_INVERSION_NONE; /* 輸出數據不反轉 */ CrcHandle.Init.OutputDataInversionMode = CRC_OUTPUTDATA_INVERSION_DISABLED; /* 輸入數據基本單元長度為32bit */ CrcHandle.InputDataFormat = CRC_INPUTDATA_FORMAT_WORDS; if (HAL_CRC_Init(&CrcHandle) != HAL_OK) { /* 初始化錯誤 */ Error_Handler(); } pdata = (uint32_t*)ROM_START; /*##-2- 調用HAL庫利用硬件CRC外設對ROM區計算CRC-32校驗碼*/ uwCRCValue = HAL_CRC_Calculate(&CrcHandle, pdata, ROM_SIZEinWORDS); 小結 對於 CRC 應用,還可以根據多項式算子編寫純軟件方案,網上有很多現成的代碼。 其基本思路無外乎查表法以及移位計算法。 差異在於一個犧牲存儲空間以換取計算效率,一個犧牲計算時間而節省存儲空間,至於如何選擇,則根據所設計的系統綜合考慮,一般根據應用場景來定。 將塊數據利用CRC算法計算出冗餘碼,有的文章、標準稱這個冗餘碼為簽名。實際應用時計算有效數據所得校驗碼與預存校驗碼進行比較,相等則校驗通過,反之則失敗。當然,也可以將原數據與所存校驗碼一起傳入校驗算法,所得結果為0則校驗通過,反之失敗。 對於數據通信,一般會在報文的尾部添加有效數據的校驗碼,再由接收方校驗收到報文的數據完整性。    ------------ END ------------ 推薦閲讀: C語言實現面向對象的原理 淺談內核的Makefile、Kconfig和.config文件 手把手教你在Keil MDK中使用GCC編譯器工具鏈 關注 微信公眾號『嵌入式專欄』,底部菜單查看更多內容,回覆“加羣”按規則加入技術交流羣。 點擊“閲讀原文”查看更多分享,歡迎點分享、收藏、點贊、在看。 免責聲明:本文內容由21ic獲得授權後發佈,版權歸原作者所有,本平台僅提供信息存儲服務。文章僅代表作者個人觀點,不代表本平台立場,如有問題,請聯繫我們,謝謝!

    時間:2020-11-22 關鍵詞: 算法 循環冗餘校驗

  • 趣圖:Linux是怎麼樣過生日的?

    Linux 是怎麼樣過生日的? ↓↓↓ ------------ END ------------ 推薦閲讀: C語言實現面向對象的原理 淺談內核的Makefile、Kconfig和.config文件 手把手教你在Keil MDK中使用GCC編譯器工具鏈 關注 微信公眾號『嵌入式專欄』,底部菜單查看更多內容,回覆“加羣”按規則加入技術交流羣。 點擊“閲讀原文”查看更多分享,歡迎點分享、收藏、點贊、在看。 免責聲明:本文內容由21ic獲得授權後發佈,版權歸原作者所有,本平台僅提供信息存儲服務。文章僅代表作者個人觀點,不代表本平台立場,如有問題,請聯繫我們,謝謝!

    時間:2020-11-22 關鍵詞: Linux 嵌入式

  • 如何設計出更高能效的太陽能、工業驅動、電動汽車充電樁和服務器等應用

    太陽能、電動汽車充電樁、儲能、不間斷電源(UPS)等能源基礎設施,工業控制、人機接口、機器視覺等自動化控制,工業伺服、變頻驅動、暖通空調(HVAC)等電機驅動,以及機器人和電動工具等工業細分領域是當前市場的熱門應用。在設計這些應用時,工程師都要求更高能效、功率密度和可靠性。 作為全球第二大功率分立和模塊半導體供應商,安森美半導體以豐富的電源專知提供廣泛的產品陣容,從硅到碳化硅(SiC),從分立器件到電源模塊,以及門極驅動器、運算放大器、光耦等,乃至完整的參考設計、在線設計工具WebDesigner+、雲平台開發工具Strata Developer Studio和現場應用支援,幫助工程師解決設計挑戰,從而更快設計出具競爭優勢的方案。 太陽能方案 隨着能源和環境問題日益凸顯,太陽能作為一種清潔的可再生能源,前景可期。太陽能發電本質上是直流(DC)技術,需要逆變器(DC-AC)來發電。 從硅轉向SiC半導體可實現能效和性能的飛躍。隨着成本的優化,越來越多的遞四方速遞開始採用SiC替代原來基於硅的逆變電路,以實現更快的開關速度、更低的損耗、更低的電感/電容成本、更緊湊的尺寸。 安森美半導體除了提供各種基於硅的涵蓋5 kW至250 kW輸出功率的3電平逆變器模塊和升壓模塊,也提供大量SiC MOSFET和SiC二極管方案陣容,以及門極驅動、運算放大器等產品。 其中,NXH40B120MNQ系列全SiC升壓功率模塊已被全球領先的電源和熱管理方案供應商台達選用,用於支持其M70A三相光伏組串逆變器,實現高達98.8%的峯值能量轉換能效。 SiC技術的使用提供了實現太陽能逆變器等應用中所要求高能效水平所需的低反向恢復和快速開關特性。 以下是安森美半導體推薦的升壓及逆變器模塊。這些模塊都集成高速IGBT、Si/SiC二極管,實現高能效、緊湊的設計,內置熱敏電阻,提供高可靠性,採用焊接/壓合引腳,易於貼裝。 表1:推薦的升壓及逆變器模塊用於太陽能發電 高性能IGBT、智能功率模塊(IPM)和功率集成模塊(PIM)助力工業驅動控制 無人化和智能化正成為趨勢,電機驅動系統往往是實現節能的核心。安森美半導體提供包括分立IGBT、IPM和PIM在內的產品助力提升工業系統能效,滿足不斷提升的能效需求。 工程師評估一個IGBT性能,通常從飽和壓降、關斷損耗和抗衝擊力三方面評估,需要根據應用的不同對IGBT做權衡設計。 安森美半導體最新的IGBT工藝技術是用於950 V、750 V和650 V的場截止型第四代溝槽IGBT工藝,帶來業界最優的飽和壓降設計和開關性能設計,同時超場截止(UFS)工藝的1200 V第三代場截止型IGBT工藝代表了全球最好的工藝水平,抗衝擊能力大幅提高,並且開關性能和飽和壓降水平都領先於競爭對手。 安森美半導體用於驅動控制的分立IGBT產品系列抗衝擊力強,可支持從3 A到160 A在內的電流等級,包含從DPAK到Power 247在內的各種封裝。 而全塑封的IGBT系列採用TO-3PF封裝設計,不需要絕緣墊片,降低安裝成本提高工作效率,同時可降低因為絕緣墊片帶來的熱阻損耗,提高功率密度。 IPM將IGBT、高低壓驅動芯片和外圍阻容器件、二極管封裝在一個模塊中,實現了更靈敏準確的保護功能,更簡單的外圍元器件設計、更簡化的生產工藝和更好的散熱性能。 安森美半導體提供1200 V和650 V/600 V全系列的IPM產品,最高功率等級7.5kW,採用不同的基板技術(直連銅基板(DBC)、陶瓷基板、絕緣金屬基板(IMST)),滿足工業逆變、HVAC、泵、工業風扇、空調、白家電乃至新興的工業機器人等不同應用及成本需求。 圖1:安森美半導體的IPM陣容 在中大功率工業控制領域,為支持驅動電路的多種選擇,安森美半導體推出PIM模塊,如最新的壓鑄模功率集成模塊(TM-PIM),廣泛運用於中央空調、變頻控制和伺服控制領域。 TM-PIM集成3相轉換器、逆變器和制動器,採用創新工藝、可靠的基板和環氧樹脂壓鑄模技術,比普通的凝膠填充電源模塊熱循環使用壽命提高10倍,功率循環使用壽命提高3倍。 它將助推客户終端逆變器系統長的使用壽命及高可靠性。該模塊採用先進的厚銅基板,省去底板,比普通模塊減小57%的體積,且提高30%的熱阻,大大增加功率密度。 表2:安森美半導體已發佈的TM-PIM 電動汽車充電樁方案 受政府節能和環保法規以及新基建等一系列政策驅動,汽車正迅速邁向電動汽車發展,市場期待充電樁達到更高的峯值能效以節省充電時間和增加續航里程。 充電樁按充電能力分為4級。現有充電樁多為1級或2級。而消費者最感興趣的是直流快充。隨着功率增加和速度要求的提高,對MOS和SiC的需求越來越強。採用SiC方案將比硅方案小10倍,充電時用電量少60%,達到99%的峯值能效。 表3:電動汽車充電樁按充電能力分為4級 安森美半導體為充電樁提供寬廣的方案,包括高性能MOSFET、IGBT及SiC產品陣容,實現更高能效、更環保、更快、更小、更輕、更高性價比和更快冷卻的優勢,其MOSFET和SiC陣容如下: 表4:充電樁MOS – Easy Drive:用於硬/軟開關,易驅動,實現低EMI和電壓尖峯,優化內部Rg和電容 表5:充電樁MOS – FRFET:用於軟開關拓撲,更小的Qrr和Trr實現更高的系統可靠性 表6:充電樁SiC二極管 表7:充電樁SiC二極管:第1.5代減小正向壓降(第1代正向壓降 = 1.5 V)和Qc 表8:充電樁SiC MOSFET 下面是一個15 kW/20 kW電動汽車充電樁方案:採用PFC + LLC拓撲,含6個Easy Drive MOSFET FCH040N65S3/FCH029N65S3,6個SiC二極管FFSH20120A/FFSH30120A,8個FRFET MOSFET NTHL040N65S3F/NTHL033N65S3HF,輸出端用16個SiC二極管FFSH2065B/FFSH3065B。 SiC二極管能夠提供卓越的開關性能,且比硅具有更高的可靠性,無逆向恢復電流、温度無關開關特性和卓越的熱性能,使系統具備更高能效、更快的運行頻率、提高的功率密度、降低的EMI,以及減小的系統尺寸和成本。 圖2:電動汽車充電樁典型應用框圖 服務器和工業電源市場 5G、雲數據中心電源都對高能效和功率密度有非常高的要求。SiC器件高達98%的能效,完美契合5G和雲電源市場的發展,SiC二極管用於無源PFC級,而氮化鎵(GaN)/SiC成為圖騰柱和LLC級的選擇。 在輸入電壓220 V至230 V、輸出電壓400 V的條件下,普通的硅PFC方案採用連續導通模式(CCM)、雙升壓、全橋拓撲,能效不到95%,2個電感使得開關頻率有限,且器件數多,佔位大,而SiC賦能的PFC方案採用反激圖騰柱,實現更高能效(98%)、頻率、功率密度和雙向功率流,更少器件數。 圖騰柱PFC是構建80PLUS®Titanium標準電源的一種高性價比方案,用於數據中心、計算應用和車載電池充電器。如Solantro的SA8000-N TP-PFC控制器結合安森美半導體的SiC MOSFET幫助實現超過99%的能效,並提供優化的開關模式、可靠的啓動、高功率密度和更低的功率損耗。 輔助電源方案 無論是能源基礎設施,電動汽車充電樁,還是服務器,都需要輔助電源。反激轉換器是最流行的輔助電源拓撲,因為它具有更少的器件數量和物料單(BOM)成本。對於小於30 W的輸出功率水平,內置高壓MOSFET的AC - DC開關電源是易於設計和緊湊尺寸的首選方法。 安森美半導體提供從3 W到30 W的廣泛的內置高壓MOSFET的AC–DC電源開關產品陣容,包括NCP107x、NCP1067x和FSL5x8等。對於> 30W的輸出功率水平,通常採用AC - DC控制器IC加外部MOSET方法,以提供設計靈活性並簡化熱管理。 為了提高功率密度和轉換能效,安森美半導體提供採用NCP1342的高頻準諧振(QR)反激方案和採用NCP1568的零電壓開關(ZVS)有源鉗位反激方案。 變壓器設計是反激設計的關鍵。為幫助電源設計減少開發工作量,安森美半導體提供一系列交鑰匙參考設計和設計電子表格工具。 安森美半導體的寬禁帶生態系統 寬禁帶可以實現太陽能逆變器、服務器電源、電動汽車充電樁等設計的能效和性能飛躍, 安森美半導體具備獨一無二的寬禁帶生態系統,包括650 V、1200 V、1700 V SiC二極管,650 V、750 V、900 V、1200 V、1700 V SiC MOSFET(全都符合車規認證),GaN高電子遷移率晶體管(HEMT),SiC和GaN驅動器及集成模塊,乃至方案、仿真模型及設計軟件等,並與整個供應鏈中的多家公司合作以降低價格並加快上市時間,其基於物理的模型平台,可以在工程師測試器件前提供其在整個温度範圍內的性能。 總結 安森美半導體廣泛的產品陣容從IGBT和功率MOSFET,到門極驅動器、運放、光耦、電源模塊,包括先進的碳化硅及寬禁帶生態系統,乃至完整的參考設計、現場應用支援和線上輔助設計的資源及工具,都可幫助工程師解決太陽能、工業驅動、電動汽車充電樁、服務器等應用領域不斷提升的能效、功率密度和性能需求,推進創新。 免責聲明:本文內容由21ic獲得授權後發佈,版權歸原作者所有,本平台僅提供信息存儲服務。文章僅代表作者個人觀點,不代表本平台立場,如有問題,請聯繫我們,謝謝!

    時間:2020-11-22 關鍵詞: 半導體 電機驅動

  • 關於現場總線技術,這篇文章我想推薦給你~

    免責聲明:本文內容由21ic獲得授權後發佈,版權歸原作者所有,本平台僅提供信息存儲服務。文章僅代表作者個人觀點,不代表本平台立場,如有問題,請聯繫我們,謝謝!

    時間:2020-11-22 關鍵詞: 通信技術 總線技術

首頁  上一頁  1 2 3 4 5 6 7 8 9 10 下一頁 尾頁
發佈文章