青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品

franksunny的個(gè)人技術(shù)空間
獲得人生中的成功需要的專注與堅(jiān)持不懈多過(guò)天才與機(jī)會(huì)。 ——C.W. Wendte

 

可重入函數(shù)與不可重入函數(shù)

 

主要用于多任務(wù)環(huán)境中,一個(gè)可重入的函數(shù)簡(jiǎn)單來(lái)說(shuō)就是可以被中斷的函數(shù),也就是說(shuō),可以在這個(gè)函數(shù)執(zhí)行的任何時(shí)刻中斷它,轉(zhuǎn)入OS調(diào)度下去執(zhí)行另外一段代碼,而返回控制時(shí)不會(huì)出現(xiàn)什么錯(cuò)誤;而不可重入的函數(shù)由于使用了一些系統(tǒng)資源,比如全局變量區(qū),中斷向量表等,所以它如果被中斷的話,可能會(huì)出現(xiàn)問(wèn)題,這類(lèi)函數(shù)是不能運(yùn)行在多任務(wù)環(huán)境下的。

也可以這樣理解,重入即表示重復(fù)進(jìn)入,首先它意味著這個(gè)函數(shù)可以被中斷,其次意味著它除了使用自己棧上的變量以外不依賴于任何環(huán)境(包括static),這樣的函數(shù)就是purecode(純代碼)可重入,可以允許有該函數(shù)的多個(gè)副本在運(yùn)行,由于它們使用的是分離的棧,所以不會(huì)互相干擾。如果確實(shí)需要訪問(wèn)全局變量(包括static),一定要注意實(shí)施互斥手段。可重入函數(shù)在并行運(yùn)行環(huán)境中非常重要,但是一般要為訪問(wèn)全局變量付出一些性能代價(jià)。

編寫(xiě)可重入函數(shù)時(shí),若使用全局變量,則應(yīng)通過(guò)關(guān)中斷、信號(hào)量(即PV操作)等手段對(duì)其加以保護(hù)。

 說(shuō)明:若對(duì)所使用的全局變量不加以保護(hù),則此函數(shù)就不具有可重入性,即當(dāng)多個(gè)進(jìn)程調(diào)用此函數(shù)時(shí),很有可能使有關(guān)全局變量變?yōu)椴豢芍獱顟B(tài)。

 

示例:假設(shè)Examint型全局變量,函數(shù)Squre_Exam返回Exam平方值。那么如下函數(shù)不具有可重入性。

unsigned int example( int para )

{

    unsigned int temp;
        Exam = para; //
**
        temp = Square_Exam( );
        return temp;
    }
   
此函數(shù)若被多個(gè)進(jìn)程調(diào)用的話,其結(jié)果可能是未知的,因?yàn)楫?dāng)(**)語(yǔ)句剛執(zhí)行完后,另外一個(gè)使用本函數(shù)的進(jìn)程可能正好被激活,那么當(dāng)新激活的進(jìn)程執(zhí)行到此函數(shù)時(shí),將使Exam賦與另一個(gè)不同的para值,所以當(dāng)控制重新回到“temp = Square_Exam( )”后,計(jì)算出的temp很可能不是預(yù)想中的結(jié)果。此函數(shù)應(yīng)如下改進(jìn)。

    unsigned int example( int para ) {
        unsigned int temp;
        [
申請(qǐng)信號(hào)量操作] //(1)
        Exam = para;
        temp = Square_Exam( );
        [
釋放信號(hào)量操作]
        return temp;
    }
    (1)
若申請(qǐng)不到信號(hào)量,說(shuō)明另外的進(jìn)程正處于給Exam賦值并計(jì)算其平方過(guò)程中(即正在使用此信號(hào)),本進(jìn)程必須等待其釋放信號(hào)后,才可繼續(xù)執(zhí)行。若申請(qǐng)到信號(hào),則可繼續(xù)執(zhí)行,但其它進(jìn)程必須等待本進(jìn)程釋放信號(hào)量后,才能再使用本信號(hào)。

   
保證函數(shù)的可重入性的方法:
   
在寫(xiě)函數(shù)時(shí)候盡量使用局部變量(例如寄存器、堆棧中的變量),對(duì)于要使用的全局變量要加以保護(hù)(如采取關(guān)中斷、信號(hào)量等方法),這樣構(gòu)成的函數(shù)就一定是一個(gè)可重入的函數(shù)。
    VxWorks
中采取的可重入的技術(shù)有:
    *
動(dòng)態(tài)堆棧變量(各子函數(shù)有自己獨(dú)立的堆棧空間)
    *
受保護(hù)的全局變量和靜態(tài)變量
    *
任務(wù)變量


--------------------------------------------------
   
在實(shí)時(shí)系統(tǒng)的設(shè)計(jì)中,經(jīng)常會(huì)出現(xiàn)多個(gè)任務(wù)調(diào)用同一個(gè)函數(shù)的情況。如果這個(gè)函數(shù)不幸被設(shè)計(jì)成為不可重入的函數(shù)的話,那么不同任務(wù)調(diào)用這個(gè)函數(shù)時(shí)可能修改其他任務(wù)調(diào)用這個(gè)函數(shù)的數(shù)據(jù),從而導(dǎo)致不可預(yù)料的后果。那么什么是可重入函數(shù)呢?所謂可重入函數(shù)是指一個(gè)可以被多個(gè)任務(wù)調(diào)用的過(guò)程,任務(wù)在調(diào)用時(shí)不必?fù)?dān)心數(shù)據(jù)是否會(huì)出錯(cuò)。不可重入函數(shù)在實(shí)時(shí)系統(tǒng)設(shè)計(jì)中被視為不安全函數(shù)。滿足下列條件的函數(shù)多數(shù)是不可重入的:
    1)
函數(shù)體內(nèi)使用了靜態(tài)的數(shù)據(jù)結(jié)構(gòu);
    2)
函數(shù)體內(nèi)調(diào)用了malloc()或者free()函數(shù);
    3)
函數(shù)體內(nèi)調(diào)用了標(biāo)準(zhǔn)I/O函數(shù)。

   
下面舉例加以說(shuō)明。
    A.
可重入函數(shù)
    void strcpy(char *lpszDest, char *lpszSrc)

 {
        while(*lpszDest++=*lpszSrc++);
        *dest=0;
    }

    B.
不可重入函數(shù)1
    charcTemp;//
全局變量
    void SwapChar1(char *lpcX, char *lpcY)

 {
        cTemp=*lpcX;
        *lpcX=*lpcY;
        lpcY=cTemp;//
訪問(wèn)了全局變量
    }

    C.
不可重入函數(shù)2
    void SwapChar2(char *lpcX,char *lpcY)

 {
        static char cTemp;//
靜態(tài)局部變量
        cTemp=*lpcX;
        *lpcX=*lpcY;
        lpcY=cTemp;//
使用了靜態(tài)局部變量
    }

   
問(wèn)題1,如何編寫(xiě)可重入的函數(shù)?
   
答:在函數(shù)體內(nèi)不訪問(wèn)那些全局變量,不使用靜態(tài)局部變量,堅(jiān)持只使用局部變量,寫(xiě)出的函數(shù)就將是可重入的。如果必須訪問(wèn)全局變量,記住利用互斥信號(hào)量來(lái)保護(hù)全局變量。

   
問(wèn)題2,如何將一個(gè)不可重入的函數(shù)改寫(xiě)成可重入的函數(shù)?
   
答:把一個(gè)不可重入函數(shù)變成可重入的唯一方法是用可重入規(guī)則來(lái)重寫(xiě)它。其實(shí)很簡(jiǎn)單,只要遵守了幾條很容易理解的規(guī)則,那么寫(xiě)出來(lái)的函數(shù)就是可重入的。
    1)
不要使用全局變量。因?yàn)閯e的代碼很可能覆蓋這些變量值。
    2)
在和硬件發(fā)生交互的時(shí)候,切記執(zhí)行類(lèi)似disinterrupt()之類(lèi)的操作,就是關(guān)閉硬件中斷。完成交互記得打開(kāi)中斷,在有些系列上,這叫做進(jìn)入/退出核心
    3)
不能調(diào)用其它任何不可重入的函數(shù)。
    4)
謹(jǐn)慎使用堆棧。最好先在使用前先OS_ENTER_KERNAL

   
堆棧操作涉及內(nèi)存分配,稍不留神就會(huì)造成益出導(dǎo)致覆蓋其他任務(wù)的數(shù)據(jù),所以,請(qǐng)謹(jǐn)慎使用堆棧!最好別用!很多黑客程序就利用了這一點(diǎn)以便系統(tǒng)執(zhí)行非法代碼從而輕松獲得系統(tǒng)控制權(quán)。還有一些規(guī)則,總之,時(shí)刻記住一句話:保證中斷是安全的!

   
實(shí)例問(wèn)題:曾經(jīng)設(shè)計(jì)過(guò)如下一個(gè)函數(shù),在代碼檢視的時(shí)候被提醒有bug,因?yàn)檫@個(gè)函數(shù)是不可重入的,為什么?
    unsigned int sum_int( unsigned int base )

{
        unsigned int index;
        static unsigned int sum = 0; //
注意,是static類(lèi)型
        for (index = 1; index <= base; index++)
            sum += index;
        return sum;
    }

   
分析:所謂的函數(shù)是可重入的(也可以說(shuō)是可預(yù)測(cè)的),即只要輸入數(shù)據(jù)相同就應(yīng)產(chǎn)生相同的輸出。這個(gè)函數(shù)之所以是不可預(yù)測(cè)的,就是因?yàn)楹瘮?shù)中使用了static變量,因?yàn)?span lang=EN-US>static
變量的特征,這樣的函數(shù)被稱為:帶內(nèi)部存儲(chǔ)器功能的的函數(shù)。因此如果需要一個(gè)可重入的函數(shù),一定要避免函數(shù)中使用static變量,這種函數(shù)中的static變量,使用原則是,能不用盡量不用。
   
將上面的函數(shù)修改為可重入的函數(shù),只要將聲明sum變量中的static關(guān)鍵字去掉,變量sum即變?yōu)橐粋€(gè)auto類(lèi)型的變量,函數(shù)即變?yōu)橐粋€(gè)可重入的函數(shù)。
   
當(dāng)然,有些時(shí)候,在函數(shù)中是必須要使用static變量的,比如當(dāng)某函數(shù)的返回值為指針類(lèi)型時(shí),則必須是static的局部變量的地址作為返回值,若為auto類(lèi)型,則返回為錯(cuò)指針。

posted @ 2007-08-03 12:56 frank.sunny 閱讀(5797) | 評(píng)論 (3)編輯 收藏

MSP430的時(shí)鐘問(wèn)題

 

MSP430的時(shí)鐘周期(振蕩周期)、機(jī)器周期、指令周期之間的關(guān)系

通用知識(shí)

時(shí)鐘周期也稱為振蕩周期:定義為時(shí)鐘脈沖的倒數(shù)(時(shí)鐘周期就是直接供內(nèi)部CPU使用的晶振的倒數(shù),例如12M的晶振,它的時(shí)鐘周期就是1/12us),是計(jì)算機(jī)中的最基本的、最小的時(shí)間單位。在一個(gè)時(shí)鐘周期內(nèi),CPU僅完成一個(gè)最基本的動(dòng)作。時(shí)鐘脈沖是計(jì)算機(jī)的基本工作脈沖,控制著計(jì)算機(jī)的工作節(jié)奏。時(shí)鐘頻率越高,工作速度就越快。

