• <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>
            隨筆 - 74, 文章 - 0, 評論 - 26, 引用 - 0
            數據加載中……

            新時代的門前 32位世界中的64位編程

            為16、32、64位架構編寫可移植代碼

              與16位相比,32位意味著程序更快、可直接尋址訪問更多的內存和更好的處理器架構。鑒于此,越來越多的程序員已經開始考慮利用64位處理器所帶來的巨大優(yōu)勢了。

              克雷研究(Cray Research 應為品牌名)計算機已經開始使用64位字,并可訪問更大的內存地址。然而,作為正在向開發(fā)標準軟件和與其他操作系統(tǒng)相互努力的一部分,我們已經停止了移植那些原本基于32位處理器的代碼。事實上,我們不斷遇到我們稱之為“32位主義”的代碼,這些代碼都是在假定機器字長為32位的情況下編寫的,因而很難移植這種代碼,所以必須確立一些簡單的指導方針,以便助于編寫跨16、32、64位處理器平臺的代碼。

              由于有一些遺留問題,C語言在數據類型和數據構造方面,顯得有點過剩了。可以使用的不僅僅是char、short、int和long類型,還有相應的unsigned(無符號)類型,當然你可在結構(structure)和聯(lián)合(union)中混合使用,可以在聯(lián)合中包含結構,再在結構中包含聯(lián)合,如果還嫌數據類型不夠復雜,還可以轉換成比特位,當然也可以把一種數據類型轉換成另一種你想要的數據類型。正是因為這些工具太強大,如果不安全地使用它們,就有可能會傷到自己了。

              高級代碼的高級結構

              在Kernighan和Plauger經典的《The Elements of Programming Style》一書中,他們的建議是“選擇使程序看上去簡單的數據表示法”。對個人而言,這意味著為高級編程使用高級數據結構,而低級編程使用低級數據結構。

              在我們移植的程序中,有一個常見的32位主義bug,不如拿它來做個例子,科內爾大學編寫的用于網間互訪的路由協(xié)議引擎,在伯克利網絡環(huán)境下(指TCP/IP),自然是使用inet_addr( )來把表示Internet地址的字符串轉換成二進制形式。Internet地址碰巧也是32位的,就如運行伯克利網絡系統(tǒng)的其他計算機一樣,都是有著同樣的字寬。

              但也存在著有關Internet地址的高級定義:in_ addr結構。這個結構定義包括了子域s_ addr,它是一個包含了Internet地址的unsigned long標量。inet_addr()接受一個指向字符的指針,并且返回一個unsigned long,在轉換地址字符串過程中如果發(fā)生錯誤,inet_addr將返回-1。

              程序Gated讀取以文本格式存放Internet地址的配置文件,并把它們放入sockaddr_in(這是一個包含了結構in_addr的高級結構)。例1(a)中的代碼可以在32位電腦上正常運行,但移植到克雷研究計算機上,卻無法運行,為什么呢?

              例1:高級代碼的高級結構

              (a)

            struct sockaddr_in saddrin
            char *str;

            if ((saddrin.sin_addr.s_addr = inet_addr(str)) == (unsigned long)-1) {
             do_some_error_handling;
            }

              (b)

            struct sockaddr_in saddrin
            char *str;

            if (inet_aton(str, &saddrin.sin_addr) ! = OK) {
             do_some_error_handling;
            }

              因為只要inet_addr能夠正確地解析字符串,那么一切OK。當inet_addr在64位計算機上返回一個錯誤時,這段代碼卻未能捕捉到。你必須要考慮比較語句中的數據位寬,來確定到底是哪出了錯。

              首先,inet_addr返回錯誤值——unsigned long -1,在64位中表示為比特位全為1,這個值被存儲在結構in_addr下的子域s_addr中,而in_addr必須是32位來匹配Internet地址,所以它是一個32比特位的unsigned int(在我們的編譯器上,int是64位)。現(xiàn)在我們存儲進32個1,存儲進的值將與unsigned long -1比較。當我們存儲32個1于unsigned int時,編譯器自動把32位提升為64位;這樣,數值0x00000000 ffffffff與0xffffffff ffffffff的比較當然就失敗了。這是一個很難發(fā)現(xiàn)的bug,特別是在這種因為32位到64位的隱式提升上。

              那我們對這個bug怎么辦呢?一個解決方法是在語句中比較0xffffffff,而不是-1,但這又會使代碼更加依賴于特定大小的對象。另一個方法是,使用一個中間的unsigned long變量,從而在把結果存入sockaddr_in前,執(zhí)行比較,但這會讓程序代碼更復雜。

              真正的問題所在是,我們期望一個unsigned long值與一個32位量(如Internet地址)相等。Internet地址必須以32位形式進行存儲,但有些時候用一個標量,來訪問這個地址的一部分,是非常方便的。在32位字長的電腦中,用一個long數值(常被當作32位)來訪問這個地址,看上去沒什么問題。讓我們暫時不想一個低級的數據項(32位Internet地址)是否與一個機器字相等,那么高級數據類型結構in_addr就應該被一直使用。因為in_addr中沒有無效值,那么應有一個單獨的狀態(tài)用作返回值。

              解決方案是定義一個新的函數,就像inet_addr那樣,但返回一個狀態(tài)值,而且接受一個結構in_addr作為參數,參見例1(b)。因為高級的數據元素可以一直使用,而返回的值是沒有溢出的,所以這個代碼是可以跨架構移植的,而不管字長是多少。雖然伯克利發(fā)布了NET2,其中的確定義了一個新的函數inet_aton(),但如果試著改變inet_addr()中的代碼,將會損壞許多程序。

              低級代碼的低級結構

              低級編程意味著直接操縱物理設備或者特定協(xié)議的通訊格式,例如,設備驅動程序經常使用非常精確的位模式來操縱控制寄存器。此外,網絡協(xié)議通過特定的比特位模式傳輸數據項時,也必須適當地轉譯。

              為了操縱物理數據項,此處的數據結構必須準確地反映被操縱的內容。比特位是一個不錯的選擇,因為它們正好指定了比特的位數及排列。事實上,正是這種精確度,使比特位相對于short、int、long,更好地映像了物理結構(short、int、long會因為電腦的不同而改變,而比特位不會)。

              當映像一個物理結構時,是通過定義格式來使比特位達到這種精度的,這就使你必須一直使用一種編碼風格來訪問結構。此時的每個位域都是命名的,你寫出的代碼可直接訪問這些位域。當訪問物理結構時,有件事可能你并不想去做,那就是使用標量數組(short、int、or long),訪問這些數組的代碼都假定存在一個特定的比特位寬,當移植這些代碼到一臺使用不同字寬的電腦上時,就可能不正確了。

              在我們移植PEX圖像庫時遇到的一個問題,就涉及到其映像的協(xié)議消息結構。在某臺電腦上,如果int整型的長度與消息中的元素一樣,那例2(a)中的代碼就會工作得很正常。32位的數據元素在32字長的電腦上沒有問題,拿到64位的克雷計算機上,它就出錯了。對例2(b)而言,不單要改變結構定義,還要改變所有涉及到coord數組的代碼。這樣,擺在我們面前就有兩個選擇,要么重寫涉及此消息的所有代碼,要么定義一個低級的結構和一個高級的結構,然后用一段特殊的代碼把數據從一個拷貝到另一個當中,不過我也不期望可以找出每個對zcoord = draw_ msg.coord[2]的引用,而且,當現(xiàn)在需要移植到一個新架構上時,把所有的代碼都改寫成如例2(c)所示無疑是一項艱苦的工作。這個特殊的問題是由于忽視字長的不同而帶來的,所以不能假設在可移植的代碼中機器字長、short、int、long都是具有同樣的大小。

              例2:低級代碼的低級結構

              (a)

            struct draw_msg {
             int objectid;
             int coord[3];
            }

              (b)

            struct draw_msg {
             int objectid:32;
             int coord1:32;
             int coord2:32;
             int coord3:32;
            }

              (c)

            int *iptr, *optr, *limit;
            int xyz[3];

            iptr = draw_msg.coord;
            limit = draw_msg.coord + sizeof(draw_msg.coord);

            optr = xyz;
            while (iptr < limit)
            *optr++ = *iptr++;
            結構打包和字對齊

               正是因為編譯器會對結構進行打包,所以不同計算機上字長的變化,還導致了另一個問題。C編譯器在字(word)的邊界上對齊字長,當具有一個字長的數據后面緊接著一個較小的數據時,這種方法會產生內存空缺(不過也有例外,比如說當有足夠多的小數據剛好可以填充一個字時)。

               一些聰明的程序員在聲明聯(lián)合時,往往在其中會帶有兩個或更多的結構,其中一個結構剛好填充聯(lián)合,另一個則可以用來從不同的角度來看待這個聯(lián)合,參見例3(a)。假設這段代碼是為16位字長的計算機所寫,int為16位,long為32位,那么存取這個結構的代碼將會得到正常的映射關系(如圖1),而例3(b)中的代碼也會按預期的那樣工作。可是,如果這段代碼一旦移植到另一臺具有32位字長的計算機上時,映射關系就改變了。如果新計算機上的編譯器允許你使用16位的int,那么字的對齊就會像圖2所示了,或者如果編譯器遵循K&R約定,那么int將會和一個字(32比特)一樣長,對齊就如圖3所示,在任一情況下,這都將導致問題。




              例3:結構打包和字對齊

              (a)

            union parse_hdr {
             struct hdr {
              char data1;
              char data2;
              int data3;
              int data4;
            } hdr;
            struct tkn {
             int class;
             long tag;
            } tkn;
            } parse_item;

              (b)

            char *ptr = msgbuf;

            parse_item.hdr.data1 = *ptr++;
            parse_item.hdr.data2 = *ptr++;
            parse_item.hdr.data3 = (*ptr++ << 8 | *ptr++);
            parse_item.hdr.data4 = (*ptr++ << 8 | *ptr++);

            if (parse.tkn.class >= MIN_TOKEN_CLASS && parse.tkn.class <= MAX_TOKEN_CLASS) {
             interpret_tag(parse.tkn.tag);
            }

              在第一個情況中(圖2),tag域不是像期望的那樣線性拉長,而被填充了一些垃圾。而在第二個情況中(圖3),無論是class還是tag域,都不再有意義,兩個char值因為被打包成一個int,所以也都不再正確。再次強調,首先不要假設標準數據類型大小一樣,其次還要了解它們是怎樣被映射成其他數據類型的,這才是書寫可移植代碼的最好方法。

              機器尋址特性

              幾乎所有的處理器都在字邊界上以字為單位進行尋址,而且通常都為此作了一些優(yōu)化。另有一些的處理器允許其他類型的尋址,如以字節(jié)為單位尋址、或在半個字邊界上以半字為單位尋址,甚至還有一些處理器有輔助硬件允許在奇數邊界上同時以字和半字進行尋址。

              尋址機制在不同計算機上會有所變化,最快的尋址模式是在字邊界上以字為單位進行尋址。其他方式的尋址需要輔助硬件,通常都會對內存訪問增加了一些時鐘周期。而這些過多的模式和特殊硬件的支持,是與RISC處理器的設計初衷背道而馳的,就拿克雷計算機來說,就只支持在字邊界上以字為單位進行尋址。

              在那些不提供多種數據類型尋址方式的計算機上,編譯器可以提供一些模擬。例如:編譯器可以生成一些指令,當讀取一個字時,通過移位和屏蔽,來找到所想要的位置,以此來模擬在字中的半字尋址,但這會需要額外的時鐘周期,并且代碼體積會更大。

              從這點上來說,位域的效率是非常低的,在以位域來取出一個字時,它們產生的代碼體積最大。當你存取同一個字中的其他位域時,又需要對包含這個位域字的內存,重新進行一遍尋址,這就是典型的以空間換時間。

              當在設計數據結構時,我們總是想用可以保存數據的最小數據類型,來達到節(jié)省空間的目的。我們有時小氣得經常使用char和short,來存取位特域,這就像是為了節(jié)省一角錢,而花了一元錢。儲存空間上的高效,會付出在程序速度和體積上隱藏的代價。

              試想你只為一個緊湊結構分配了一小點的空間,但卻產生了大量的代碼來存取結構中的域,而且這段代碼還是經常執(zhí)行的,那么,會因為非字尋址,而導致代碼運行緩慢,而且充斥了大量用于提取域的代碼,程序體積也因此增大。這些額外代碼所占的空間,會讓你前面為節(jié)省空間所做的努力付之東流。

              在高級數據結構中,特定的比特定位已不是必須的了,應在所有的域中都使用字(word),而不要操心它們所占用的空間。特別是在程序某些依賴于機器的部分,應該為字準備一個typedef,如下:

            /*在這臺計算機上,int是一個字長*/
            typedef word int;

              在高級結構中,對所有域都使用字有如下好處:

              a.. 對其他計算機架構的可移植性

              b.. 編譯器可能生成最快的代碼

              c.. 處理器可能最快地訪問到所需內存

              d.. 絕對沒有結構對齊的意外發(fā)生

              必須也承認,在某些時候,是不能做到全部使用字的。例如,有一個很大的結構,但不會被經常存取,如果使用了數千個字的話,體積將會增大25%,但使用字通常會節(jié)省空間、提高執(zhí)行速度,而且更具移植性。

              以下是我們的結論:

              書寫跨平臺移植的代碼,其實是件簡單的事情。最基本的規(guī)則是,盡可能地隱藏機器字長的細節(jié),用非常精確的數據元素位大小來映射物理數據結構。或者像前面所建議的,為高級編程使用高級數據結構,而低級編程使用低級數據結構,當闡明高級數據結構時,對標準C的標量類型,不要作任何假設

            posted on 2006-07-07 13:51 井泉 閱讀(286) 評論(0)  編輯 收藏 引用 所屬分類: c軟件工程

            久久精品国产亚洲精品| 久久久久亚洲av成人无码电影| 久久久久人妻一区精品| 日产精品久久久一区二区| 色综合久久综合中文综合网| 亚洲∧v久久久无码精品| 久久久久亚洲精品天堂| 国产成人精品久久综合| 亚洲国产成人久久精品影视| 日本精品久久久久久久久免费| 久久综合给久久狠狠97色| 国产99久久精品一区二区| 色偷偷91久久综合噜噜噜噜| 亚洲成色WWW久久网站| 国产成人99久久亚洲综合精品| 久久久久亚洲AV综合波多野结衣| 成人久久免费网站| 伊人久久大香线蕉无码麻豆| 久久国产免费| 欧美精品一本久久男人的天堂| 一本色道久久HEZYO无码| 91精品国产高清久久久久久国产嫩草 | 国产精品99久久久久久董美香 | 一级做a爰片久久毛片人呢| 久久这里只精品99re66| 久久精品成人免费观看97| 无码日韩人妻精品久久蜜桃| 午夜精品久久久久久| 成人午夜精品久久久久久久小说| 久久精品国产亚洲AV香蕉| 午夜精品久久久久久久| 欧美亚洲国产精品久久高清| 精品国产乱码久久久久软件| 囯产精品久久久久久久久蜜桃| 久久久精品国产亚洲成人满18免费网站| 无码人妻久久一区二区三区免费| 亚洲国产婷婷香蕉久久久久久| 久久久久99精品成人片| 国产精品日韩深夜福利久久| 久久国产福利免费| 久久久SS麻豆欧美国产日韩|