• <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
            數(shù)據(jù)加載中……

            新時(shí)代的門前 32位世界中的64位編程

            為16、32、64位架構(gòu)編寫可移植代碼

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

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

              由于有一些遺留問題,C語言在數(shù)據(jù)類型和數(shù)據(jù)構(gòu)造方面,顯得有點(diǎn)過剩了。可以使用的不僅僅是char、short、int和long類型,還有相應(yīng)的unsigned(無符號(hào))類型,當(dāng)然你可在結(jié)構(gòu)(structure)和聯(lián)合(union)中混合使用,可以在聯(lián)合中包含結(jié)構(gòu),再在結(jié)構(gòu)中包含聯(lián)合,如果還嫌數(shù)據(jù)類型不夠復(fù)雜,還可以轉(zhuǎn)換成比特位,當(dāng)然也可以把一種數(shù)據(jù)類型轉(zhuǎn)換成另一種你想要的數(shù)據(jù)類型。正是因?yàn)檫@些工具太強(qiáng)大,如果不安全地使用它們,就有可能會(huì)傷到自己了。

              高級代碼的高級結(jié)構(gòu)

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

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

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

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

              例1:高級代碼的高級結(jié)構(gòu)

              (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;
            }

              因?yàn)橹灰猧net_addr能夠正確地解析字符串,那么一切OK。當(dāng)inet_addr在64位計(jì)算機(jī)上返回一個(gè)錯(cuò)誤時(shí),這段代碼卻未能捕捉到。你必須要考慮比較語句中的數(shù)據(jù)位寬,來確定到底是哪出了錯(cuò)。

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

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

              真正的問題所在是,我們期望一個(gè)unsigned long值與一個(gè)32位量(如Internet地址)相等。Internet地址必須以32位形式進(jìn)行存儲(chǔ),但有些時(shí)候用一個(gè)標(biāo)量,來訪問這個(gè)地址的一部分,是非常方便的。在32位字長的電腦中,用一個(gè)long數(shù)值(常被當(dāng)作32位)來訪問這個(gè)地址,看上去沒什么問題。讓我們暫時(shí)不想一個(gè)低級的數(shù)據(jù)項(xiàng)(32位Internet地址)是否與一個(gè)機(jī)器字相等,那么高級數(shù)據(jù)類型結(jié)構(gòu)in_addr就應(yīng)該被一直使用。因?yàn)閕n_addr中沒有無效值,那么應(yīng)有一個(gè)單獨(dú)的狀態(tài)用作返回值。

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

              低級代碼的低級結(jié)構(gòu)

              低級編程意味著直接操縱物理設(shè)備或者特定協(xié)議的通訊格式,例如,設(shè)備驅(qū)動(dòng)程序經(jīng)常使用非常精確的位模式來操縱控制寄存器。此外,網(wǎng)絡(luò)協(xié)議通過特定的比特位模式傳輸數(shù)據(jù)項(xiàng)時(shí),也必須適當(dāng)?shù)剞D(zhuǎn)譯。

              為了操縱物理數(shù)據(jù)項(xiàng),此處的數(shù)據(jù)結(jié)構(gòu)必須準(zhǔn)確地反映被操縱的內(nèi)容。比特位是一個(gè)不錯(cuò)的選擇,因?yàn)樗鼈冋弥付吮忍氐奈粩?shù)及排列。事實(shí)上,正是這種精確度,使比特位相對于short、int、long,更好地映像了物理結(jié)構(gòu)(short、int、long會(huì)因?yàn)殡娔X的不同而改變,而比特位不會(huì))。

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

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

              例2:低級代碼的低級結(jié)構(gòu)

              (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++;
            結(jié)構(gòu)打包和字對齊

               正是因?yàn)榫幾g器會(huì)對結(jié)構(gòu)進(jìn)行打包,所以不同計(jì)算機(jī)上字長的變化,還導(dǎo)致了另一個(gè)問題。C編譯器在字(word)的邊界上對齊字長,當(dāng)具有一個(gè)字長的數(shù)據(jù)后面緊接著一個(gè)較小的數(shù)據(jù)時(shí),這種方法會(huì)產(chǎn)生內(nèi)存空缺(不過也有例外,比如說當(dāng)有足夠多的小數(shù)據(jù)剛好可以填充一個(gè)字時(shí))。

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




              例3:結(jié)構(gòu)打包和字對齊

              (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);
            }

              在第一個(gè)情況中(圖2),tag域不是像期望的那樣線性拉長,而被填充了一些垃圾。而在第二個(gè)情況中(圖3),無論是class還是tag域,都不再有意義,兩個(gè)char值因?yàn)楸淮虬梢粋€(gè)int,所以也都不再正確。再次強(qiáng)調(diào),首先不要假設(shè)標(biāo)準(zhǔn)數(shù)據(jù)類型大小一樣,其次還要了解它們是怎樣被映射成其他數(shù)據(jù)類型的,這才是書寫可移植代碼的最好方法。

              機(jī)器尋址特性

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

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

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

              從這點(diǎn)上來說,位域的效率是非常低的,在以位域來取出一個(gè)字時(shí),它們產(chǎn)生的代碼體積最大。當(dāng)你存取同一個(gè)字中的其他位域時(shí),又需要對包含這個(gè)位域字的內(nèi)存,重新進(jìn)行一遍尋址,這就是典型的以空間換時(shí)間。

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

              試想你只為一個(gè)緊湊結(jié)構(gòu)分配了一小點(diǎn)的空間,但卻產(chǎn)生了大量的代碼來存取結(jié)構(gòu)中的域,而且這段代碼還是經(jīng)常執(zhí)行的,那么,會(huì)因?yàn)榉亲謱ぶ罚鴮?dǎo)致代碼運(yùn)行緩慢,而且充斥了大量用于提取域的代碼,程序體積也因此增大。這些額外代碼所占的空間,會(huì)讓你前面為節(jié)省空間所做的努力付之東流。

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

            /*在這臺(tái)計(jì)算機(jī)上,int是一個(gè)字長*/
            typedef word int;

              在高級結(jié)構(gòu)中,對所有域都使用字有如下好處:

              a.. 對其他計(jì)算機(jī)架構(gòu)的可移植性

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

              c.. 處理器可能最快地訪問到所需內(nèi)存

              d.. 絕對沒有結(jié)構(gòu)對齊的意外發(fā)生

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

              以下是我們的結(jié)論:

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

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

            …久久精品99久久香蕉国产| 97精品国产97久久久久久免费| 久久久久亚洲精品中文字幕| 亚洲精品成人久久久| 久久人做人爽一区二区三区| 久久久久久亚洲精品成人| 久久精品嫩草影院| 亚洲欧美国产日韩综合久久| 久久亚洲国产成人精品性色| 久久久久久久久久免免费精品| 久久无码人妻一区二区三区午夜| 久久国产免费观看精品| 一本大道久久东京热无码AV| 99久久精品无码一区二区毛片 | 国产精品久久一区二区三区| 久久国产免费| 亚洲午夜久久久久妓女影院| 久久WWW免费人成—看片| 久久强奷乱码老熟女网站| 99久久伊人精品综合观看| 伊人久久综合成人网| 久久亚洲国产成人影院网站| 国产激情久久久久影院| 国产精品免费福利久久| 热re99久久6国产精品免费| 亚洲精品国产第一综合99久久| 嫩草影院久久99| 久久九九亚洲精品| 成人久久久观看免费毛片| 亚洲欧美日韩久久精品第一区| 亚洲欧洲久久av| 久久精品一区二区三区中文字幕| 国产福利电影一区二区三区久久老子无码午夜伦不 | 97久久精品人妻人人搡人人玩| 久久精品国产2020| 亚洲精品无码久久久久AV麻豆| 久久久精品久久久久久| 久久久久国产一区二区三区| 日本久久中文字幕| 久久久久久国产a免费观看黄色大片 | 久久免费美女视频|