機(jī)器周期:在計(jì)算機(jī)中,常把一條指令的執(zhí)行過(guò)程劃分為若干個(gè)階段,每一個(gè)階段完成一項(xiàng)工作。每一項(xiàng)工作稱為一個(gè)基本操作,完成一個(gè)基本操作所需要的時(shí)間稱為機(jī)器周期。8051系列單片機(jī)的一個(gè)機(jī)器周期由6個(gè)S周期(狀態(tài)周期)組成。一個(gè)S周期=2個(gè)時(shí)鐘周期,所以8051單片機(jī)的一個(gè)機(jī)器周期=6個(gè)狀態(tài)周期=12個(gè)時(shí)鐘周期。

指令周期:執(zhí)行一條指令所需要的時(shí)間,一般由若干個(gè)機(jī)器周期組成。指令不同,所需的機(jī)器周期也不同。

專用知識(shí):

430中,一個(gè)時(shí)鐘周期 = MCLK晶振的倒數(shù)。如果MCLK8M,則一個(gè)時(shí)鐘周期為1/8us

一個(gè)機(jī)器周期 = 一個(gè)時(shí)鐘周期,即430每個(gè)動(dòng)作都能完成一個(gè)基本操作;

一個(gè)指令周期 = 16個(gè)機(jī)器周期,具體根據(jù)具體指令而定。

另:指令長(zhǎng)度,只是一個(gè)存儲(chǔ)單位與時(shí)間沒(méi)有必然關(guān)系。

 

 

MSP430根據(jù)型號(hào)的不同最多可以選擇使用3個(gè)振蕩器。我們可以根據(jù)需要選擇合適的振蕩頻率,并可以在不需要時(shí)隨時(shí)關(guān)閉振蕩器,以節(jié)省功耗。這3個(gè)振蕩器分別為:

1DCO  數(shù)控RC振蕩器。它在芯片內(nèi)部,不用時(shí)可以關(guān)閉。DCO的振蕩頻率會(huì)受周?chē)h(huán)境溫度和MSP430工作電壓的影響,且同一型號(hào)的芯片所產(chǎn)生的頻率也不相同。但DCO的調(diào)節(jié)功能可以改善它的性能,他的調(diào)節(jié)分為以下3步:a:選擇BCSCTL1.RSELx確定時(shí)鐘的標(biāo)稱頻率;b:選擇DCOCTL.DCOx在標(biāo)稱頻率基礎(chǔ)上分段粗調(diào);c:選擇DCOCTL.MODx的值進(jìn)行細(xì)調(diào)。

2LFXT1  接低頻振蕩器。典型為接32768HZ的時(shí)鐘振蕩器,此時(shí)振蕩器不需要接負(fù)載電容。也可以接450KHZ~8MHZ的標(biāo)準(zhǔn)晶體振蕩器,此時(shí)需要接負(fù)載電容。

3XT2  450KHZ~8MHZ的標(biāo)準(zhǔn)晶體振蕩器。此時(shí)需要接負(fù)載電容,不用時(shí)可以關(guān)閉。

低頻振蕩器主要用來(lái)降低能量消耗,如使用電池供電的系統(tǒng),高頻振蕩器用來(lái)對(duì)事件做出快速反應(yīng)或者供CPU進(jìn)行大量運(yùn)算。當(dāng)然高端430還有鎖頻環(huán)(FLL)FLL+等模塊,但是初步不用考慮那么多。

MSP4303種時(shí)鐘信號(hào):MCLK系統(tǒng)主時(shí)鐘;SMCLK系統(tǒng)子時(shí)鐘;ACLK輔助時(shí)鐘。

1MCLK系統(tǒng)主時(shí)鐘。除了CPU運(yùn)算使用此時(shí)鐘以外,外圍模塊也可以使用。MCLK可以選擇任何一個(gè)振蕩器所產(chǎn)生的時(shí)鐘信號(hào)并進(jìn)行1248分頻作為其信號(hào)源。

2SMCLK系統(tǒng)子時(shí)鐘。供外圍模塊使用。并在使用前可以通過(guò)各模塊的寄存器實(shí)現(xiàn)分頻。SMCLK可以選擇任何一個(gè)振蕩器所產(chǎn)生的時(shí)鐘信號(hào)并進(jìn)行1248分頻作為其信號(hào)源。

3ACLK輔助時(shí)鐘。供外圍模塊使用。并在使用前可以通過(guò)各模塊的寄存器實(shí)現(xiàn)分頻。但ACLK只能由LFXT1進(jìn)行1248分頻作為信號(hào)源。

PUC復(fù)位后,MCLKSMCLK的信號(hào)源為DCO,DCO的振蕩頻率默認(rèn)為800KHZACLK的信號(hào)源為LFXT1

MSP430內(nèi)部含有晶體振蕩器失效監(jiān)測(cè)電路,監(jiān)測(cè)LFXT1(工作在高頻模式)和XT2輸出的時(shí)鐘信號(hào)。當(dāng)時(shí)鐘信號(hào)丟失50us時(shí),監(jiān)測(cè)電路捕捉到振蕩器失效。如果MCLK信號(hào)來(lái)自LFXT1或者XT2,那么MSP430自動(dòng)把MCLK的信號(hào)切換為DCO,這樣可以保證程序繼續(xù)運(yùn)行。但MSP430不對(duì)工作在低頻模式的LFXT1進(jìn)行監(jiān)測(cè)。

 

為了實(shí)現(xiàn)具體的時(shí)鐘可以設(shè)置跟時(shí)鐘相關(guān)的寄存器,在低端430中是DCOCTLBCSCTL1BCSCTL2三個(gè)寄存器。而對(duì)于高端的430,則要考慮SCFI0SCFQCTLFLL_CTL0FLL_CTL1BTCTL等幾個(gè)寄存器。具體設(shè)置,參看DataSheet

 

 

 

上傳上來(lái)的時(shí)候,發(fā)現(xiàn)圖片沒(méi)有顯示出來(lái),不好意思
posted @ 2007-06-07 22:14 frank.sunny 閱讀(4377) | 評(píng)論 (7)編輯 收藏
     摘要:   關(guān)于變量的存儲(chǔ)問(wèn)題   以前從事上位機(jī)程序代碼的編寫(xiě),壓根不用很具體的考慮變量的具體存放位置,只知道以下概念就行了: 1. 堆區(qū)( heap ):由程序員申請(qǐng)分配和釋放,屬動(dòng)態(tài)內(nèi)存分配方式,變量存放于動(dòng)態(tài)存儲(chǔ)區(qū),若程序員不釋放,程序結(jié)束時(shí)可能會(huì)由 OS 回收。不過(guò)這個(gè)內(nèi)存分配很容易引起問(wèn)題,如果申請(qǐng)的內(nèi)存不釋放就會(huì)造成內(nèi)存泄漏;如果釋放的不是所要釋放的內(nèi)存,則輕者引...  閱讀全文
posted @ 2007-06-01 15:00 frank.sunny 閱讀(3134) | 評(píng)論 (2)編輯 收藏

 

MSP430入門(mén)

 

本人學(xué)習(xí)和使用msp430一個(gè)月,偶在微控論壇看到一筆記,記載的蠻好的,現(xiàn)轉(zhuǎn)載到自己博客以做備忘。

硬件初步

這只是我在學(xué)習(xí)TI公司生產(chǎn)的16位超的功耗單片機(jī)MSP430的隨筆,希望能對(duì)其他朋友有所借鑒,不對(duì)之處還請(qǐng)多指教。

下面,開(kāi)始430之旅。

講解430的書(shū)現(xiàn)在也有很多了,不過(guò)大多數(shù)都是詳細(xì)說(shuō)明底層硬件結(jié)構(gòu)的,看了不免有些空洞和枯燥,我認(rèn)為了解一個(gè)MCU的操作首先要對(duì)其基礎(chǔ)特性有所了解,然后再仔細(xì)研究各模塊的功能。

1.首先你要知道msp430的存儲(chǔ)器結(jié)構(gòu)。典型微處理器的結(jié)構(gòu)有兩種:馮。諾依曼結(jié)構(gòu)——程序存儲(chǔ)器和數(shù)據(jù)存儲(chǔ)器統(tǒng)一編碼;哈佛結(jié)構(gòu)——程序存儲(chǔ)器和數(shù)據(jù)存儲(chǔ)器;msp430系列單片機(jī)屬于前者,而常用的mcs51系列屬于后者。

00xf特殊功能寄存器;0x100x1ff外圍模塊寄存器;0x200-?根據(jù)不同型號(hào)地址從低向高擴(kuò)展;0x10000x107f seg_b0x1080_0x10ff seg_a flash信息存儲(chǔ)剩下的從0xffff開(kāi)始向下擴(kuò)展,根據(jù)不同容量,例如14960KB0xffff0x1100

2.復(fù)位信號(hào)是MCU工作的起點(diǎn),430的復(fù)位信號(hào)有兩種:上電復(fù)位信號(hào)POR和上電清除信號(hào)PUCPOR信號(hào)只在上電和RST/NMI復(fù)位管腳被設(shè)置為復(fù)位功能,且低電平時(shí)系統(tǒng)復(fù)位。而PUC信號(hào)是POR信號(hào)產(chǎn)生,以及其他如看門(mén)狗定時(shí)溢出、安全鍵值出現(xiàn)錯(cuò)誤是產(chǎn)生。但是,無(wú)論那種信號(hào)觸發(fā)的復(fù)位,都會(huì)使msp430在地址0xffff處讀取復(fù)位中斷向量,然后程序從中斷向量所指的地址開(kāi)始執(zhí)行。復(fù)位后的狀態(tài)不寫(xiě)了,詳見(jiàn)參考書(shū),嘿嘿。

3.系統(tǒng)時(shí)鐘是一個(gè)程序運(yùn)行的指揮官,時(shí)序和中斷也是整個(gè)程序的核心和中軸線。430最多有三個(gè)振蕩器,DCO內(nèi)部振蕩器;LFXT1外接低頻振蕩器,常見(jiàn)的32768HZ,不用外接負(fù)載電容;也可接高頻450KHZ8M,需接負(fù)載電容;XT2接高頻450KHZ8M,加外接電容。(經(jīng)驗(yàn)中發(fā)現(xiàn),接XT2時(shí),需要注意自己開(kāi)啟XT2,并延時(shí)50us等待XT2起振,然后手工清除IFG1中的OFIFG位,其操作順序?yàn)椋捍蜷_(kāi)XT2->等待XT2穩(wěn)定->切換系統(tǒng)時(shí)鐘為XT2

430有三種時(shí)鐘信號(hào):MCLK系統(tǒng)主時(shí)鐘,可分頻1 2 4 8,供cpu使用,其他外圍模塊在有選擇情況下也可使用;SMCLK系統(tǒng)子時(shí)鐘,供外圍模塊使用,可選則不同振蕩器產(chǎn)生的時(shí)鐘信號(hào);ACLK輔助時(shí)鐘,只能由LFXT1產(chǎn)生,供外圍模塊。

4.中斷是430處理器的一大特色,因?yàn)閹缀趺總€(gè)外圍模塊都能產(chǎn)生,430可以在沒(méi)有任務(wù)時(shí)進(jìn)入低功耗狀態(tài),有事件時(shí)中斷喚醒cpu,處理完畢再次進(jìn)入低功耗狀態(tài)。

整個(gè)中斷的響應(yīng)過(guò)程是這樣的,當(dāng)有中斷請(qǐng)求時(shí),如果cpu處于活動(dòng)狀態(tài),先完成當(dāng)前命令;如果處于低功耗,先退出,將下一條指令的pc值壓入堆棧;如果有多個(gè)中斷請(qǐng)求,先響應(yīng)優(yōu)先級(jí)高的;執(zhí)行完后,等待中斷請(qǐng)求標(biāo)志位復(fù)位,要注意,單中斷源的中斷請(qǐng)求標(biāo)志位自動(dòng)復(fù)位,而多中斷的標(biāo)志位需要軟件復(fù)位;然后系統(tǒng)總中斷允許位SR.GIE復(fù)位,相應(yīng)的中斷向量值裝入pc,程序從這個(gè)地址繼續(xù)執(zhí)行。

