❶ java內存或者是緩存管理怎麼實現
暈, 你確定你是用Java,
OK,不管Java還是C#
1. 定義你的map
HashMap<String, String> map = new HashMap<String, String>();
2. 存入你的信息, 反正你得要有個唯一標識的key,以及你想存的對象
map.put("userid", "userMessage");
....
3. 每次存入後,判斷一下當前大小,java 的hashmap是size()方法,如果大於你的最大數,把原來存的前進前出刪掉
if(map.size()>100){
//map.remove(arg0)
....
}
更高級內容:
如果對map中的cache內容有過訪問,給該內容的權重+1,每次刪除的時候先將權重由小至大刪除。 這就是命中率緩存,一般緩存都是按這種演算法做的,只是具體演算法有改進
❷ Cache和Buffer的主要區別是什麼都是緩存,區別在哪
cache是高速緩沖存儲器,,介於CPU與主存之間,它的工作速度數倍於主存,全部功能由硬體實現,並且對程序員是透明的.buffer一般是主存.還有,一般buffer對程序員是不透明的,除非是底層的地程序員,偶爾會需要知道一些buffer的詳細信息(一般是嵌入式的,必須對每個地址都要自己分配),一般情況下,只要程序自己去分配就好了!
❸ hibernate一級緩存和二級緩存的區別
一級緩存:
就是Session級別的緩存。一個Session做了一個查詢操作,它會把這個操作的結果放在一級緩存中。
如果短時間內這個session(一定要同一個session)又做了同一個操作,那麼hibernate直接從一級緩存中拿,而不會再去連資料庫,取數據。
它是內置的事務范圍的緩存,不能被卸載。
二級緩存:
就是SessionFactory級別的緩存。顧名思義,就是查詢的時候會把查詢結果緩存到二級緩存中。
如果同一個sessionFactory創建的某個session執行了相同的操作,hibernate就會從二級緩存中拿結果,而不會再去連接資料庫。
這是可選的插件式的緩存,在默認情況下,SessionFactory不會啟用這個插件。
可以在每個類或每個集合的粒度上配置。緩存適配器用於把具體的緩存實現軟體與Hibernate集成。
嚴格意義上說,SessionFactory緩存分為兩類:內置緩存和外置緩存。我們通常意義上說的二級緩存是指外置緩存。
內置緩存與session級別緩存實現方式相似。前者是SessionFactory對象的一些集合屬性包含的數據,後者是指Session的一些集合屬性包含的數據
SessionFactory的內置緩存中存放了映射元數據和預定義SQL語句。
映射元數據是映射文件中數據的拷貝;
而預定義SQL語句是在Hibernate初始化階段根據映射元數據推導出來。
SessionFactory的內置緩存是只讀的,應用程序不能修改緩存中的映射元數據和預定義SQL語句,因此SessionFactory不需要進行內置緩存與映射文件的同步。
Hibernate的這兩級緩存都位於持久化層,存放的都是資料庫數據的拷貝。
緩存的兩個特性:
緩存的范圍
緩存的並發訪問策略
1、緩存的范圍
決定了緩存的生命周期以及可以被誰訪問。緩存的范圍分為三類。
事務范圍
進程范圍
集群范圍
註:
對大多數應用來說,應該慎重地考慮是否需要使用集群范圍的緩存,因為訪問的速度不一定會比直接訪問資料庫數據的速度快多少。
事務范圍的緩存是持久化層的第一級緩存,通常它是必需的;進程范圍或集群范圍的緩存是持久化層的第二級緩存,通常是可選的。
2、緩存的並發訪問策略
當多個並發的事務同時訪問持久化層的緩存的相同數據時,會引起並發問題,必須採用必要的事務隔離措施。
在進程范圍或集群范圍的緩存,即第二級緩存,會出現並發問題。
因此可以設定以下四種類型的並發訪問策略,每一種策略對應一種事務隔離級別。
事務型並發訪問策略是事務隔離級別最高,只讀型的隔離級別最低。事務隔離級別越高,並發性能就越低。
A 事務型:僅僅在受管理環境中適用。它提供了Repeatable Read事務隔離級別。
對於經常被讀但很少修改的數據,可以採用這種隔離類型,因為它可以防止臟讀和不可重復讀這類的並發問題。
B 讀寫型:提供了Read Committed事務隔離級別。僅僅在非集群的環境中適用。
對於經常被讀但很少修改的數據,可以採用這種隔離類型,因為它可以防止臟讀這類的並發問題。
C 非嚴格讀寫型:不保證緩存與資料庫中數據的一致性。
如果存在兩個事務同時訪問緩存中相同數據的可能,必須為該數據配置一個很短的數據過期時間,從而盡量避免臟讀。
對於極少被修改,並且允許偶爾臟讀的數據,可以採用這種並發訪問策略。
D 只讀型:對於從來不會修改的數據,如參考數據,可以使用這種並發訪問策略。
什麼樣的數據適合存放到第二級緩存中?
1、很少被修改的數據
2、不是很重要的數據,允許出現偶爾並發的數據
3、不會被並發訪問的數據
4、參考數據
不適合存放到第二級緩存的數據?
1、經常被修改的數據
2、財務數據,絕對不允許出現並發
3、與其他應用共享的數據。
Hibernate的二級緩存策略的一般過程如下:
1) 條件查詢的時候,總是發出一條select * from table_name where …. (選擇所有欄位)這樣的SQL語句查詢資料庫,一次獲得所有的數據對象。
2) 把獲得的所有數據對象根據ID放入到第二級緩存中。
3) 當Hibernate根據ID訪問數據對象的時候,首先從Session一級緩存中查;查不到,如果配置了二級緩存,那麼從二級緩存中查;查不到,再查詢資料庫,把結果按照ID放入到緩存。
4) 刪除、更新、增加數據的時候,同時更新緩存。
註:
Hibernate的二級緩存策略,是針對於ID查詢的緩存策略,對於條件查詢則毫無作用。為此,Hibernate提供了針對條件查詢的Query緩存。
Query緩存策略的過程如下:
1) Hibernate首先根據這些信息組成一個Query Key,Query Key包括條件查詢的請求一般信息:SQL, SQL需要的參數,記錄范圍(起始位置rowStart,最大記錄個數maxRows),等。
2) Hibernate根據這個Query Key到Query緩存中查找對應的結果列表。如果存在,那麼返回這個結果列表;如果不存在,查詢資料庫,獲取結果列表,把整個結果列表根據Query Key放入到Query緩存中。
3) Query Key中的SQL涉及到一些表名,如果這些表的任何數據發生修改、刪除、增加等操作,這些相關的Query Key都要從緩存中清空。
❹ java程序員最常用的技術有哪些
Java的技術體系是非常龐大的,需要我們學習的技術非常多,往往很多初學的人,通過互聯網查閱了一個龐大的學習列表,然後不知道如何下手。網上很多大牛列的技術不是不重要,但是掌握住企業應用的Java的核心技術,快速上手,是一種高效的學習手段。結合本人十餘年的項目研發和帶人經驗,整理出如下方面。
首先JavaSE是核心,這是初學Java人員應最先接觸學習的部分。Java的運行原理,jdk的配置,與jre的區別,基本數據類型,流程式控制制(順序結構、選擇結構、循環結構),數組、集合框架,異常處理等,這些都是比較容易學習的,需要多練習,在練習過程中加強理解。面向對象部分是Java初學者,尤其是沒有任何編程語言基礎的學起來有些難度,類、對象、繼承、封裝、多態等技術點需要多參照些現有的設計模型,學習設計的思路。諸如工廠模式、觀察者模式、代理模式等重要的設計模式也是需要學習的,否則在將來應用框架時就會只知其然,不知其所以然。IO流、多線程也一定需要學習,尤其是XML、JSON等文件格式一定要掌握,這在數據交互時常用。
其次是資料庫知識,作為初級Java程序員必須要掌握一種常用的關系型書庫的應用,如MySQL或oracle等,資料庫對象諸如表、視圖等的創建、增刪改查語句,尤其是查詢,在企業中經常需要從十幾張表、或幾十張表中查詢數據,所以對於如何進行內連接、外連接、以及聯合查詢等一定要掌握,另外對於索引、事務等也要掌握。
第三是Java Web部分,由於Java主要做web開發,一些前端技術HTML3、CSS5,javaScript,jQuery等這些不見得要學得有多深入,但是必須要掌握,tomcat、jsp,以及和資料庫的交互這些都是必須要掌握的。
第四是框架部分,主流的ORM框架有Mybatis、hibernate,MVC框架有Spring MVC、Struts2等,可以優先掌握主流的SSM框架組合,框架的學習有人認為很簡單,就按照規定、規范調用、使用唄,初學者可以先學習如何使用這些框架,然後慢慢的探究內部原理,因為框架是技術封裝、簡化的產物。
這裡面有些同類型的技術比如hibernate,如果會使用Mybatis了,那麼上手就會很容易,同理如果Spring MVC框架應用熟練了,那麼Struts2框架其實就可以現學現賣了。
一個web程序包含的模塊很多,不一定包括所有模塊。
系統模塊:Windows、Linux系統等。
存儲模塊:這里既包括關系型資料庫MySQL、oracle等,也包括內存資料庫redis、memcached等。
程序模塊:還可以細化成持久化模塊、業務邏輯模塊、表現層模塊,MVC框架的實現。
搜索模塊:應用solr或Elasticsearch等。
伺服器模塊:tomcat、weblogic、Resion等
中間件模塊:nginx、MQ消息隊列技術等。
在這里額外說一下技術和技能的區別,初學者學一個技術可能很容易,但是這個技術如何在企業開發環境中應用這就是技能了,所以在學習的同時,要多應用,最好通過一些案例項目來學習,這樣既高效,學習的還扎實。
補充一點,現在的應用級別越來越大,海量數據、高並發是處理的重點,單應用的程序已經無法滿足要求,分布式是趨勢,Dubbo、Zookeeper、Docker、SpringBoot、SpringCloud、MyCat等技術,包括上面系統模塊里提到的一些技術都要學習的。
❺ 緩存與寄存器關系
緩存是用來存放從內存中取出的指令和數據,用來提高cpu訪問內存的速度
而寄存器是用來存放cpu在執行指令時所需要的操作數或執行結果
寄存器的內容可以通過編程式控制制,也就是說對程序員而言是可見的,而緩存不能通過編程式控制制,對程序員而言是透明的。
❻ (暢想)如何改進編程模式及cpu體系結構防止緩沖區溢出,不要求標准答案,只要想像得有道理
緩沖區溢出。本文首先解釋什麼是緩沖區溢出,以及它們為何如此常見和如此危險。然後討論廣泛用於解決緩沖區溢出的新 Linux 和 UNIX 方法 ―― 以及為什麼這些方法還不足夠。隨後將展示 C/C++ 程序中防止緩沖區溢出的各種方法,同時包括靜態調整大小的方法(比如標準的 C 庫和 OpenBSD/strlcpy 解決方案)和動態調整大小的解決方案,以及一些將為您提供幫助的工具。最後,本文以一些關於緩沖區溢出缺陷的未來發展形勢的預測來結束全文的討論。
如果希望自己的程序是安全的,您需要知道什麼是緩沖區溢出,如何防止它們,可以採用哪些最新的自動化工具來防止它們(以及為什麼這些工具還不足夠),還有如何在您自己的程序中防止它們。
什麼是緩沖區溢出?
緩沖區以前可能被定義為「包含相同數據類型的實例的一個連續計算機內存塊」。在 C 和 C++ 中,緩沖區通常是使用數組和諸如 malloc() 和 new 這樣的內存分配常式來實現的。極其常見的緩沖區種類是簡單的字元數組。 溢出 是指數據被添加到分配給該緩沖區的內存塊之外。
如果攻擊者能夠導致緩沖區溢出,那麼它就能控製程序中的其他值。雖然存在許多利用緩沖區溢出的方法,不過最常見的方法還是「stack-smashing」攻擊。Elias Levy (又名為 Aleph One)的一篇經典文章「Smashing the Stack for Fun and Profit」解釋了 stack-smashing 攻擊,Elias Levy 是 Bugtraq 郵件列表(請參閱 參考資料 以獲得相關鏈接)的前任主持人。
清單 1. 一個簡單的程序
void function1(int a, int b, int c) {
char buffer1[5];
gets(buffer1); /* DON'T DO THIS */
}
void main() {
function(1,2,3);
}
假設使用 gcc 來編譯清單 1 中的簡單程序,在 X86 上的 Linux 中運行,並且緊跟在對 gets() 的調用之後中止。此時的內存內容看起來像什麼樣子呢?答案是它看起來類似圖 1,其中展示了從左邊的低位地址到右邊的高位地址排序的內存布局。
圖 1. 堆棧視圖
內存的底部 內存的頂部
buffer1 sfp ret a b c
<--- 增長 --- [ ] [ ] [ ] [ ] [ ] [ ] ...
為什麼緩沖區溢出如此常見?
在幾乎所有計算機語言中,不管是新的語言還是舊的語言,使緩沖區溢出的任何嘗試通常都會被該語言本身自動檢測並阻止(比如通過引發一個異常或根據需要給緩沖區添加更多空間)。但是有兩種語言不是這樣:C 和 C++ 語言。C 和 C++ 語言通常只是讓額外的數據亂寫到其餘內存的任何位置,而這種情況可能被利用從而導致恐怖的結果。更糟糕的是,用 C 和 C++ 編寫正確的代碼來始終如一地處理緩沖區溢出則更為困難;很容易就會意外地導致緩沖區溢出。除了 C 和 C++ 使用得 非常廣泛外,上述這些可能都是不相關的事實;例如,Red Hat Linux 7.1 中 86% 的代碼行都是用 C 或 C ++ 編寫的。因此,大量的代碼對這個問題都是脆弱的,因為實現語言無法保護代碼避免這個問題。
在 C 和 C++ 語言本身中,這個問題是不容易解決的。該問題基於 C 語言的根本設計決定(特別是 C 語言中指針和數組的處理方式)。由於 C++ 是最兼容的 C 語言超集,它也具有相同的問題。存在一些能防止這個問題的 C/C++ 兼容版本,但是它們存在極其嚴重的性能問題。而且一旦改變 C 語言來防止這個問題,它就不再是 C 語言了。許多語言(比如 Java 和 C#)在語法上類似 C,但它們實際上是不同的語言,將現有 C 或 C++ 程序改為使用那些語言是一項艱巨的任務。
然而,其他語言的用戶也不應該沾沾自喜。有些語言存在允許緩沖區溢出發生的「轉義」子句。Ada 一般會檢測和防止緩沖區溢出(即針對這樣的嘗試引發一個異常),但是不同的程序可能會禁用這個特性。C# 一般會檢測和防止緩沖區溢出,但是它允許程序員將某些常式定義為「不安全的」,而這樣的代碼 可能 會導致緩沖區溢出。因此如果您使用那些轉義機制,就需要使用 C/C++ 程序所必須使用的相同種類的保護機制。許多語言都是用 C 語言來實現的(至少部分是用 C 語言來實現的 ),並且用任何語言編寫的所有程序本質上都依賴用 C 或 C++ 編寫的庫。因此,所有程序都會繼承那些問題,所以了解這些問題是很重要的。
導致緩沖區溢出的常見 C 和 C++ 錯誤
從根本上講,在程序將數據讀入或復制到緩沖區中的任何時候,它需要在復制 之前檢查是否有足夠的空間。能夠容易看出來的異常就不可能會發生 ―― 但是程序通常會隨時間而變更,從而使得不可能成為可能。
遺憾的是,C 和 C++ 附帶的大量危險函數(或普遍使用的庫)甚至連這點(指檢查空間)也無法做到。程序對這些函數的任何使用都是一個警告信號,因為除非慎重地使用它們,否則它們就會成為程序缺陷。您不需要記住這些函數的列表;我的真正目的是說明這個問題是多麼普遍。這些函數包括 strcpy(3)、strcat(3)、sprintf(3) (及其同類 vsprintf(3) )和 gets(3) 。 scanf() 函數集( scanf(3)、fscanf(3)、sscanf(3)、vscanf(3)、vsscanf(3) 和 vfscanf(3) )可能會導致問題,因為使用一個沒有定義最大長度的格式是很容易的(當讀取不受信任的輸入時,使用格式「%s」總是一個錯誤)。
其他危險的函數包括 realpath(3)、getopt(3)、getpass(3)、streadd(3)、strecpy(3) 和 strtrns(3) 。 從理論上講, snprintf() 應該是相對安全的 ―― 在現代 GNU/Linux 系統中的確是這樣。但是非常老的 UNIX 和 Linux 系統沒有實現 snprintf() 所應該實現的保護機制。
Microsoft 的庫中還有在相應平台上導致同類問題的其他函數(這些函數包括 wcscpy()、_tcscpy()、_mbscpy()、wcscat()、_tcscat()、_mbscat() 和 CopyMemory() )。注意,如果使用 Microsoft 的 MultiByteToWideChar() 函數,還存在一個常見的危險錯誤 ―― 該函數需要一個最大尺寸作為字元數目,但是程序員經常將該尺寸以位元組計(更普遍的需要),結果導致緩沖區溢出缺陷。
另一個問題是 C 和 C++ 對整數具有非常弱的類型檢查,一般不會檢測操作這些整數的問題。由於它們要求程序員手工做所有的問題檢測工作,因此以某種可被利用的方式不正確地操作那些整數是很容易的。特別是,當您需要跟蹤緩沖區長度或讀取某個內容的長度時,通常就是這種情況。但是如果使用一個有符號的值來存儲這個長度值會發生什麼情況呢 ―― 攻擊者會使它「成為負值」,然後把該數據解釋為一個實際上很大的正值嗎?當數字值在不同的尺寸之間轉換時,攻擊者會利用這個操作嗎?數值溢出可被利用嗎? 有時處理整數的方式會導致程序缺陷。
防止緩沖區溢出的新技術
當然,要讓程序員 不犯常見錯誤是很難的,而讓程序(以及程序員)改為使用另一種語言通常更為困難。那麼為何不讓底層系統自動保護程序避免這些問題呢?最起碼,避免 stack-smashing 攻擊是一件好事,因為 stack-smashing 攻擊是特別容易做到的。
一般來說,更改底層系統以避免常見的安全問題是一個極好的想法,我們在本文後面也會遇到這個主題。事實證明存在許多可用的防禦措施,而一些最受歡迎的措施可分組為以下類別:
基於探測方法(canary)的防禦。這包括 StackGuard(由 Immunix 所使用)、ProPolice(由 OpenBSD 所使用)和 Microsoft 的 /GS 選項。
非執行的堆棧防禦。這包括 Solar Designer 的 non-exec 補丁(由 OpenWall 所使用)和 exec shield(由 Red Hat/Fedora 所使用)。
其他方法。這包括 libsafe(由 Mandrake 所使用)和堆棧分割方法。
遺憾的是,迄今所見的所有方法都具有弱點,因此它們不是萬能葯,但是它們會提供一些幫助。
基於探測方法的防禦
研究人員 Crispen Cowan 創建了一個稱為 StackGuard 的有趣方法。Stackguard 修改 C 編譯器(gcc),以便將一個「探測」值插入到返回地址的前面。「探測儀」就像煤礦中的探測儀:它在某個地方出故障時發出警告。在任何函數返回之前,它執行檢查以確保探測值沒有改變。如果攻擊者改寫返回地址(作為 stack-smashing 攻擊的一部分),探測儀的值或許就會改變,系統內就會相應地中止。這是一種有用的方法,不過要注意這種方法無法防止緩沖區溢出改寫其他值(攻擊者仍然能夠利用這些值來攻擊系統)。人們也曾擴展這種方法來保護其他值(比如堆上的值)。Stackguard(以及其他防禦措施)由 Immunix 所使用。
IBM 的 stack-smashing 保護程序(ssp,起初名為 ProPolice)是 StackGuard 的方法的一種變化形式。像 StackGuard 一樣,ssp 使用一個修改過的編譯器在函數調用中插入一個探測儀以檢測堆棧溢出。然而,它給這種基本的思路添加了一些有趣的變化。 它對存儲局部變數的位置進行重新排序,並復制函數參數中的指針,以便它們也在任何數組之前。這樣增強了ssp 的保護能力;它意味著緩沖區溢出不會修改指針值(否則能夠控制指針的攻擊者就能使用指針來控製程序保存數據的位置)。默認情況下,它不會檢測所有函數,而只是檢測確實需要保護的函數(主要是使用字元數組的函數)。從理論上講,這樣會稍微削弱保護能力,但是這種默認行為改進了性能,同時仍然能夠防止大多數問題。考慮到實用的因素,它們以獨立於體系結構的方式使用 gcc 來實現它們的方法,從而使其更易於運用。從 2003 年 5 月的發布版本開始,廣受贊譽的 OpenBSD(它重點關注安全性)在他們的整個發行套件中使用了 ssp(也稱為 ProPolice)。
Microsoft 基於 StackGuard 的成果,添加了一個編譯器標記(/GS)來實現其 C 編譯器中的探測儀。
非執行的堆棧防禦
另一種方法首先使得在堆棧上執行代碼變得不可能。 遺憾的是,x86 處理器(最常見的處理器)的內存保護機制無法容易地支持這點;通常,如果一個內存頁是可讀的,它就是可執行的。一個名叫 Solar Designer 的開發人員想出了一種內核和處理器機制的聰明組合,為 Linux 內核創建了一個「非執行的堆棧補丁」;有了這個補丁,堆棧上的程序就不再能夠像通常的那樣在 x86 上運行。 事實證明在有些情況下,可執行程序 需要在堆棧上;這包括信號處理和跳板代碼(trampoline)處理。trampoline 是有時由編譯器(比如 GNAT Ada 編譯器)生成的奇妙結構,用以支持像嵌套子常式之類的結構。Solar Designer 還解決了如何在防止攻擊的同時使這些特殊情況不受影響的問題。
一段時間之後,人們又想出了一種防止該問題的新思路:將所有可執行代碼轉移到一個稱為「ASCII 保護(ASCII armor)」區域的內存區。要理解這是如何工作的,就必須知道攻擊者通常不能使用一般的緩沖區溢出攻擊來插入 ASCII NUL 字元(0)這個事實。 這意味著攻擊者會發現,要使一個程序返回包含 0 的地址是很困難的。由於這個事實,將所有可執行代碼轉移到包含 0 的地址就會使得攻擊該程序困難多了。
具有這個屬性的最大連續內存范圍是從 0 到 0x01010100 的一組內存地址,因此它們就被命名為 ASCII 保護區域(還有具有此屬性的其他地址,但它們是分散的)。與非可執行的堆棧相結合,這種方法就相當有價值了:非可執行的堆棧阻止攻擊者發送可執行代碼,而 ASCII 保護內存使得攻擊者難於通過利用現有代碼來繞過非可執行堆棧。這樣將保護程序代碼避免堆棧、緩沖區和函數指針溢出,而且全都不需重新編譯。
然而,ASCII 保護內存並不適用於所有程序;大程序也許無法裝入 ASCII 保護內存區域(因此這種保護是不完美的),而且有時攻擊者 能夠將 0 插入目的地址。 此外,有些實現不支持跳板代碼,因此可能必須對需要這種保護的程序禁用該特性。Red Hat 的 Ingo Molnar 在他的「exec-shield」補丁中實現了這種思想,該補丁由 Fedora 核心(可從 Red Hat 獲得它的免費版本)所使用。最新版本的 OpenWall GNU/Linux (OWL)使用了 Solar Designer 提供的這種方法的實現(請參閱 參考資料 以獲得指向這些版本的鏈接)。
其他方法
還有其他許多方法。一種方法就是使標准庫對攻擊更具抵抗力。Lucent Technologies 開發了 Libsafe,這是多個標准 C 庫函數的包裝,也就是像 strcpy() 這樣已知的對 stack-smashing 攻擊很脆弱的函數。Libsafe 是在 LGPL 下授予許可證的開放源代碼軟體。那些函數的 libsafe 版本執行相關的檢查,確保數組改寫不會超出堆棧楨。然而,這種方法僅保護那些特定的函數,而不是從總體上防止堆棧溢出缺陷,並且它僅保護堆棧,而不保護堆棧中的局部變數。它們的最初實現使用了 LD_PRELOAD ,而這可能與其他程序產生沖突。Linux 的 Mandrake 發行套件(從 7.1 版開始)包括了 libsafe。
另一種方法稱為「分割控制和數據堆棧」―― 基本的思路是將堆棧分割為兩個堆棧,一個用於存儲控制信息(比如「返回」地址),另一個用於控制其他所有數據。Xu et al. 在 gcc 中實現了這種方法,StackShield 在匯編程序中實現了這種方法。這樣使得操縱返回地址困難多了,但它不會阻止改變調用函數的數據的緩沖區溢出攻擊。
事實上還有其他方法,包括隨機化可執行程序的位置;Crispen 的「PointGuard」將這種探測儀思想引申到了堆中,等等。如何保護當今的計算機現在已成了一項活躍的研究任務。
一般保護是不足夠的
如此多不同的方法意味著什麼呢?對用戶來說,好的一面在於大量創新的方法正在試驗之中;長期看來,這種「競爭」會更容易看出哪種方法最好。而且,這種多樣性還使得攻擊者躲避所有這些方法更加困難。然而,這種多樣性也意味著開發人員需要 避免編寫會干擾其中任何一種方法的代碼。這在實踐上是很容易的;只要不編寫對堆棧楨執行低級操作或對堆棧的布局作假設的代碼就行了。即使不存在這些方法,這也是一個很好的建議。
操作系統供應商需要參與進來就相當明顯了:至少挑選一種方法,並使用它。緩沖區溢出是第一號的問題,這些方法中最好的方法通常能夠減輕發行套件中幾乎半數已知缺陷的影響。可以證明,不管是基於探測儀的方法更好,還是基於非可執行堆棧的方法更好,它們都具有各自的優點。可以將它們結合起來使用,但是少數方法不支持這樣使用,因為附加的性能損失使得這樣做不值得。我並沒有其他意思,至少就這些方法本身而言是這樣;libsafe 和分割控制及數據堆棧的方法在它們所提供的保護方面都具有局限性。當然,最糟糕的解決辦法就是根本不對這個第一號的缺陷提供保護。還沒有實現一種方法的軟體供應商需要立即計劃這樣做。從 2004 年開始,用戶應該開始避免使用這樣的操作系統,即它們至少沒有對緩沖區溢出提供某種自動保護機制。
然而,沒有哪種方法允許開發人員忽略緩沖區溢出。所有這些方法都能夠被攻擊者破壞。 攻擊者也許能夠通過改變函數中其他數據的值來利用緩沖區溢出;沒有哪種方法能夠防止這點。如果能夠插入某些難於創建的值(比如 NUL 字元),那麼這其中的許多方法都能被攻擊者繞開;隨著多媒體和壓縮數據變得更加普遍,攻擊者繞開這些方法就更容易了。從根本上講,所有這些方法都能減輕從程序接管攻擊到拒絕服務攻擊的緩沖區溢出攻擊所帶來的破壞。遺憾的是,隨著計算機系統在更多關鍵場合的使用,即使拒絕服務通常也是不可接受的。因而,盡管發行套件應該至少包括一種適當的防禦方法,並且開發人員應該使用(而不是反對)那些方法,但是開發人員仍然需要最初就編寫無缺陷的軟體。
C/C++ 解決方案
針對緩沖區溢出的一種簡單解決辦法就是轉為使用能夠防止緩沖區溢出的語言。畢竟,除了 C 和 C++ 外,幾乎每種高級語言都具有有效防止緩沖區溢出的內置機制。但是許多開發人員因為種種原因還是選擇使用 C 和 C++。那麼您能做什麼呢?
事實證明存在許多防止緩沖區溢出的不同技術,但它們都可劃分為以下兩種方法:靜態分配的緩沖區和動態分配的緩沖區。首先,我們將講述這兩種方法分別是什麼。然後,我們將討論靜態方法的兩個例子(標准 C strncpy/strncat 和 OpenBSD 的 strlcpy/strlcat ),接著討論動態方法的兩個例子(SafeStr 和 C++ 的 std::string )。
重要選擇:靜態和動態分配的緩沖區
緩沖區具有有限的空間。因此實際上存在處理緩沖區空間不足的兩種可能方式。
「靜態分配的緩沖區」方法:也就是當緩沖區用完時,您抱怨並拒絕為緩沖區增加任何空間。
「動態分配的緩沖區」方法:也就是當緩沖區用完時,動態地將緩沖區大小調整到更大的尺寸,直至用完所有內存。
靜態方法具有一些缺點。事實上,靜態方法有時可能會帶來不同的缺陷。靜態方法基本上就是丟棄「過多的」數據。如果程序無論如何還是使用了結果數據,那麼攻擊者會嘗試填滿緩沖區,以便在數據被截斷時使用他希望的任何內容來填充緩沖區。如果使用靜態方法,應該確保攻擊者能夠做的最糟糕的事情不會使得預先的假設無效,而且檢查最終結果也是一個好主意。
動態方法具有許多優點:它們能夠向上適用於更大的問題(而不是帶來任意的限制),而且它們沒有導致安全問題的字元數組截斷問題。但它們也具有自身的問題:在接受任意大小的數據時,可能會遇到內存不足的情況 ―― 而這在輸入時也許不會發生。任何內存分配都可能會失敗,而編寫真正很好地處理該問題的 C 或 C++ 程序是很困難的。甚至在內存真正用完之前,也可能導致計算機變得太忙而不可用。簡而言之,動態方法通常使得攻擊者發起拒絕服務攻擊變得更加容易。因此仍然需要限制輸入。此外,必須小心設計程序來處理任意位置的內存耗盡問題,而這不是一件容易的事情。
標准 C 庫方法
最簡單的方法之一是簡單地使用那些設計用於防止緩沖區溢出的標准 C 庫函數(即使在使用 C ++,這也是可行的),特別是 strncpy(3) 和 strncat(3) 。這些標准 C 庫函數一般支持靜態分配方法,也就是在數據無法裝入緩沖區時丟棄它。這種方法的最大優點在於,您可以肯定這些函數在任何機器上都可用,並且任何 C/C++ 開發人員都會了解它們。許許多多的程序都是以這種方式編寫的,並且確實可行。
遺憾的是,要正確地做到這點卻是令人吃驚的困難。下面是其中的一些問題:
strncpy(3) 和 strncat(3) 都要求您給出 剩餘的空間,而不是給出緩沖區的總大小。這之所以會成為問題是因為,雖然緩沖區的大小一經分配就不會變化,但是緩沖區中剩餘的空間量會在每次添加或刪除數據時發生變化。這意味著程序員必須始終跟蹤或重新計算剩餘的空間。這種跟蹤或重新計算很容易出錯,而任何錯誤都可能給緩沖區攻擊打開方便之門。
在發生了溢出(和數據丟失)時,兩個函數都不會給出簡單的報告,因此如果要檢測緩沖區溢出,程序員就必須做更多的工作。
如果源字元串至少和目標一樣長,那麼函數 strncpy(3) 還不會使用 NUL 來結束字元串;這可能會在以後導致嚴重破壞。因而,在運行 strncpy(3) 之後,您通常需要重新結束目標字元串。
函數 strncpy(3) 還可以用來僅把源字元串的 一部分復制到目標中。 在執行這個操作時,要復制的字元的數目通常是基於源字元串的相關信息來計算的。 這樣的危險之處在於,如果忘了考慮可用的緩沖區空間,那麼 即使在使用 strncpy(3) 時也可能會留下緩沖區攻擊隱患。這個函數也不會復制 NUL 字元,這可能也是一個問題。
可以通過一種防止緩沖區溢出的方式使用 sprintf() ,但是意外地留下緩沖區溢出攻擊隱患是非常容易的。 sprintf() 函數使用一個控制字元串來指定輸出格式,該控制字元串通常包括「 %s 」(字元串輸出)。如果指定字元串輸出的精確指定符(比如 %.10s ),那麼您就能夠通過指定輸出的最大長度來防止緩沖區溢出。甚至可以使用「 * 」作為精確指定符(比如「 %.*s 」),這樣您就可以傳入一個最大長度值,而不是在控制字元串中嵌入最大長度值。這樣的問題在於,很容易就會不正確地使用 sprintf() 。一個「欄位寬度」(比如「 %10s 」)僅指定了最小長度 ―― 而不是最大長度。「欄位寬度」指定符會留下緩沖區溢出隱患,而欄位寬度和精確寬度指定符看起來幾乎完全相同 ―― 唯一的區別在於安全的版本具有一個點號。另一個問題在於,精確欄位僅指定一個參數的最大長度,但是緩沖區需要針對組合起來的數據的最大尺寸調整大小。
scanf() 系列函數具有一個最大寬度值,至少 IEEE Standard 1003-2001 清楚地規定這些函數一定不能讀取超過最大寬度的數據。遺憾的是,並非所有規范都清楚地規定了這一點,我們不清楚是否所有實現都正確地實現了這些限制(這在如今的 GNU/Linux 系統上就 不能正確地工作)。如果您依賴它,那麼在安裝或初始化期間運行小測試來確保它能正確工作,這樣做將是明智的。
strncpy(3) 還存在一個惱人的性能問題。從理論上講, strncpy(3) 是 strcpy(3) 的安全替代者,但是 strncpy(3) 還會在源字元串結束時使用 NUL 來填充整個目標空間。 這是很奇怪的,因為實際上並不存在這樣做的很好理由,但是它從一開始就是這樣,並且有些程序還依賴這個特性。這意味著從 strcpy(3) 切換到 strncpy(3) 會降低性能 ―― 這在如今的計算機上通常不是一個嚴重的問題,但它仍然是有害的。
那麼可以使用標准 C 庫的常式來防止緩沖區溢出嗎?是的,不過並不容易。如果計劃沿著這條路線走,您需要理解上述的所有要點。或者,您可以使用下面幾節將要講述的一種替代方法。
OpenBSD 的 strlcpy/strlcat
OpenBSD 開發人員開發了一種不同的靜態方法,這種方法基於他們開發的新函數 strlcpy(3) 和 strlcat(3) 。這些函數執行字元串復制和拼接,不過更不容易出錯。這些函數的原型如下:
size_t strlcpy (char *dst, const char *src, size_t size);
size_t strlcat (char *dst, const char *src, size_t size);
strlcpy() 函數把以 NUL 結尾的字元串從「 src 」復制到「 dst 」(最多 size-1 個字元)。 strlcat() 函數把以 NUL 結尾的字元串 src 附加到 dst 的結尾(但是目標中的字元數目將不超過 size-1)。
初看起來,它們的原型和標准 C 庫函數並沒有多大區別。但是事實上,它們之間存在一些顯著區別。這些函數都接受目標的總大小(而不是剩餘空間)作為參數。這意味著您不必連續地重新計算空間大小,而這是一項易於出錯的任務。此外,只要目標的大小至少為 1,兩個函數都保證目標將以 NUL 結尾(您不能將任何內容放入零長度的緩沖區)。如果沒有發生緩沖區溢出,返回值始終是組合字元串的長度;這使得檢測緩沖區溢出真正變得容易了。
遺憾的是, strlcpy(3) 和 strlcat(3) 並不是在類 UNIX 系統的標准庫中普遍可用。OpenBSD 和 Solaris 將它們內置在 <string.h> 中,但是 GNU/Linux 系統卻不是這樣。這並不是一件那麼困難的事情;因為當底層系統沒有提供它們時,您甚至可以將一些小函數直接包括在自己的程序源代碼中。
SafeStr
Messier 和 Viega 開發了「SafeStr」庫,這是一種用於 C 的動態方法,它自動根據需要調整字元串的大小。使用 malloc() 實現所使用的相同技巧,Safestr 字元串很容易轉換為常規的 C「 char * 」字元串:safestr 在傳遞指針「之前」的地址處存儲重要信息。這種技術的優點在於,在現有程序中使用 SafeStr 將會很容易。SafeStr 還支持「只讀」和「受信任」的字元串,這也可能是有用的。這種方法的一個問題在於它需要 XXL(這是一個給 C 添加異常處理和資源管理支持的庫),因此您實際上要僅為了處理字元串而引入一個重要的庫。Safestr 是在開放源代碼的 BSD 風格的許可證下發布的。
C++ std::string
針對 C++ 用戶的另一種解決方案是標準的 std::string 類,這是一種動態的方法(緩沖區根據需要而增長)。它幾乎是不需要傷腦筋的,因為 C++ 語言直接支持該類,因此不需要做特殊的工作就可使用它,並且其他庫也可能會使用它。就其本身而言, std::string 通常會防止緩沖區溢出,但是如果通過它提取一個普通 C 字元串(比如使用 data() 或 c_str() ),那麼上面討論的所有問題都會重新出現。還要記住 data() 並不總是返回以 NUL 結尾的字元串。
由於種種歷史原因,許多 C++ 庫和預先存在的程序都創建了它們自己的字元串類。這可能使得 std::string 更難於使用,並且在使用那些庫或修改那些程序時效率很低,因為不同的字元串類型將不得不連續地來回轉換。並非其他所有那些字元串類都會防止緩沖區溢出,並且如果它們對 C 不受保護的 char* 類型執行自動轉換,那麼緩沖區溢出缺陷很容易引入那些類中。
❼ CPU里專用寄存器和通用寄存器是不是一、二級緩存
寄存器組可分為專用寄存器和通用寄存器。專用寄存器的作用是固定的,分別寄存相應的數據。而通用寄存器用途廣泛並可由程序員規定其用途。通用寄存器的數目因微處理器而異。這里說的寄存器全部都指的是一級緩存即L1
❽ OPENGL 緩沖區中的顏色緩存的功能是什麼
顏色緩存
指程序員繪圖所用的緩存,分為:
左、右緩存——用於立體感視圖(必須要有左緩存);
前、後緩存——用於雙緩存(必須要有前緩存);
4個輔助緩存——可選擇的、不可顯示(程序員可以自己定義和使用它們)。
❾ 為什麼說cache對程序員是透明的
cache對程序員是透明的是因為程序員不需要知道其運行原理。因為程序員不需要知道cache的緩存機制,直接調用cache介面即可實現cache緩存。
cache獨一無二地提供了三種整合的、能並發訪問同一數據的資料庫技術:成熟的對象訪問方式,高性能的 SQL 訪問方式以及豐富的多維訪問。在對象數據、關系型數據以及多維數據視圖之間不需要映射,這樣就大大節省了開發和運行時間。
(9)程序員常見緩存種類擴展閱讀:
cache的作用
Caché提供了快速 Web 應用開發、高速的事務處理、大規模的擴展性、對事務數據的實時查詢。 Caché運行概述對Caché架構和性能進行了深層次的描述。 Caché的技術優勢主要在為什麼選擇Caché這一文檔中稱述。
在小冊子以多維引擎全面整合對象和 SQL 中,你可以了解到後關系型技術更多的優勢。 Caché問與答中主要回答了一些關於Caché的常見問題,以及為什麼增值商和企業選擇Caché來提升他們應用的性能。
❿ mybatis的緩存機制是怎麼樣的
通常為了減輕資料庫的壓力,我們會引入緩存。在Dao查詢資料庫之前,先去緩存中找是否有要找的數據,如果有則用緩存中的數據即可,就不用查詢資料庫了。如果沒有才去資料庫中查找。這樣就能分擔一下資料庫的壓力。另外,為了讓緩存中的數據與資料庫同步,我們應該在該數據發生變化的地方加入更新緩存的邏輯代碼。這樣無形之中增加了工作量,同時也是一種對原有代碼的入侵。這對於有著代碼潔癖的程序員來說,無疑是一種傷害。MyBatis框架早就考慮到了這些問題,因此MyBatis提供了自定義的二級緩存概念,方便引入我們自己的緩存機制,而不用更改原有的業務邏輯。