這里要注意,中斷允許位SR.GIE和中斷嵌套問(wèn)題。如果當(dāng)你執(zhí)行中斷程序過(guò)程中,希望可以響應(yīng)更高級(jí)別的中斷請(qǐng)求時(shí),必須在進(jìn)入第一個(gè)中斷時(shí)把SR.GIE置位。

其實(shí),其他的外圍模塊時(shí)鐘沿著時(shí)鐘和中斷這個(gè)核心來(lái)執(zhí)行的。具體的結(jié)構(gòu)我也不羅索了,可以參考430系列手冊(cè)。

 

C語(yǔ)言編程起步

因?yàn)槌S玫?span lang=EN-US>430編程開(kāi)發(fā)是c語(yǔ)言,所以下面講解C語(yǔ)言對(duì)430編程的整體結(jié)構(gòu)。基本上屬于框架結(jié)構(gòu),即整體的模塊化編程,其實(shí)這也是硬件編程的基本法則拉(可不是我規(guī)定的法則哦)。

首先是程序的頭文件,包括#include <MSP430x14x.h>,這是14系列,因?yàn)槌S?span lang=EN-US>149;其他型號(hào)可自己修改。還可以包括#include "data.h" 等數(shù)據(jù)庫(kù)頭文件,或函數(shù)變量聲明頭文件,都是你自己定義的哦。

接著就是函數(shù)和變量的聲明 void Init_Sys(void),即系統(tǒng)初始化。系統(tǒng)初始化是個(gè)整體的概念,廣義上講包括所有外圍模塊的初始化,你可以把外圍模塊初始化的子函數(shù)寫(xiě)到Init_Sys()中,也可以分別寫(xiě)各個(gè)模塊的初始化。但結(jié)構(gòu)的簡(jiǎn)潔,最好寫(xiě)完系統(tǒng)的時(shí)鐘初始化后,其他所用到的模塊(包括一些中斷初始化)也在這里初始化。

void Init_Sys()

{

   unsigned int i;

   BCSCTL1&=~XT2OFF;                //打開(kāi)XT2振蕩器

   do

   {

   IFG1 &= ~OFIFG;              // 清除振蕩器失效標(biāo)志

   for (i = 0xFF; i > 0; i--);  // 延時(shí),等待XT2起振

}

while ((IFG1 & OFIFG) != 0);    // 判斷XT2是否起振

BCSCTL2 =SELM_2+SELS;           //選擇MCLKSMCLKXT2

 

//以下對(duì)各種模塊、中斷、外圍設(shè)備等進(jìn)行初始化

........................................

_EINT(); //打開(kāi)全局中斷控制

}

這里涉及到時(shí)鐘問(wèn)題,通常我們選擇XT28M晶振,也即系統(tǒng)主時(shí)鐘MCLK8M,cpu執(zhí)行命令以此時(shí)鐘為準(zhǔn);但其他外圍模塊可以在相應(yīng)的控制寄存器中選擇其他的時(shí)鐘,ACLK;當(dāng)你對(duì)速度要求很低,定時(shí)時(shí)間間隔大時(shí),就可以選擇ACLK,例如在定時(shí)器Timea初始化中設(shè)置。

主程序:

void main( void )

{

   WDTCTL = WDTPW + WDTHOLD;//關(guān)閉看門(mén)狗

   InitSys();               //初始化

//自己任務(wù)中的其他功能函數(shù)

 。。。。。。。。。。。。。。。。。。。。。

   while(1);

}

主程序之后我要講講中斷函數(shù),中斷是你做單片機(jī)任務(wù)中不可缺少的部分,也可以說(shuō)是靈魂了(夸張嗎)。

/***********************************************************************

                         各中斷函數(shù),可按優(yōu)先級(jí)依次書(shū)寫(xiě)

***********************************************************************/

舉個(gè)定時(shí)中斷的例子:

//初始化

void Init_Timer_A(void)

{

    TACTL = TASSEL0 + TACLR;               // ACLK, clear TAR
        CCTL0 = CCIE;                         // CCR0
中斷使能
        CCR0=32768;                           //
定時(shí)1s
        TACTL|=MC0;                           //
增計(jì)數(shù)模式
    }
//    
中斷服務(wù)

#pragma vector=TIMERA0_VECTOR

__interrupt void TimerA0()

{

// 你自己要求中斷執(zhí)行的任務(wù)

}

當(dāng)然,還有其他的定時(shí),和多種中斷,各系列芯片的中斷向量個(gè)數(shù)也不同。

 

學(xué)完并懂得以上知識(shí)后,接下去推薦看微控論壇上的msp430常用模塊應(yīng)用原理。

posted @ 2007-04-18 16:04 frank.sunny 閱讀(3205) | 評(píng)論 (5)編輯 收藏

也談關(guān)于時(shí)間

最近轉(zhuǎn)去搞低層些的單片機(jī)程序編程,在一塊msp430上要增加一個(gè)國(guó)際標(biāo)準(zhǔn)時(shí)間,由于以前在VC中都是拿來(lái)用的,沒(méi)遇到問(wèn)題,也就不會(huì)去深究。在單片機(jī)上想用標(biāo)準(zhǔn)C里面的timetime_t*)函數(shù)求得系統(tǒng)時(shí)間,最后結(jié)果出不來(lái)。后來(lái)才知道原來(lái)以前是取得的是操作系統(tǒng)的時(shí)間,汗死,單片機(jī)沒(méi)系統(tǒng)的啊,希望能夠盡早讓我搞嵌入式啊,呵呵。

后來(lái)自己弄明白了,設(shè)個(gè)時(shí)間值,然后用單片機(jī)晶振累加計(jì)數(shù),還是可以用time.h輕松實(shí)現(xiàn)標(biāo)準(zhǔn)時(shí)間計(jì)時(shí)的,而且方便不用考慮自己去寫(xiě)時(shí)間轉(zhuǎn)換函數(shù),以下是具體的time,h的講解,我就不再展開(kāi)了。

time.h從頭學(xué)

本文從介紹基礎(chǔ)概念入手,探討了在C/C++中對(duì)日期和時(shí)間操作所用到的數(shù)據(jù)結(jié)構(gòu)和函數(shù),并對(duì)計(jì)時(shí)、時(shí)間的獲取、時(shí)間的計(jì)算和顯示格式等方面進(jìn)行了闡述。本文還通過(guò)大量的實(shí)例向你展示了time.h頭文件中聲明的各種函數(shù)和數(shù)據(jù)結(jié)構(gòu)的詳細(xì)使用方法。  

關(guān)鍵字:UTC(世界標(biāo)準(zhǔn)時(shí)間),Calendar Time(日歷時(shí)間),epoch(時(shí)間點(diǎn)),clock tick(時(shí)鐘計(jì)時(shí)單元)  

1.  概念  

C/C++中,對(duì)字符串的操作有很多值得注意的問(wèn)題,同樣,C/C++對(duì)時(shí)間的操作也有許多值得大家注意的地方。最近,在技術(shù)群中有很多網(wǎng)友也多次問(wèn)到過(guò)C++語(yǔ)言中對(duì)時(shí)間的操作、獲取和顯示等等的問(wèn)題。下面,在這篇文章中,筆者將主要介紹在C/C++中時(shí)間和日期的使用方法.  

通過(guò)學(xué)習(xí)許多C/C++庫(kù),你可以有很多操作、使用時(shí)間的方法。但在這之前你需要了解一些時(shí)間日期的概念,主要有以下幾個(gè):  

Coordinated Universal TimeUTC):協(xié)調(diào)世界時(shí),又稱為世界標(biāo)準(zhǔn)時(shí)間,也就是大家所熟知的格林威治標(biāo)準(zhǔn)時(shí)間(Greenwich Mean TimeGMT)。比如,中國(guó)內(nèi)地的時(shí)間與UTC的時(shí)差為+8,也就是UTC+8。美國(guó)是UTC-5 

Calendar Time:日歷時(shí)間,是用從一個(gè)標(biāo)準(zhǔn)時(shí)間點(diǎn)到此時(shí)的時(shí)間經(jīng)過(guò)的秒數(shù)來(lái)表示的時(shí)間。這個(gè)標(biāo)準(zhǔn)時(shí)間點(diǎn)對(duì)不同的編譯器來(lái)說(shuō)會(huì)有所不同,但對(duì)一個(gè)編譯系統(tǒng)來(lái)說(shuō),這個(gè)標(biāo)準(zhǔn)時(shí)間點(diǎn)是不變的,該編譯系統(tǒng)中的時(shí)間對(duì)應(yīng)的日歷時(shí)間都通過(guò)該標(biāo)準(zhǔn)時(shí)間點(diǎn)來(lái)衡量,所以可以說(shuō)日歷時(shí)間是相對(duì)時(shí)間,但是無(wú)論你在哪一個(gè)時(shí)區(qū),在同一時(shí)刻對(duì)同一個(gè)標(biāo)準(zhǔn)時(shí)間點(diǎn)來(lái)說(shuō),日歷時(shí)間都是一樣的。

epoch:時(shí)間點(diǎn)。時(shí)間點(diǎn)在標(biāo)準(zhǔn)C/C++中是一個(gè)整數(shù),它用此時(shí)的時(shí)間和標(biāo)準(zhǔn)時(shí)間點(diǎn)相差的秒數(shù)(即日歷時(shí)間)來(lái)表示。

clock tick:時(shí)鐘計(jì)時(shí)單元(而不把它叫做時(shí)鐘滴答次數(shù)),一個(gè)時(shí)鐘計(jì)時(shí)單元的時(shí)間長(zhǎng)短是由CPU控制的。一個(gè)clock tick不是CPU的一個(gè)時(shí)鐘周期,而是C/C++的一個(gè)基本計(jì)時(shí)單位。

我們可以使用ANSI標(biāo)準(zhǔn)庫(kù)中的time.h頭文件。這個(gè)頭文件中定義的時(shí)間和日期所使用的方法,無(wú)論是在結(jié)構(gòu)定義,還是命名,都具有明顯的C語(yǔ)言風(fēng)格。下面,我將說(shuō)明在C/C++中怎樣使用日期的時(shí)間功能。  

2. 計(jì)時(shí)  

C/C++中的計(jì)時(shí)函數(shù)是clock(),而與其相關(guān)的數(shù)據(jù)類(lèi)型是clock_t。在MSDN中,查得對(duì)clock函數(shù)定義如下:  

clock_t clock( void );  

這個(gè)函數(shù)返回從開(kāi)啟這個(gè)程序進(jìn)程程序中調(diào)用clock()函數(shù)時(shí)之間的CPU時(shí)鐘計(jì)時(shí)單元(clock tick)數(shù),在MSDN中稱之為掛鐘時(shí)間(wal-clock)。其中clock_t是用來(lái)保存時(shí)間的數(shù)據(jù)類(lèi)型,在time.h文件中,我們可以找到對(duì)它的定義:  

#ifndef _CLOCK_T_DEFINED  
typedef long clock_t;  
#define _CLOCK_T_DEFINED  
#endif  

很明顯,clock_t是一個(gè)長(zhǎng)整形數(shù)。在time.h文件中,還定義了一個(gè)常量CLOCKS_PER_SEC,它用來(lái)表示一秒鐘會(huì)有多少個(gè)時(shí)鐘計(jì)時(shí)單元,其定義如下:

#define CLOCKS_PER_SEC ((clock_t)1000)

可以看到每過(guò)千分之一秒(1毫秒),調(diào)用clock()函數(shù)返回的值就加1。下面舉個(gè)例子,你可以使用公式clock()/CLOCKS_PER_SEC來(lái)計(jì)算一個(gè)進(jìn)程自身的運(yùn)行時(shí)間:

void elapsed_time()  
{  
printf("Elapsed time:%u secs.\n",clock()/CLOCKS_PER_SEC);  
}  

當(dāng)然,你也可以用clock函數(shù)來(lái)計(jì)算你的機(jī)器運(yùn)行一個(gè)循環(huán)或者處理其它事件到底花了多少時(shí)間:  

#include “stdio.h”  
#include “stdlib.h”  
#include “time.h”  

int main( void )  
{  
long i = 10000000L;  
clock_t start, finish;  
double duration;  
/*
測(cè)量一個(gè)事件持續(xù)的時(shí)間*/  
printf( "Time to do %ld empty loops is ", i );  
start = clock();  
while( i-- ) ;  
finish = clock();  
duration = (double)(finish - start) / CLOCKS_PER_SEC;  
printf( "%f seconds\n", duration );
system("pause");  
}  

在筆者的機(jī)器上,運(yùn)行結(jié)果如下:

Time to do 10000000 empty loops is 0.03000 seconds

上面我們看到時(shí)鐘計(jì)時(shí)單元的長(zhǎng)度為1毫秒,那么計(jì)時(shí)的精度也為1毫秒,那么我們可不可以通過(guò)改變CLOCKS_PER_SEC的定義,通過(guò)把它定義的大一些,從而使計(jì)時(shí)精度更高呢?通過(guò)嘗試,你會(huì)發(fā)現(xiàn)這樣是不行的。在標(biāo)準(zhǔn)C/C++中,最小的計(jì)時(shí)單位是一毫秒。

3.與日期和時(shí)間相關(guān)的數(shù)據(jù)結(jié)構(gòu)  

在標(biāo)準(zhǔn)C/C++中,我們可通過(guò)tm結(jié)構(gòu)來(lái)獲得日期和時(shí)間,tm結(jié)構(gòu)在time.h中的定義如下:  

#ifndef _TM_DEFINED  
struct tm {  
int tm_sec; /*
取值區(qū)間為[0,59] */  
int tm_min; /*
- 取值區(qū)間為
[0,59] */  
int tm_hour; /*
時(shí) - 取值區(qū)間為
[0,23] */  
int tm_mday; /*
一個(gè)月中的日期 - 取值區(qū)間為
[1,31] */  
int tm_mon; /*
月份(從一月開(kāi)始,0代表一月) - 取值區(qū)間為
[0,11] */  
int tm_year; /*
年份,其值等于實(shí)際年份減去
1900 */  
int tm_wday; /*
星期取值區(qū)間為[0,6],其中0代表星期天,1代表星期一,以此類(lèi)推
*/  
int tm_yday; /*
從每年的11開(kāi)始的天數(shù)取值區(qū)間為[0,365],其中0代表111代表12,以此類(lèi)推
*/  
int tm_isdst; /*
夏令時(shí)標(biāo)識(shí)符,實(shí)行夏令時(shí)的時(shí)候,tm_isdst為正。不實(shí)行夏令時(shí)的進(jìn)候,tm_isdst0;不了解情況時(shí),tm_isdst()為負(fù)。
*/  
};  
#define _TM_DEFINED  
#endif  

ANSI C標(biāo)準(zhǔn)稱使用tm結(jié)構(gòu)的這種時(shí)間表示為分解時(shí)間(broken-down time)

而日歷時(shí)間(Calendar Time)是通過(guò)time_t數(shù)據(jù)類(lèi)型來(lái)表示的,用time_t表示的時(shí)間(日歷時(shí)間)是從一個(gè)時(shí)間點(diǎn)(例如:1970110時(shí)00)到此時(shí)的秒數(shù)。在time.h中,我們也可以看到time_t是一個(gè)長(zhǎng)整型數(shù):  

#ifndef _TIME_T_DEFINED  
typedef long time_t; /*
時(shí)間值 */  
#define _TIME_T_DEFINED /*
避免重復(fù)定義
time_t */  
#endif  

大家可能會(huì)產(chǎn)生疑問(wèn):既然time_t實(shí)際上是長(zhǎng)整型,到未來(lái)的某一天,從一個(gè)時(shí)間點(diǎn)(一般是1970110時(shí)00)到那時(shí)的秒數(shù)(即日歷時(shí)間)超出了長(zhǎng)整形所能表示的數(shù)的范圍怎么辦?對(duì)time_t數(shù)據(jù)類(lèi)型的值來(lái)說(shuō),它所表示的時(shí)間不能晚于203811819時(shí)1407。為了能夠表示更久遠(yuǎn)的時(shí)間,一些編譯器廠商引入了64位甚至更長(zhǎng)的整形數(shù)來(lái)保存日歷時(shí)間。比如微軟在Visual C++中采用了__time64_t數(shù)據(jù)類(lèi)型來(lái)保存日歷時(shí)間,并通過(guò)_time64()函數(shù)來(lái)獲得日歷時(shí)間(而不是通過(guò)使用32位字的time()函數(shù)),這樣就可以通過(guò)該數(shù)據(jù)類(lèi)型保存3001110時(shí)00(不包括該時(shí)間點(diǎn))之前的時(shí)間。  

time.h頭文件中,我們還可以看到一些函數(shù),它們都是以time_t為參數(shù)類(lèi)型或返回值類(lèi)型的函數(shù):

double difftime(time_t time1, time_t time0);  

time_t mktime(struct tm * timeptr); 

time_t time(time_t * timer); 

char * asctime(const struct tm * timeptr);

char * ctime(const time_t *timer); 

此外,time.h還提供了兩種不同的函數(shù)將日歷時(shí)間(一個(gè)用time_t表示的整數(shù))轉(zhuǎn)換為我們平時(shí)看到的把年月日時(shí)分秒分開(kāi)顯示的時(shí)間格式tm

struct tm * gmtime(const time_t *timer); struct tm * localtime(const time_t * timer);

通過(guò)查閱MSDN,我們可以知道Microsoft C/C++ 7.0中時(shí)間點(diǎn)的值(time_t對(duì)象的值)是從189912310時(shí)00到該時(shí)間點(diǎn)所經(jīng)過(guò)的秒數(shù),而其它各種版本的Microsoft C/C++和所有不同版本的Visual C++都是計(jì)算的從1970110時(shí)00到該時(shí)間點(diǎn)所經(jīng)過(guò)的秒數(shù)。  

4.與日期和時(shí)間相關(guān)的函數(shù)及應(yīng)用

在本節(jié),我將向大家展示怎樣利用time.h中聲明的函數(shù)對(duì)時(shí)間進(jìn)行操作。這些操作包括取當(dāng)前時(shí)間、計(jì)算時(shí)間間隔、以不同的形式顯示時(shí)間等內(nèi)容。  

4.1 獲得日歷時(shí)間

我們可以通過(guò)time()函數(shù)來(lái)獲得日歷時(shí)間(Calendar Time),其原型為:

time_t time(time_t * timer);

如果你已經(jīng)聲明了參數(shù)timer,你可以從參數(shù)timer返回現(xiàn)在的日歷時(shí)間,同時(shí)也可以通過(guò)返回值返回現(xiàn)在的日歷時(shí)間,即從一個(gè)時(shí)間點(diǎn)(例如:1970110時(shí)00)到現(xiàn)在此時(shí)的秒數(shù)。如果參數(shù)為空(NUL),函數(shù)將只通過(guò)返回值返回現(xiàn)在的日歷時(shí)間,比如下面這個(gè)例子用來(lái)顯示當(dāng)前的日歷時(shí)間:

#include "time.h"  
#include "stdio.h"  
int main(void)  
{  
struct tm *ptr;  
time_t lt;  
lt =time(NUL);  
printf("The Calendar Time now is %d\n",lt);  
return 0;  
}  

運(yùn)行的結(jié)果與當(dāng)時(shí)的時(shí)間有關(guān),我當(dāng)時(shí)運(yùn)行的結(jié)果是:  

The Calendar Time now is 1122707619  

其中1122707619就是我運(yùn)行程序時(shí)的日歷時(shí)間。即從1970110時(shí)00到此時(shí)的秒數(shù)。  

4.2 獲得日期和時(shí)間  

這里說(shuō)的日期和時(shí)間就是我們平時(shí)所說(shuō)的年、月、日、時(shí)、分、秒等信息。從第2節(jié)我們已經(jīng)知道這些信息都保存在一個(gè)名為tm的結(jié)構(gòu)體中,那么如何將一個(gè)日歷時(shí)間保存為一個(gè)tm結(jié)構(gòu)的對(duì)象呢?  

其中可以使用的函數(shù)是gmtime()localtime(),這兩個(gè)函數(shù)的原型為:  

struct tm * gmtime(const time_t *timer);  
struct tm * localtime(const time_t * timer);  

其中gmtime()函數(shù)是將日歷時(shí)間轉(zhuǎn)化為世界標(biāo)準(zhǔn)時(shí)間(即格林尼治時(shí)間),并返回一個(gè)tm結(jié)構(gòu)體來(lái)保存這個(gè)時(shí)間,而localtime()函數(shù)是將日歷時(shí)間轉(zhuǎn)化為本地時(shí)間。比如現(xiàn)在用gmtime()函數(shù)獲得的世界標(biāo)準(zhǔn)時(shí)間是20057307點(diǎn)1820,那么我用localtime()函數(shù)在中國(guó)地區(qū)獲得的本地時(shí)間會(huì)比世界標(biāo)準(zhǔn)時(shí)間晚8個(gè)小時(shí),即200573015點(diǎn)1820。下面是個(gè)例子:  

#include "time.h"  
#include "stdio.h"  
int main(void)  
{  
struct tm *local;  
time_t t;  
t=time(NUL);  
local=localtime(&t);  
printf("Local hour is: %d\n",local->tm_hour);  
local=gmtime(&t);  
printf("UTC hour is: %d\n",local->tm_hour);  
return 0;  
}  

運(yùn)行結(jié)果是:  

Local hour is: 15  
UTC hour is: 7  

4.3 固定的時(shí)間格式  

我們可以通過(guò)asctime()函數(shù)和ctime()函數(shù)將時(shí)間以固定的格式顯示出來(lái),兩者的返回值都是char*型的字符串。返回的時(shí)間格式為:  

星期幾 月份日期 時(shí)::秒 年\n\0  
例如:Wed Jan 02 02:03:55 1980\n\0  

其中\n是一個(gè)換行符,\0是一個(gè)空字符,表示字符串結(jié)束。下面是兩個(gè)函數(shù)的原型:  

char * asctime(const struct tm * timeptr);  
char * ctime(const time_t *timer);  

其中asctime()函數(shù)是通過(guò)tm結(jié)構(gòu)來(lái)生成具有固定格式的保存時(shí)間信息的字符串,而ctime()是通過(guò)日歷時(shí)間來(lái)生成時(shí)間字符串。這樣的話,asctime()函數(shù)只是把tm結(jié)構(gòu)對(duì)象中的各個(gè)域填到時(shí)間字符串的相應(yīng)位置就行了,而ctime()函數(shù)需要先參照本地的時(shí)間設(shè)置,把日歷時(shí)間轉(zhuǎn)化為本地時(shí)間,然后再生成格式化后的字符串。在下面,如果t是一個(gè)非空的time_t變量的話,那么:  

printf(ctime(&t));  

等價(jià)于:  

struct tm *ptr;  
ptr=localtime(&t);  
printf(asctime(ptr));  

那么,下面這個(gè)程序的兩條printf語(yǔ)句輸出的結(jié)果就是不同的了(除非你將本地時(shí)區(qū)設(shè)為世界標(biāo)準(zhǔn)時(shí)間所在的時(shí)區(qū)):  

#include "time.h"  
#include "stdio.h"  
int main(void)  
{  
struct tm *ptr;  
time_t lt;  
lt =time(NUL);  
ptr=gmtime(<);  
printf(asctime(ptr));  
printf(ctime(<));  
return 0;  
}  

運(yùn)行結(jié)果:  

Sat Jul 30 08:43:03 2005  
Sat Jul 30 16:43:03 2005  

4.4 自定義時(shí)間格式  

我們可以使用strftime()函數(shù)將時(shí)間格式化為我們想要的格式。它的原型如下:  

size_t strftime(  
char *strDest,  
size_t maxsize,  
const char *format,  
const struct tm *timeptr  
);
我們可以根據(jù)format指向字符串中格式命令把timeptr中保存的時(shí)間信息放在strDest指向的字符串中,最多向strDest中存放maxsize個(gè)字符。該函數(shù)返回向strDest指向的字符串中放置的字符數(shù)。  

函數(shù)strftime()的操作有些類(lèi)似于sprintf():識(shí)別以百分號(hào)(%)開(kāi)始的格式命令集合,格式化輸出結(jié)果放在一個(gè)字符串中。格式化命令說(shuō)明串strDest中各種日期和時(shí)間信息的確切表示方法。格式串中的其他字符原樣放進(jìn)串中。格式命令列在下面,它們是區(qū)分大小寫(xiě)的。  

%a 星期幾的簡(jiǎn)寫(xiě)  
%A
星期幾的全稱
  
%b
月分的簡(jiǎn)寫(xiě)
  
%B
月份的全稱
  
%c
標(biāo)準(zhǔn)的日期的時(shí)間串
  
%C
年份的后兩位數(shù)字
  
%d
十進(jìn)制表示的每月的第幾天
  
%D
//
  
%e
在兩字符域中,十進(jìn)制表示的每月的第幾天
  
%F
--
  
%g
年份的后兩位數(shù)字,使用基于周的年
  
%G
年分,使用基于周的年
  
%h
簡(jiǎn)寫(xiě)的月份名
  
%H 24
小時(shí)制的小時(shí)
  
%I 12
小時(shí)制的小時(shí)
  
%j
十進(jìn)制表示的每年的第幾天
  
%m
十進(jìn)制表示的月份
  
%M
十時(shí)制表示的分鐘數(shù)
  
%n
新行符
  
%p
本地的AMPM的等價(jià)顯示
  
%r 12
小時(shí)的時(shí)間
  
%R
顯示小時(shí)和分鐘:
hh:mm  
%S
十進(jìn)制的秒數(shù)
  
%t
水平制表符
  
%T
顯示時(shí)分秒:
hh:mm:ss  
%u
每周的第幾天,星期一為第一天 (值從06,星期一為0
  
%U
第年的第幾周,把星期日做為第一天(值從053
  
%V
每年的第幾周,使用基于周的年
  
%w
十進(jìn)制表示的星期幾(值從06,星期天為0
  
%W
每年的第幾周,把星期一做為第一天(值從053
  
%x
標(biāo)準(zhǔn)的日期串
  
%X
標(biāo)準(zhǔn)的時(shí)間串
  
%y
不帶世紀(jì)的十進(jìn)制年份(值從099
  
%Y
帶世紀(jì)部分的十進(jìn)制年份
  
%z
%Z 時(shí)區(qū)名稱,如果不能得到時(shí)區(qū)名稱則返回空字符。
  
%%
百分號(hào)  

如果想顯示現(xiàn)在是幾點(diǎn)了,并以12小時(shí)制顯示,就象下面這段程序:  

#include “time.h”  
#include “stdio.h”  
int main(void)  
{  
struct tm *ptr;  
time_t lt;  
char str[80];  
lt=time(NUL);  
ptr=localtime(<);  
strftime(str,100,"It is now %I %p",ptr);  
printf(str);  
return 0;  
}  

其運(yùn)行結(jié)果為:  
It is now 4PM  

而下面的程序則顯示當(dāng)前的完整日期:  

#include <stdio.h>  
#include <time.h>  

void main( void )  
{  
struct tm *newtime;  
char tmpbuf[128];  
time_t lt1;  
time( <1 );  
newtime=localtime(<1);  
strftime( tmpbuf, 128, "Today is %A, day %d of %B in the year %Y.\n", newtime);  
printf(tmpbuf);  
}  

運(yùn)行結(jié)果:  

Today is Saturday, day 30 of July in the year 2005.  

4.5 計(jì)算持續(xù)時(shí)間的長(zhǎng)度  

有時(shí)候在實(shí)際應(yīng)用中要計(jì)算一個(gè)事件持續(xù)的時(shí)間長(zhǎng)度,比如計(jì)算打字速度。在第1節(jié)計(jì)時(shí)部分中,我已經(jīng)用clock函數(shù)舉了一個(gè)例子。Clock()函數(shù)可以精確到毫秒級(jí)。同時(shí),我們也可以使用difftime()函數(shù),但它只能精確到秒。該函數(shù)的定義如下:  

double difftime(time_t time1, time_t time0);  

雖然該函數(shù)返回的以秒計(jì)算的時(shí)間間隔是double類(lèi)型的,但這并不說(shuō)明該時(shí)間具有同double一樣的精確度,這是由它的參數(shù)覺(jué)得的(time_t是以秒為單位計(jì)算的)。比如下面一段程序:  

#include "time.h"  
#include "stdio.h"  
#include "stdlib.h"  
int main(void)  
{  
time_t start,end;  
start = time(NUL);  
system("pause");  
end = time(NUL);  
printf("The pause used %f seconds.\n",difftime(end,start));//<-  
system("pause");  
return 0;  
}  

運(yùn)行結(jié)果為:  
請(qǐng)按任意鍵繼續(xù)
. . .  
The pause used 2.000000 seconds.  
請(qǐng)按任意鍵繼續(xù). . .  

可以想像,暫停的時(shí)間并不那么巧是整整2秒鐘。其實(shí),你將上面程序的帶有“//<-”注釋的一行用下面的一行代碼替換:  

printf("The pause used %f seconds.\n",end-start);  

其運(yùn)行結(jié)果是一樣的。  

4.6 分解時(shí)間轉(zhuǎn)化為日歷時(shí)間  

這里說(shuō)的分解時(shí)間就是以年、月、日、時(shí)、分、秒等分量保存的時(shí)間結(jié)構(gòu),在C/C++中是tm結(jié)構(gòu)。我們可以使用mktime()函數(shù)將用tm結(jié)構(gòu)表示的時(shí)間轉(zhuǎn)化為日歷時(shí)間。其函數(shù)原型如下:  

time_t mktime(struct tm * timeptr);  

其返回值就是轉(zhuǎn)化后的日歷時(shí)間。這樣我們就可以先制定一個(gè)分解時(shí)間,然后對(duì)這個(gè)時(shí)間進(jìn)行操作了,下面的例子可以計(jì)算出199771是星期幾:  

#include "time.h"  
#include "stdio.h"  
#include "stdlib.h"  
int main(void)  
{  
struct tm t;  
time_t t_of_day;  
t.tm_year=1997-1900;  
t.tm_mon=6;  
t.tm_mday=1;  
t.tm_hour=0;  
t.tm_min=0;  
t.tm_sec=1;  
t.tm_isdst=0;  
t_of_day=mktime(&t);  
printf(ctime(&t_of_day));  
return 0;  
}  
運(yùn)行結(jié)果:  
Tue Jul 01 00:00:01 1997  
現(xiàn)在注意了,有了mktime()函數(shù),是不是我們可以操作現(xiàn)在之前的任何時(shí)間呢?你可以通過(guò)這種辦法算出1945815號(hào)是星期幾嗎?答案是否定的。因?yàn)檫@個(gè)時(shí)間在197011之前,所以在大多數(shù)編譯器中,這樣的程序雖然可以編譯通過(guò),但運(yùn)行時(shí)會(huì)異常終止。

posted @ 2007-04-05 23:53 frank.sunny 閱讀(1015) | 評(píng)論 (0)編輯 收藏
 

switch選擇結(jié)構(gòu)理解局部變量

 

函數(shù)體內(nèi)部自定義變量,稱為局部變量,存儲(chǔ)于棧(stack)中,由編譯器自動(dòng)分配和釋放,局部變量的生存期(或者說(shuō)作用域)是當(dāng)前函數(shù)內(nèi)部,使用時(shí)必須初始化,否則其值將不定。以前對(duì)局部變量的定義也就是這么多,而且也就那么在用。近期碰到如下一個(gè)問(wèn)題:

void func( void )

{

   int x = 2;

   switch ( x )

   {

         int m =0;       //initialization skipped by case0,case1,case2,default

   case 0 :

         int i = 0;        //initialization skipped by case1,case2,default

         { int j = 1; }   // OK, initialized in enclosing block

         break;

   case 1 :

         break;

   case 2:

         break;

   default:

         int k = 1;              // OK, initialization not skipped

   }

}

遇到這個(gè)問(wèn)題,網(wǎng)上的解答很多,很多人覺(jué)得switch內(nèi)不能定義局部變量,這個(gè)明顯是不對(duì)的。因?yàn)槲野汛a改成以下形式后就完全可以用了。

void func( void )

{

   int x = 2;

   switch ( x )

   {

         int m;

         m = 0;                   //without execute;

   case 0:

         int i;

         i = 0;

         { int j = 1; }   // OK, initialized in enclosing block

         printf("%d    %d\n", m, i);

         break;

   case 1:

         i = 1;

         printf("%d    %d\n", m, i);

         break;

   case 2:

         i = 2;

         printf("%d    %d\n", m, i);

         break;

   default:

         int k = 1;              // OK, initialization not skipped

   }

}

編譯時(shí)有一個(gè)warning,即“local variable 'm' used without having been initialized”,執(zhí)行結(jié)果為:-858993460       2

因此switch內(nèi)不但可以定義變量,而且也不用像很多人所說(shuō)的在case內(nèi)遇到要用變量時(shí)一定要用{}括起來(lái),不過(guò)嚴(yán)格的說(shuō)不用{}擴(kuò)起來(lái)的變量是是屬于整個(gè)switch塊結(jié)構(gòu)的,為此編程一定要將新增變量作用域限定在case內(nèi)就必須要用{}

通過(guò)switch···case結(jié)構(gòu),對(duì)局部變量的聲明、定義以及初始化等概念可以有一個(gè)比較清晰的認(rèn)識(shí)。我的理解就是:聲明語(yǔ)句不管是放在哪里,其編譯時(shí)都是將其置頂?shù)綁K的頭部,如int k雖然在default中,但是這個(gè)變量的聲明就在switch{}內(nèi),其生存期與變量m等同,只是由于前面沒(méi)有聲明,所以default之前不能用。

posted @ 2007-04-05 23:25 frank.sunny 閱讀(1497) | 評(píng)論 (0)編輯 收藏

?

關(guān)于內(nèi)存映射文件處理

今天看到一篇文章講內(nèi)存映射文件的處理,雖然自己沒(méi)有處理過(guò)如此大的文件系統(tǒng),但是好奇就也看了下,誰(shuí)知道自己以后會(huì)不會(huì)用到或考到這方面的知識(shí)。所以就給自己 mark 一下,增加點(diǎn)自己的印象。

首先,通過(guò) CreateFile() 函數(shù)來(lái)創(chuàng)建或打開(kāi)一個(gè)文件內(nèi)核對(duì)象,這個(gè)對(duì)象標(biāo)識(shí)了磁盤(pán)上將要用作內(nèi)存映射文件的文件。(其實(shí)是獲取文件句柄)

其次,通過(guò) CreateFileMapping() 函數(shù)來(lái)為剛才創(chuàng)建的文件內(nèi)核對(duì)象創(chuàng)建一個(gè)文件映射內(nèi)核對(duì)象并告訴系統(tǒng)文件的尺寸以及訪問(wèn)文件的方式。(獲取文件映射內(nèi)核對(duì)象的句柄)

再次,通過(guò) MapViewOfFile() 函數(shù)將文件內(nèi)核映射對(duì)象添加到進(jìn)程中。(獲取映射內(nèi)核對(duì)象的指針)

接著,程序就可以通過(guò)指針進(jìn)行常規(guī)的文件讀取了,這里的操作就和文件操作一樣,不做展開(kāi)。

用完之后,還得回收,先用 UnmapViewOfFile() 將釋放映射內(nèi)核對(duì)象指針,然后通過(guò) CloseHandle 關(guān)閉之前創(chuàng)建的文件映射內(nèi)核對(duì)象句柄和文件內(nèi)核對(duì)象句柄。

?

以下是我找到的文章的出處: http://newcactus.bokee.com/viewdiary.15316244.html

下面純粹是粘貼別人的作品:

VC++ 中使用內(nèi)存映射文件處理大文件

摘要: 本文給出了一種方便實(shí)用的解決大文件的讀取、存儲(chǔ)等處理的方法,并結(jié)合相關(guān)程序代碼對(duì)具體的實(shí)現(xiàn)過(guò)程進(jìn)行了介紹。

  引言

  文件操作是應(yīng)用程序最為基本的功能之一,Win32 APIMFC均提供有支持文件處理的函數(shù)和類(lèi),常用的有Win32 APICreateFile()WriteFile()ReadFile()MFC提供的CFile類(lèi)等。一般來(lái)說(shuō),以上這些函數(shù)可以滿足大多數(shù)場(chǎng)合的要求,但是對(duì)于某些特殊應(yīng)用領(lǐng)域所需要的動(dòng)輒幾十GB、幾百GB、乃至幾TB的海量存儲(chǔ),再以通常的文件處理方法進(jìn)行處理顯然是行不通的。目前,對(duì)于上述這種大文件的操作一般是以內(nèi)存映射文件的方式來(lái)加以處理的,本文下面將針對(duì)這種Windows核心編程技術(shù)展開(kāi)討論。

  內(nèi)存映射文件

  內(nèi)存映射文件與虛擬內(nèi)存有些類(lèi)似,通過(guò)內(nèi)存映射文件可以保留一個(gè)地址空間的區(qū)域,同時(shí)將物理存儲(chǔ)器提交給此區(qū)域,只是內(nèi)存文件映射的物理存儲(chǔ)器來(lái)自一個(gè)已經(jīng)存在于磁盤(pán)上的文件,而非系統(tǒng)的頁(yè)文件,而且在對(duì)該文件進(jìn)行操作之前必須首先對(duì)文件進(jìn)行映射,就如同將整個(gè)文件從磁盤(pán)加載到內(nèi)存。由此可以看出,使用內(nèi)存映射文件處理存儲(chǔ)于磁盤(pán)上的文件時(shí),將不必再對(duì)文件執(zhí)行I/O操作,這意味著在對(duì)文件進(jìn)行處理時(shí)將不必再為文件申請(qǐng)并分配緩存,所有的文件緩存操作均由系統(tǒng)直接管理,由于取消了將文件數(shù)據(jù)加載到內(nèi)存、數(shù)據(jù)從內(nèi)存到文件的回寫(xiě)以及釋放內(nèi)存塊等步驟,使得內(nèi)存映射文件在處理大數(shù)據(jù)量的文件時(shí)能起到相當(dāng)重要的作用。另外,實(shí)際工程中的系統(tǒng)往往需要在多個(gè)進(jìn)程之間共享數(shù)據(jù),如果數(shù)據(jù)量小,處理方法是靈活多變的,如果共享數(shù)據(jù)容量巨大,那么就需要借助于內(nèi)存映射文件來(lái)進(jìn)行。實(shí)際上,內(nèi)存映射文件正是解決本地多個(gè)進(jìn)程間數(shù)據(jù)共享的最有效方法。

  內(nèi)存映射文件并不是簡(jiǎn)單的文件I/O操作,實(shí)際用到了Windows的核心編程技術(shù)--內(nèi)存管理。所以,如果想對(duì)內(nèi)存映射文件有更深刻的認(rèn)識(shí),必須對(duì)Windows操作系統(tǒng)的內(nèi)存管理機(jī)制有清楚的認(rèn)識(shí),內(nèi)存管理的相關(guān)知識(shí)非常復(fù)雜,超出了本文的討論范疇,在此就不再贅述,感興趣的讀者可以參閱其他相關(guān)書(shū)籍。下面給出使用內(nèi)存映射文件的一般方法:

  首先要通過(guò)CreateFile()函數(shù)來(lái)創(chuàng)建或打開(kāi)一個(gè)文件內(nèi)核對(duì)象,這個(gè)對(duì)象標(biāo)識(shí)了磁盤(pán)上將要用作內(nèi)存映射文件的文件。在用CreateFile()將文件映像在物理存儲(chǔ)器的位置通告給操作系統(tǒng)后,只指定了映像文件的路徑,映像的長(zhǎng)度還沒(méi)有指定。為了指定文件映射對(duì)象需要多大的物理存儲(chǔ)空間還需要通過(guò)CreateFileMapping()函數(shù)來(lái)創(chuàng)建一個(gè)文件映射內(nèi)核對(duì)象以告訴系統(tǒng)文件的尺寸以及訪問(wèn)文件的方式。在創(chuàng)建了文件映射對(duì)象后,還必須為文件數(shù)據(jù)保留一個(gè)地址空間區(qū)域,并把文件數(shù)據(jù)作為映射到該區(qū)域的物理存儲(chǔ)器進(jìn)行提交。由MapViewOfFile()函數(shù)負(fù)責(zé)通過(guò)系統(tǒng)的管理而將文件映射對(duì)象的全部或部分映射到進(jìn)程地址空間。此時(shí),對(duì)內(nèi)存映射文件的使用和處理同通常加載到內(nèi)存中的文件數(shù)據(jù)的處理方式基本一樣,在完成了對(duì)內(nèi)存映射文件的使用時(shí),還要通過(guò)一系列的操作完成對(duì)其的清除和使用過(guò)資源的釋放。這部分相對(duì)比較簡(jiǎn)單,可以通過(guò)UnmapViewOfFile()完成從進(jìn)程的地址空間撤消文件數(shù)據(jù)的映像、通過(guò)CloseHandle()關(guān)閉前面創(chuàng)建的文件映射對(duì)象和文件對(duì)象。

  內(nèi)存映射文件相關(guān)函數(shù)

  在使用內(nèi)存映射文件時(shí),所使用的API函數(shù)主要就是前面提到過(guò)的那幾個(gè)函數(shù),下面分別對(duì)其進(jìn)行介紹:

HANDLE CreateFile(LPCTSTR lpFileName,
DWORD dwDesiredAccess,
DWORD dwShareMode,
LPSECURITY_ATTRIBUTES lpSecurityAttributes,
DWORD dwCreationDisposition,
DWORD dwFlagsAndAttributes,
HANDLE hTemplateFile);


  函數(shù)CreateFile()即使是在普通的文件操作時(shí)也經(jīng)常用來(lái)創(chuàng)建、打開(kāi)文件,在處理內(nèi)存映射文件時(shí),該函數(shù)來(lái)創(chuàng)建/打開(kāi)一個(gè)文件內(nèi)核對(duì)象,并將其句柄返回,在調(diào)用該函數(shù)時(shí)需要根據(jù)是否需要數(shù)據(jù)讀寫(xiě)和文件的共享方式來(lái)設(shè)置參數(shù)dwDesiredAccessdwShareMode,錯(cuò)誤的參數(shù)設(shè)置將會(huì)導(dǎo)致相應(yīng)操作時(shí)的失敗。

HANDLE CreateFileMapping(HANDLE hFile,
LPSECURITY_ATTRIBUTES lpFileMappingAttributes,
DWORD flProtect,
DWORD dwMaximumSizeHigh,
DWORD dwMaximumSizeLow,
LPCTSTR lpName);


  CreateFileMapping()函數(shù)創(chuàng)建一個(gè)文件映射內(nèi)核對(duì)象,通過(guò)參數(shù)hFile指定待映射到進(jìn)程地址空間的文件句柄(該句柄由CreateFile()函數(shù)的返回值獲取)。由于內(nèi)存映射文件的物理存儲(chǔ)器實(shí)際是存儲(chǔ)于磁盤(pán)上的一個(gè)文件,而不是從系統(tǒng)的頁(yè)文件中分配的內(nèi)存,所以系統(tǒng)不會(huì)主動(dòng)為其保留地址空間區(qū)域,也不會(huì)自動(dòng)將文件的存儲(chǔ)空間映射到該區(qū)域,為了讓系統(tǒng)能夠確定對(duì)頁(yè)面采取何種保護(hù)屬性,需要通過(guò)參數(shù)flProtect來(lái)設(shè)定,保護(hù)屬性PAGE_READONLYPAGE_READWRITEPAGE_WRITECOPY分別表示文件映射對(duì)象被映射后,可以讀取、讀寫(xiě)文件數(shù)據(jù)。在使用PAGE_READONLY時(shí),必須確保CreateFile()采用的是GENERIC_READ參數(shù);PAGE_READWRITE則要求CreateFile()采用的是GENERIC_READ|GENERIC_WRITE參數(shù);至于屬性PAGE_WRITECOPY則只需要確保CreateFile()采用了GENERIC_READGENERIC_WRITE其中之一即可。DWORD型的參數(shù)dwMaximumSizeHighdwMaximumSizeLow也是相當(dāng)重要的,指定了文件的最大字節(jié)數(shù),由于這兩個(gè)參數(shù)共64位,因此所支持的最大文件長(zhǎng)度為16EB,幾乎可以滿足任何大數(shù)據(jù)量文件處理場(chǎng)合的要求。

LPVOID MapViewOfFile(HANDLE hFileMappingObject,
DWORD dwDesiredAccess,
DWORD dwFileOffsetHigh,
DWORD dwFileOffsetLow,
DWORD dwNumberOfBytesToMap);


  MapViewOfFile()函數(shù)負(fù)責(zé)把文件數(shù)據(jù)映射到進(jìn)程的地址空間,參數(shù)hFileMappingObjectCreateFileMapping()返回的文件映像對(duì)象句柄。參數(shù)dwDesiredAccess則再次指定了對(duì)文件數(shù)據(jù)的訪問(wèn)方式,而且同樣要與CreateFileMapping()函數(shù)所設(shè)置的保護(hù)屬性相匹配。雖然這里一再對(duì)保護(hù)屬性進(jìn)行重復(fù)設(shè)置看似多余,但卻可以使應(yīng)用程序能更多的對(duì)數(shù)據(jù)的保護(hù)屬性實(shí)行有效控制。MapViewOfFile()函數(shù)允許全部或部分映射文件,在映射時(shí),需要指定數(shù)據(jù)文件的偏移地址以及待映射的長(zhǎng)度。其中,文件的偏移地址由DWORD型的參數(shù)dwFileOffsetHighdwFileOffsetLow組成的64位值來(lái)指定,而且必須是操作系統(tǒng)的分配粒度的整數(shù)倍,對(duì)于Windows操作系統(tǒng),分配粒度固定為64KB。當(dāng)然,也可以通過(guò)如下代碼來(lái)動(dòng)態(tài)獲取當(dāng)前操作系統(tǒng)的分配粒度:

SYSTEM_INFO sinf;
GetSystemInfo(&sinf);
DWORD dwAllocationGranularity = sinf.dwAllocationGranularity;


  參數(shù)dwNumberOfBytesToMap指定了數(shù)據(jù)文件的映射長(zhǎng)度,這里需要特別指出的是,對(duì)于Windows 9x操作系統(tǒng),如果MapViewOfFile()無(wú)法找到足夠大的區(qū)域來(lái)存放整個(gè)文件映射對(duì)象,將返回空值(NULL);但是在Windows 2000下,MapViewOfFile()只需要為必要的視圖找到足夠大的一個(gè)區(qū)域即可,而無(wú)須考慮整個(gè)文件映射對(duì)象的大小。

  在完成對(duì)映射到進(jìn)程地址空間區(qū)域的文件處理后,需要通過(guò)函數(shù)UnmapViewOfFile()完成對(duì)文件數(shù)據(jù)映像的釋放,該函數(shù)原型聲明如下:

BOOL UnmapViewOfFile(LPCVOID lpBaseAddress);


  唯一的參數(shù)lpBaseAddress指定了返回區(qū)域的基地址,必須將其設(shè)定為MapViewOfFile()的返回值。在使用了函數(shù)MapViewOfFile()之后,必須要有對(duì)應(yīng)的UnmapViewOfFile()調(diào)用,否則在進(jìn)程終止之前,保留的區(qū)域?qū)o(wú)法釋放。除此之外,前面還曾由CreateFile()CreateFileMapping()函數(shù)創(chuàng)建過(guò)文件內(nèi)核對(duì)象和文件映射內(nèi)核對(duì)象,在進(jìn)程終止之前有必要通過(guò)CloseHandle()將其釋放,否則將會(huì)出現(xiàn)資源泄漏的問(wèn)題。

  除了前面這些必須的API函數(shù)之外,在使用內(nèi)存映射文件時(shí)還要根據(jù)情況來(lái)選用其他一些輔助函數(shù)。例如,在使用內(nèi)存映射文件時(shí),為了提高速度,系統(tǒng)將文件的數(shù)據(jù)頁(yè)面進(jìn)行高速緩存,而且在處理文件映射視圖時(shí)不立即更新文件的磁盤(pán)映像。為解決這個(gè)問(wèn)題可以考慮使用FlushViewOfFile()函數(shù),該函數(shù)強(qiáng)制系統(tǒng)將修改過(guò)的數(shù)據(jù)部分或全部重新寫(xiě)入磁盤(pán)映像,從而可以確保所有的數(shù)據(jù)更新能及時(shí)保存到磁盤(pán)。
使用內(nèi)存映射文件處理大文件應(yīng)用示例

  下面結(jié)合一個(gè)具體的實(shí)例來(lái)進(jìn)一步講述內(nèi)存映射文件的使用方法。該實(shí)例從端口接收數(shù)據(jù),并實(shí)時(shí)將其存放于磁盤(pán),由于數(shù)據(jù)量大(幾十GB),在此選用內(nèi)存映射文件進(jìn)行處理。下面給出的是位于工作線程MainProc中的部分主要代碼,該線程自程序運(yùn)行時(shí)啟動(dòng),當(dāng)端口有數(shù)據(jù)到達(dá)時(shí)將會(huì)發(fā)出事件hEvent[0]WaitForMultipleObjects()函數(shù)等待到該事件發(fā)生后將接收到的數(shù)據(jù)保存到磁盤(pán),如果終止接收將發(fā)出事件hEvent[1],事件處理過(guò)程將負(fù)責(zé)完成資源的釋放和文件的關(guān)閉等工作。下面給出此線程處理函數(shù)的具體實(shí)現(xiàn)過(guò)程:

……
//
創(chuàng)建文件內(nèi)核對(duì)象,其句柄保存于hFile
HANDLE hFile = CreateFile("Recv1.zip",
GENERIC_WRITE | GENERIC_READ,
FILE_SHARE_READ,
NULL,
CREATE_ALWAYS,
FILE_FLAG_SEQUENTIAL_SCAN,
NULL);

//
創(chuàng)建文件映射內(nèi)核對(duì)象,句柄保存于hFileMapping
HANDLE hFileMapping = CreateFileMapping(hFile,NULL,PAGE_READWRITE,
0, 0x4000000, NULL);
//
釋放文件內(nèi)核對(duì)象
CloseHandle(hFile);

//
設(shè)定大小、偏移量等參數(shù)
__int64 qwFileSize = 0x4000000;
__int64 qwFileOffset = 0;
__int64 T = 600 * sinf.dwAllocationGranularity;
DWORD dwBytesInBlock = 1000 * sinf.dwAllocationGranularity;

//
將文件數(shù)據(jù)映射到進(jìn)程的地址空間
PBYTE pbFile = (PBYTE)MapViewOfFile(hFileMapping,
FILE_MAP_ALL_ACCESS,
(DWORD)(qwFileOffset>>32), (DWORD)(qwFileOffset&0xFFFFFFFF), dwBytesInBlock);
while(bLoop)
{
//
捕獲事件hEvent[0]和事件hEvent[1]
DWORD ret = WaitForMultipleObjects(2, hEvent, FALSE, INFINITE);
ret -= WAIT_OBJECT_0;
switch (ret)
{
//
接收數(shù)據(jù)事件觸發(fā)
case 0:
//
從端口接收數(shù)據(jù)并保存到內(nèi)存映射文件
nReadLen=syio_Read(port[1], pbFile + qwFileOffset, QueueLen);
qwFileOffset += nReadLen;

//
當(dāng)數(shù)據(jù)寫(xiě)滿60%時(shí),為防數(shù)據(jù)溢出,需要在其后開(kāi)辟一新的映射視圖
if (qwFileOffset > T)
{
T = qwFileOffset + 600 * sinf.dwAllocationGranularity;
UnmapViewOfFile(pbFile);
pbFile = (PBYTE)MapViewOfFile(hFileMapping,
FILE_MAP_ALL_ACCESS,
(DWORD)(qwFileOffset>>32), (DWORD)(qwFileOffset&0xFFFFFFFF), dwBytesInBlock);
}
break;

//
終止事件觸發(fā)
case 1:
bLoop = FALSE;

//
從進(jìn)程的地址空間撤消文件數(shù)據(jù)映像
UnmapViewOfFile(pbFile);

//
關(guān)閉文件映射對(duì)象
CloseHandle(hFileMapping);
break;
}
}


  在終止事件觸發(fā)處理過(guò)程中如果只簡(jiǎn)單的執(zhí)行UnmapViewOfFile()CloseHandle()函數(shù)將無(wú)法正確標(biāo)識(shí)文件的實(shí)際大小,即如果開(kāi)辟的內(nèi)存映射文件為30GB,而接收的數(shù)據(jù)只有14GB,那么上述程序執(zhí)行完后,保存的文件長(zhǎng)度仍是30GB。也就是說(shuō),在處理完成后還要再次通過(guò)內(nèi)存映射文件的形式將文件恢復(fù)到實(shí)際大小,下面是實(shí)現(xiàn)此要求的主要代碼:

// 創(chuàng)建另外一個(gè)文件內(nèi)核對(duì)象
hFile2 = CreateFile("Recv.zip",
GENERIC_WRITE | GENERIC_READ,
FILE_SHARE_READ,
NULL,
CREATE_ALWAYS,
FILE_FLAG_SEQUENTIAL_SCAN,
NULL);

//
以實(shí)際數(shù)據(jù)長(zhǎng)度創(chuàng)建另外一個(gè)文件映射內(nèi)核對(duì)象
hFileMapping2 = CreateFileMapping(hFile2,
NULL,
PAGE_READWRITE,
0,
(DWORD)(qwFileOffset&0xFFFFFFFF),
NULL);

//
關(guān)閉文件內(nèi)核對(duì)象
CloseHandle(hFile2);

//
將文件數(shù)據(jù)映射到進(jìn)程的地址空間
pbFile2 = (PBYTE)MapViewOfFile(hFileMapping2,
FILE_MAP_ALL_ACCESS,
0, 0, qwFileOffset);

//
將數(shù)據(jù)從原來(lái)的內(nèi)存映射文件復(fù)制到此內(nèi)存映射文件
memcpy(pbFile2, pbFile, qwFileOffset);

file://
從進(jìn)程的地址空間撤消文件數(shù)據(jù)映像
UnmapViewOfFile(pbFile);
UnmapViewOfFile(pbFile2);

//
關(guān)閉文件映射對(duì)象
CloseHandle(hFileMapping);
CloseHandle(hFileMapping2);

//
刪除臨時(shí)文件
DeleteFile("Recv1.zip");


  結(jié)論

  經(jīng)實(shí)際測(cè)試,內(nèi)存映射文件在處理大數(shù)據(jù)量文件時(shí)表現(xiàn)出了良好的性能,比通常使用CFile類(lèi)和ReadFile()WriteFile()等函數(shù)的文件處理方式具有明顯的優(yōu)勢(shì)。本文所述代碼在Windows 98下由Microsoft Visual C++ 6.0編譯通過(guò)。

?

posted @ 2007-03-30 21:16 frank.sunny 閱讀(4673) | 評(píng)論 (0)編輯 收藏
     摘要: 聞道有先后,術(shù)業(yè)有專功 ? 對(duì)于網(wǎng)絡(luò),我似乎是后知后覺(jué)的多些。特別是畢業(yè)以后的一年半多的工作生涯中,完全沒(méi)有好好的接觸過(guò)網(wǎng)絡(luò)這個(gè)資源。一開(kāi)題就跑題了,言歸正傳,本科畢業(yè)后一段時(shí)間才知道有林銳這么一個(gè)人,還拜讀了他寫(xiě)的《大學(xué)十年》,當(dāng)時(shí)感想頗多,但是沒(méi)有記下來(lái),所以現(xiàn)在想來(lái)好像也沒(méi)有其它太大的感想,現(xiàn)在記得當(dāng)時(shí)可能想做的...  閱讀全文
posted @ 2007-03-30 20:40 frank.sunny 閱讀(2313) | 評(píng)論 (4)編輯 收藏

網(wǎng)訊筆試歸來(lái)

昨天去網(wǎng)訊(杭州)筆試了,做了下筆試題,感覺(jué)題目都不難,但是自己做的的確不怎么樣,估計(jì)是沒(méi)機(jī)會(huì)去了,不過(guò)暫時(shí)還是先把幾道自己還記得的題目,寫(xiě)出來(lái),總結(jié)下,以做復(fù)習(xí)。

1、  要求自己實(shí)現(xiàn) String 類(lèi),給出了 String 類(lèi)的以下頭文件類(lèi)聲明

class String

{

public:

       String(const char *m_char = NULL);

       String(const String & Str);

       String& operator = (const String &Str);

       ~String();

private:

       char * m_Data;

};

 

關(guān)于 String 類(lèi)的筆試題,以前看林銳的隨筆時(shí)聽(tīng)說(shuō)他在微軟面試時(shí)曾碰到那么一道題目,我自己也沒(méi)有真的下筆去做過(guò),平常都是拿來(lái)就用的,這次自己碰到,才知道會(huì)死得那么慘,反正編得不堪入目(我就不拿出來(lái)獻(xiàn)丑了),下面是我回來(lái)后,自己重新寫(xiě)的答案。

String::String(const char* m_char)

{

       int m_nLength = strlen(m_char) + 1;

       if (m_Data != NULL)

       {

              delete [] m_Data;

              m_Data = NULL;

       }// 以上判斷是否必要 ??

       m_Data = new char[m_nLength];

       memcpy(m_Data, m_char, m_nLength);

}

 

String::String(const String &Str)

{

       int m_nLength = strlen(Str.m_Data) + 1;// 以前真的不知道,原來(lái)對(duì)象的私有變量

// 在類(lèi)的實(shí)現(xiàn)代碼中也是可以訪問(wèn)的

       if (m_Data != NULL)

       {

              delete [] m_Data;

              m_Data = NULL;

       }// 以上判斷是否必要 ??

       m_Data = new char[m_nLength];

       memcpy(m_Data, Str.m_Data, m_nLength);

}

 

String& String::operator = (const String& Str) 

{

       if(this == &Str)

              return *this;

 

       int m_nLength = strlen(Str.m_Data) + 1;

       if (m_Data != NULL)

       {

              delete [] m_Data;

              m_Data = NULL;

       }// 以上判斷是否必要 ??

      

       m_Data = new char[m_nLength];

       memcpy(m_Data, Str.m_Data, m_nLength);

       return *this;

}

 

String::~String()

{

       if (m_Data != NULL)

       {

              delete [] m_Data;

              m_Data = NULL;

       }

}

 

2、  關(guān)于內(nèi)存分配

這個(gè)題目很簡(jiǎn)單,就給了一個(gè)函數(shù),然后問(wèn)函數(shù)內(nèi)的局部變量存放在哪里,我也不知道為什么當(dāng)時(shí)會(huì)選擇 heap( ) ,下面再把幾個(gè)概念羅列出來(lái):

1.            堆區(qū)( heap ):由程序員申請(qǐng)分配和釋放,屬動(dòng)態(tài)內(nèi)存分配方式,若程序員不釋放,程序結(jié)束時(shí)可能會(huì)由 OS 回收。不過(guò)這個(gè)內(nèi)存分配很容易引起問(wèn)題,如果申請(qǐng)的內(nèi)存不釋放就會(huì)造成內(nèi)存泄漏;如果釋放的不是所要釋放的內(nèi)存,則輕者引起程序運(yùn)行結(jié)果出錯(cuò),重者系統(tǒng)崩潰。

2.            棧區(qū)( stack ):編譯器自動(dòng)分配釋放,存放函數(shù)的形參值、局部變量的值,也是屬于動(dòng)態(tài)內(nèi)存分配方式,它由系統(tǒng)分配,所以執(zhí)行效率也高,不過(guò)自由度小,聲明時(shí)就得決定其具體大小。

3.            全局區(qū)(靜態(tài)區(qū))( static ):全局變量和靜態(tài)變量的存儲(chǔ)是放在一塊的,而且初始化的全局變量和靜態(tài)變量在一塊區(qū)域,未初始化的全局變量和未初始化的靜態(tài)變量在相鄰的另一塊區(qū)域。程序結(jié)束后由系統(tǒng)釋放,所以也不會(huì)造成內(nèi)存問(wèn)題。

除了以上的變量外,還有兩類(lèi)存放位置,文字常量區(qū)和程序代碼區(qū),兩者都是由系統(tǒng)分配和釋放,且文字常量區(qū)和前面三區(qū)合成為程序數(shù)據(jù)區(qū),與程序代碼區(qū)相對(duì)應(yīng)。

3、  關(guān)于類(lèi)繼承的構(gòu)造和析構(gòu)函數(shù)

class Base

{

public:

       Base(){cout<< "Base" <<endl;};

       ~Base(){cout<<"~Base"<<endl;};

protected:

private:

};

 

class First:public Base

{

public:

       First(){cout << "First" << endl;};

       ~First(){cout << "~First" <<endl;};

};

 

int main()

{

       Base *a = new First;

       delete a;

}

問(wèn)程序的輸出會(huì)是什么?

結(jié)果很簡(jiǎn)單,也就是 Base

                               First

                   ~Base

其它還有一個(gè)關(guān)于 & 的題目,把我搞的云里霧里的,還要再看些東西才知道怎么來(lái)解釋。

posted @ 2007-03-04 21:53 frank.sunny 閱讀(3408) | 評(píng)論 (10)編輯 收藏
     摘要: 雖然自己用多線程編程用過(guò)一陣子,但是未曾仔細(xì)了解過(guò)概念,用的也是亂亂的,今天看到一篇線程總結(jié)的文章,感覺(jué)講的很好,Windows下的多線程也就是了解了線程的概念然后加一同步代碼就行了。 Windows平臺(tái)下的多線程編程 ? ...  閱讀全文
posted @ 2007-01-26 22:10 frank.sunny 閱讀(2561) | 評(píng)論 (0)編輯 收藏
僅列出標(biāo)題
共7頁(yè): 1 2 3 4 5 6 7 

常用鏈接

留言簿(13)

隨筆分類(lèi)

個(gè)人其它博客

基礎(chǔ)知識(shí)鏈接

最新評(píng)論

閱讀排行榜

評(píng)論排行榜

青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品
  • <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>
            亚洲高清视频中文字幕| 亚洲国产婷婷| 免费av成人在线| 久久精品视频va| 久久全球大尺度高清视频| 亚洲午夜一区| 亚洲一区二区三区四区视频| 一区二区三区精品| 欧美一级在线播放| 久久综合激情| 亚洲欧洲日韩在线| 亚洲一区二区三区视频播放| 午夜精品久久久久久久白皮肤| 久久国产综合精品| 欧美大片91| 国产女人18毛片水18精品| 在线看不卡av| 欧美一级午夜免费电影| 欧美激情一区二区三区| 一区二区三区 在线观看视| 久久大综合网| 欧美性开放视频| 永久免费毛片在线播放不卡| 一区二区高清视频| 蜜臀av国产精品久久久久| 99国产精品久久久久久久成人热| 久久爱www久久做| 欧美日韩国产色站一区二区三区| 国产视频在线观看一区二区三区 | 亚洲欧美日韩中文视频| 久久亚洲免费| 亚洲一区视频在线| 欧美日韩成人综合| 在线观看一区| 久久精品国产精品| 在线天堂一区av电影| 欧美jizzhd精品欧美巨大免费| 国产欧美日韩视频| 亚洲一本视频| 亚洲精品韩国| 欧美日韩国内自拍| 91久久精品一区二区三区| 久久国产精品亚洲va麻豆| 亚洲一级电影| 国产精品成人一区二区三区夜夜夜 | 亚洲精品小视频在线观看| 亚洲一区二区三区乱码aⅴ| 久久精品国产欧美激情| 亚洲国产精品嫩草影院| 久久久国产一区二区| 国产欧美日韩在线观看| 亚洲影视综合| 一本久道综合久久精品| 欧美激情免费在线| 亚洲人成亚洲人成在线观看| 欧美www在线| 久久免费99精品久久久久久| 国语自产偷拍精品视频偷| 久久黄色级2电影| 午夜久久电影网| 国产欧美在线观看一区| 久久精品成人一区二区三区蜜臀 | 久久国产欧美精品| 国产午夜亚洲精品理论片色戒 | 日韩一级黄色大片| 国产精品99一区二区| 亚洲午夜国产成人av电影男同| 99在线精品视频在线观看| 欧美日韩一区二区三区在线看| 国产精品99久久久久久久女警| 亚洲美女av网站| 国产精品毛片a∨一区二区三区|国| 亚洲影院污污.| 亚洲欧美视频| 在线播放国产一区中文字幕剧情欧美 | 国产精品久久91| 欧美一级片久久久久久久| 欧美一区二区在线免费观看| 国产一区二区三区观看| 欧美大片一区二区| 国产精品成人一区二区| 久久久久国产精品厨房| 毛片基地黄久久久久久天堂| 99亚洲视频| 午夜亚洲一区| 亚洲精品中文字| 亚洲免费在线精品一区| 一区精品在线播放| 日韩午夜黄色| 黄色成人小视频| 99视频国产精品免费观看| 国内精品久久久久久久影视蜜臀| 亚洲国产精品专区久久| 国产欧美日韩亚洲精品| 欧美jizz19性欧美| 欧美系列精品| 欧美高清在线精品一区| 国产女人精品视频| 韩国女主播一区二区三区| 亚洲国产日韩在线| 日韩视频一区二区在线观看 | 伊人久久综合| 一本到12不卡视频在线dvd| 韩国av一区二区三区在线观看| 亚洲精品四区| 亚洲国产另类 国产精品国产免费| 一本色道久久88综合亚洲精品ⅰ | 亚洲国产成人不卡| 亚洲欧美春色| 9l国产精品久久久久麻豆| 久久久欧美精品| 久久精品国产精品| 国产精品videossex久久发布| 欧美.www| 尤妮丝一区二区裸体视频| 亚洲一区在线观看视频 | 老司机午夜精品视频在线观看| 国产精品劲爆视频| 亚洲精品国产精品国产自| 欲色影视综合吧| 久久久www| 久久人人爽人人爽| 国产亚洲福利社区一区| 亚洲一区二区在线免费观看| 一区二区免费看| 欧美经典一区二区三区| 欧美激情精品久久久久久变态| 激情视频一区二区| 欧美在线视频免费| 久久黄色影院| 国产日本亚洲高清| 欧美亚洲在线播放| 久久免费视频网站| 红桃视频亚洲| 男人插女人欧美| 亚洲黄色天堂| 亚洲视频综合在线| 国产精品播放| 亚洲欧美文学| 久久乐国产精品| 在线观看成人小视频| 久久久久一区二区| 欧美黄色精品| 一本大道久久a久久精品综合 | 激情欧美日韩| 蜜桃视频一区| 最新国产乱人伦偷精品免费网站 | 欧美午夜在线一二页| 亚洲五月六月| 久久精品亚洲一区二区| 一区二区在线视频| 欧美大尺度在线观看| 日韩一级大片在线| 久久激情视频久久| 亚洲国产毛片完整版| 欧美欧美全黄| 亚洲欧美制服中文字幕| 女人香蕉久久**毛片精品| 国产精品v欧美精品∨日韩| 国产精品久久久一区麻豆最新章节 | 亚洲人成毛片在线播放| 欧美国产亚洲视频| 99这里只有久久精品视频| 性色av一区二区三区在线观看| 国外视频精品毛片| 欧美黑人一区二区三区| 亚洲综合日韩| 欧美成人精品h版在线观看| 正在播放亚洲一区| 精品999在线观看| 欧美色区777第一页| 欧美在线视频免费播放| 91久久精品美女高潮| 欧美一区二区三区视频免费播放| 亚洲国产成人精品久久| 国产精品一区二区a| 欧美国产日韩精品| 欧美在线看片| 亚洲作爱视频| 欧美顶级艳妇交换群宴| 久久大逼视频| 在线视频中文亚洲| 黄色精品网站| 国产精品日日摸夜夜摸av| 久久人人超碰| 亚洲男人的天堂在线观看| 亚洲黄色在线观看| 久久综合九九| 欧美一区二区三区电影在线观看| 亚洲免费久久| 亚洲电影第1页| 国产一区二区三区在线播放免费观看| 欧美精品大片| 欧美成人综合| 美女黄网久久| 久久亚洲综合| 久久久精品国产99久久精品芒果| 亚洲一区不卡| 正在播放亚洲一区| 亚洲精品日韩在线|