專利名稱:解釋內(nèi)存數(shù)據(jù)塊的數(shù)據(jù)結(jié)構(gòu)的方法
技術(shù)領(lǐng)域:
本發(fā)明屬于通訊技術(shù)類,尤其涉及通訊技術(shù)分布式系統(tǒng)中對網(wǎng)絡(luò)字節(jié)流數(shù)據(jù)進行解釋的方法。
背景技術(shù):
目前,通訊技術(shù)中的數(shù)據(jù)結(jié)構(gòu)定義是在程序的源代碼中定義的,這是獲取結(jié)構(gòu)信息的最終來源。通常,在程序編譯的時候,會生成一個非常復(fù)雜而龐大的符號表,它包含了所有變量、類型和函數(shù)的定義及作用域的信息。程序在調(diào)試時將會根據(jù)變量名(符號),到符號表中查找它的類型結(jié)構(gòu)和內(nèi)存地址,從而解釋該變量在內(nèi)存中的二進制數(shù)據(jù)。其實,一方面只有將程序編譯成debug版本(即將符號表編入執(zhí)行文件),才能通過變量名找到變量的內(nèi)存地址和類型結(jié)構(gòu),而這樣的話執(zhí)行文件必會龐大,而且效率也會很低。另一方面,就算把程序編譯成Release版本(不含符號表),而通過其他手段(如按中國專利CN1286431A中所述的方法脫離調(diào)試環(huán)境,直接讀取符號表信息),以獲取某變量的內(nèi)存地址和類型結(jié)構(gòu),從而獲取內(nèi)存數(shù)據(jù)并進行解釋。這樣雖然可以通過變量名進行調(diào)試,但是在不知道變量名的情況下,想解釋一塊內(nèi)存數(shù)據(jù)、符號表及其依賴符號表的相關(guān)技術(shù)就顯得無能為力了。還有一方面,符號表對內(nèi)存數(shù)據(jù)的解釋是針對各個成員變量的,雖然它能給出每個成員變量的值,但對那些數(shù)據(jù)結(jié)構(gòu)很復(fù)雜的大結(jié)構(gòu),成員變量非常多,符號表不能給出每個成員變量的實際意義,所以查看起來還是很費力,這對解釋內(nèi)存數(shù)據(jù)是不盡人意的。
在通訊技術(shù)領(lǐng)域,或者與其他數(shù)據(jù)處理密切相關(guān)的領(lǐng)域中所涉及的程序,通常會分配一些很大的數(shù)據(jù)區(qū),這些數(shù)據(jù)區(qū)也許并沒有變量名和它對應(yīng),所以通常的調(diào)試器不能對這些數(shù)據(jù)區(qū)進行解釋。但這些數(shù)據(jù)區(qū)的內(nèi)容卻是非常重要的,一旦有故障發(fā)生,開發(fā)人員最關(guān)心,同時能最快定位錯誤的切入點就是這些數(shù)據(jù)區(qū)。所以,如果不能對這些龐大的數(shù)據(jù)區(qū)作出很有效的解釋,將會給故障定位和解決帶來巨大的困難。
發(fā)明內(nèi)容
本發(fā)明的目的是提供一種解釋內(nèi)存數(shù)據(jù)塊的數(shù)據(jù)結(jié)構(gòu)的方法,解決上述難題,以滿足對內(nèi)存數(shù)據(jù)進行解釋,及時確定故障定位,快速解決通訊故障等多方面的需要。
本發(fā)明的目的是這樣實現(xiàn)的一種解釋內(nèi)存數(shù)據(jù)塊的數(shù)據(jù)結(jié)構(gòu)的方法,包括下列步驟1,由結(jié)構(gòu)分析器分析源文件,生成類型結(jié)構(gòu)的描述文件,并存放在解釋內(nèi)存數(shù)據(jù)進程所在的主機上。
2,獲取內(nèi)存數(shù)據(jù)塊的字節(jié)流數(shù)據(jù),選擇與該內(nèi)存數(shù)據(jù)塊對應(yīng)的結(jié)構(gòu)描述文件,并生成一個結(jié)構(gòu)樹,和顯示該內(nèi)存數(shù)據(jù)塊每個字節(jié)的意義,從而解釋該內(nèi)存數(shù)據(jù)塊。
由于本發(fā)明采用了以上的技術(shù)方案,因而具有以下的優(yōu)點1,直接從源文件獲取信息,所以開發(fā)人員修改結(jié)構(gòu)定義或版本變化都不會影響這種解釋的正確性。
2,由開發(fā)人員在定義結(jié)構(gòu)時對每個成員變量的作用進行注釋,這些注釋信息作為成員變量屬性表的一部分,也將寫入結(jié)構(gòu)描述文件,從而在結(jié)構(gòu)樹中同樣可以得到,這樣就更加有利于維護人員對各個變量的理解,這是現(xiàn)有技術(shù)中的符號表不能實現(xiàn)的。
3,由于結(jié)構(gòu)描述文件放棄了源文件中許多與結(jié)構(gòu)無關(guān)的信息,使得它非常的小巧。而且,由于結(jié)構(gòu)描述文件采用的數(shù)據(jù)存儲方式本身就是樹型結(jié)構(gòu),而不同于符號表的表結(jié)構(gòu),這樣就省去了查找哈希表所花的時間,大大提高了生成結(jié)構(gòu)樹的效率。
4,本發(fā)明能夠?qū)?nèi)存數(shù)據(jù)進行解釋,而不需要關(guān)心該內(nèi)存數(shù)據(jù)有沒有變量名和它對應(yīng),也不需要該程序是Release版本,完全擺脫符號表的限制,只需要知道該內(nèi)存數(shù)據(jù)是與哪種結(jié)構(gòu)對應(yīng)即可。而且在得到每個成員變量的值時還可以得到它的實際意義。所以,本發(fā)明的方法是對現(xiàn)有符號表技術(shù)及其調(diào)試技術(shù)的一個重要改進和提高。
圖1是本發(fā)明的一種從描述文件還原結(jié)構(gòu)樹的流程示意圖;圖2是本發(fā)明的一種從源文件獲取結(jié)構(gòu)信息的流程示意圖。
具體實施例方式
以下結(jié)合附圖對本發(fā)明的實施作如下詳述在圖1中,本發(fā)明由結(jié)構(gòu)分析器分析源文件,生成類型結(jié)構(gòu)的描述文件,并存放在解釋內(nèi)存數(shù)據(jù)進程所在的主機上。其內(nèi)存數(shù)據(jù)塊可以是本機的內(nèi)存數(shù)據(jù)塊;也可以是其他主機上的內(nèi)存數(shù)據(jù)塊。其他主機上的內(nèi)存數(shù)據(jù)塊是在所在的主機上設(shè)置一個代理模塊,負責將那臺主機中的內(nèi)存數(shù)據(jù)發(fā)送給本機。
此步驟又可以分成以下幾個步驟來實現(xiàn)1,獲取與該結(jié)構(gòu)相關(guān)的所有頭文件。
2,讀取這些頭文件,將其中定義的所有結(jié)構(gòu)(只分析結(jié)構(gòu)類型及其成員變量,如C語言中的Struct類型,C++和Java中的Class類型)放進一個列表中,每個結(jié)構(gòu)又對應(yīng)了一張它的成員變量的列表,這樣就擁有了所有結(jié)構(gòu)的信息。
3,以所需創(chuàng)建的結(jié)構(gòu)為起點,分析它的每個成員變量,生成成員變量的屬性表(包括離結(jié)構(gòu)起點的偏移量,類型,長度,作用(變量的注釋),名稱,維數(shù)等),如果成員變量也是結(jié)構(gòu)類型,則繼續(xù)分析它所對應(yīng)的結(jié)構(gòu)。在每分析一個成員變量時,都將它的屬性表寫入結(jié)構(gòu)描述文件中,直到所需創(chuàng)建的那個結(jié)構(gòu)分析結(jié)束。
其具體執(zhí)行程序的步驟如下1,打開描述文件讀取文件頭中的結(jié)構(gòu)描述注釋,步驟101,并以此作為節(jié)點名創(chuàng)建根節(jié)點,步驟102,同時將當前節(jié)點壓入堆棧作為下面節(jié)點的父節(jié)點,步驟103,并對文件中讀取下一個變量屬性表作判斷,步驟104;2,對文件中讀取下一個變量屬性表的結(jié)果作判斷,是否讀取失敗,步驟105若失敗,則退出,步驟106,若成功,則按照變量屬性表的內(nèi)容在上一層節(jié)點中添加葉子節(jié)點,和將該節(jié)點和變量屬性表綁定,步驟107,并作結(jié)構(gòu)的判斷,步驟108;3,對結(jié)構(gòu)作判斷,若是,則將當前節(jié)點壓入堆棧作為下面節(jié)點的父節(jié)點的程序,步驟109,若否,則對是否結(jié)構(gòu)的最后一個變量作判斷,步驟110;4,對是否結(jié)構(gòu)的最后一個變量作判斷,若否,則返回對文件中讀取下一個變量屬性表作判斷的程序步驟,即返回步驟104若是,則將當前父節(jié)點退出堆棧,步驟111,并返回對文件中讀取下一個變量屬性表作判斷的程序,即返回步驟104。
在圖2中,本發(fā)明從源文件獲取結(jié)構(gòu)信息,從而解釋該內(nèi)存數(shù)據(jù)塊,其具體執(zhí)行程序的步驟如下1,如果有新行則處理新行,如果沒有則讀取下一行,步驟201;并對文件是否結(jié)束作判斷,步驟202;2,對文件是否結(jié)束作判斷,若是,則退出,步驟213,若否,則裁剪注釋部分(保留變量定義后的注釋)重新組成一行,步驟203,并作是否常量定義的判斷,步驟204;
3,對是否常量定義作判斷,若是,則將常量和它的值加入哈希表,步驟205,返回如果有新行則處理新行,如果若否,則作是否結(jié)構(gòu)定義開始的判斷,步驟206;4,對是否結(jié)構(gòu)定義開始作判斷,若是,則壓入堆棧,生成lstVar作為當前變量列表,和將lstVar及結(jié)構(gòu)名壓入哈希表hsStruct備查,步驟207,并返回如果有新行則處理新行,如果沒有則讀取下一行的程序步驟,步驟201;若否,則對是否結(jié)構(gòu)定義結(jié)束作判斷,步驟208;5)對是否結(jié)構(gòu)定義結(jié)束作判斷,若是,則彈出堆棧,步驟211,將變量和結(jié)構(gòu)名稱加入當前結(jié)構(gòu)對應(yīng)的變量列表,步驟212,并返回如果有新行則處理新行,如果沒有則讀取下一行的程序步驟,步驟201,若否,則對是否變量定義作判斷,步驟209;6)對是否變量定義作判斷,若否,則返回如果有新行則處理新行,如果沒有則讀取下一行的程序步驟,步驟201,若是,則將變量名稱和變量屬性表加入當前哈希表,步驟210,并返回如果有新行則處理新行,如果沒有則讀取下一行的程序步驟,步驟201。
由上所述,本發(fā)明在生成結(jié)構(gòu)描述文件時,應(yīng)首先設(shè)置一個字節(jié)計數(shù)器,以內(nèi)存數(shù)據(jù)塊對應(yīng)的結(jié)構(gòu)為起點,每處理一個成員變量,字節(jié)計數(shù)器就增加相應(yīng)的長度,這樣,每個成員變量在內(nèi)存數(shù)據(jù)塊中的位置就是當前的字節(jié)計數(shù)器的值,把它寫入變量的屬性表中,同時通過分析變量名的特征來確定該變量是否是位變量,或者是數(shù)組,然后把這些屬性也添加到屬性表中,寫入結(jié)構(gòu)描述文件。總之,變量的屬性越多,在結(jié)構(gòu)描述文件中得到的信息也就越多。
接著,本發(fā)明從源文件獲取結(jié)構(gòu)信息,即結(jié)構(gòu)分析器,由它分析源文件中的語法現(xiàn)象。這些語法包括包含注釋,編譯預(yù)處理,常量定義,變量定義,類型定義(結(jié)構(gòu)定義),函數(shù)定義。在變量定義中又有位定義,數(shù)組定義,指針定義等特殊語法,這些語法都將得到支持。
當讀入一行后,先去除注釋部分(保留變量定義后的注釋),對新的一行進行結(jié)構(gòu)分析,判斷該行是否是常量定義,或是類型定義(結(jié)構(gòu)定義),或是變量定義。如果不是則跳過;如果是常量定義,則將該常量和它代表的值加入哈希表中備查。如果是類型定義(結(jié)構(gòu)定義)開始則壓入堆棧,同時生成一個列表lstVar作為當前變量列表,并將lstVar和結(jié)構(gòu)名(如果沒有結(jié)構(gòu)名稱則自動生成一個)壓入哈希表hsStruct備查。如果是類型定義(結(jié)構(gòu)定義)結(jié)束,則彈出堆棧,判斷如果用該結(jié)構(gòu)定義了變量,則將該變量和結(jié)構(gòu)名稱加入當前結(jié)構(gòu)對應(yīng)的變量列表。如果是變量定義,則將當前變量屬性表加入當前變量列表lstVar,(變量屬性表包括類型,長度,名稱和注釋)。這樣,搜索完所有的源文件后,將得到一個含有所有結(jié)構(gòu)的哈希表(Key=結(jié)構(gòu)名稱,Value=含有所有成員變量的列表lstVar),通過結(jié)構(gòu)名稱就可以得到該結(jié)構(gòu)的所有成員變量的信息。
由此,本發(fā)明中設(shè)置的結(jié)構(gòu)樹是根據(jù)結(jié)構(gòu)描述文件生成的,它的每個葉子節(jié)點都代表結(jié)構(gòu)的一個成員變量。如果結(jié)構(gòu)中有嵌套,該節(jié)點將由許多子節(jié)點構(gòu)成,每個子節(jié)點代表子結(jié)構(gòu)的各個成員變量,依此類推。每一個節(jié)點或字節(jié)點都綁定了它所對應(yīng)的成員變量的屬性表。在生成樹的過程和對內(nèi)存數(shù)據(jù)塊的各個字節(jié)進行解釋的過程是同步完成的,即每生成一個節(jié)點,該節(jié)點對應(yīng)的內(nèi)存數(shù)據(jù)就被讀取并解釋了。一旦結(jié)構(gòu)樹生成后,就可以從字節(jié)流中的字節(jié)定位結(jié)構(gòu)的成員變量,也可以從結(jié)構(gòu)的成員變量定位字節(jié)流的某段字節(jié),以滿足對內(nèi)存數(shù)據(jù)進行解釋,及時確定故障定位,快速解決通訊故障等方面的需要。
本發(fā)明經(jīng)在中興通訊ZXSS10后臺數(shù)據(jù)維護終端的探針模塊樣機上試驗,取得極好的效果。其探針模塊向前臺發(fā)送消息,請求獲取前臺內(nèi)存中的數(shù)據(jù)。在前臺,有個探針代理模塊,響應(yīng)探針模塊的請求,把前臺內(nèi)存中的數(shù)據(jù)傳送到后臺。當探針獲取前臺內(nèi)存中的原始字節(jié)流數(shù)據(jù)后,利用已經(jīng)生成的和前臺的數(shù)據(jù)結(jié)構(gòu)對應(yīng)的結(jié)構(gòu)描述文件來生成結(jié)構(gòu)樹,使字節(jié)流中的每個字節(jié)都能在結(jié)構(gòu)樹中找到相應(yīng)的變量與之對應(yīng)。反之亦然,隨意選取結(jié)構(gòu)樹的某個節(jié)點(即某個成員變量),都能在字節(jié)流中進行定位,并能得到更詳盡的與該變量相關(guān)的信息(屬性)。例如,呼叫數(shù)據(jù)區(qū)里保存了與一次通話相關(guān)的所有信息,它其實是一個很大的數(shù)據(jù)結(jié)構(gòu),通過這種方法可以知道呼叫數(shù)據(jù)區(qū)(該結(jié)構(gòu))的所有成員變量的值及其功能。如果通話出現(xiàn)了問題,查看該結(jié)構(gòu)就可以幫助開發(fā)人員或維護人員迅速定位錯誤。這樣,可以使調(diào)試或維護所花的時間大大縮短,提高了工作效率。
本發(fā)明應(yīng)用于電信設(shè)備中的優(yōu)點是很明顯的。但是,它的應(yīng)用遠不限于此。任何一個應(yīng)用,只要能獲取內(nèi)存數(shù)據(jù)塊,并且知道它的結(jié)構(gòu),就可以利用本發(fā)明來對內(nèi)存數(shù)據(jù)塊進行解釋,其都在本發(fā)明所要求保護的范圍內(nèi)。
權(quán)利要求
1.一種解釋內(nèi)存數(shù)據(jù)塊的數(shù)據(jù)結(jié)構(gòu)的方法,包括下列步驟1)由結(jié)構(gòu)分析器分析源文件,生成結(jié)構(gòu)類型的描述文件,并存放在解釋內(nèi)存數(shù)據(jù)進程所在的主機上。2)獲取內(nèi)存數(shù)據(jù)塊的字節(jié)流數(shù)據(jù),選擇與該內(nèi)存數(shù)據(jù)塊對應(yīng)的結(jié)構(gòu)描述文件,并生成一個結(jié)構(gòu)樹,和顯示該內(nèi)存數(shù)據(jù)塊每個字節(jié)的意義,從而解釋該內(nèi)存數(shù)據(jù)塊。
2.根據(jù)權(quán)利要求1所述的一種解釋內(nèi)存數(shù)據(jù)塊的數(shù)據(jù)結(jié)構(gòu)的方法,其特征在于,第1)步驟中還包括下列步驟1)獲取與該結(jié)構(gòu)相關(guān)的所有頭文件;2)讀取這些頭文件,將其中定義的所有結(jié)構(gòu)放進一個列表中,并將每個結(jié)構(gòu)對應(yīng)一張它的成員變量的列表;3)以所需創(chuàng)建的結(jié)構(gòu)為起點,分析它的每個成員變量,并生成成員變量的屬性表;如果成員變量是結(jié)構(gòu)類型,則繼續(xù)分析它所對應(yīng)的結(jié)構(gòu);4)在每分析一個成員變量時,都將它的屬性表寫入結(jié)構(gòu)描述文件中,直到所需創(chuàng)建的那個結(jié)構(gòu)分析結(jié)束。
3.根據(jù)權(quán)利要求1所述的一種解釋內(nèi)存數(shù)據(jù)塊的數(shù)據(jù)結(jié)構(gòu)的方法,其特征在于,第1)步驟中所述的內(nèi)存數(shù)據(jù)塊是本機的內(nèi)存數(shù)據(jù)塊。
4.根據(jù)權(quán)利要求1所述的一種解釋內(nèi)存數(shù)據(jù)塊的數(shù)據(jù)結(jié)構(gòu)的方法,其特征在于,第1)步驟中所述的內(nèi)存數(shù)據(jù)塊可以是其他主機上的內(nèi)存數(shù)據(jù)塊,其他主機上的內(nèi)存數(shù)據(jù)塊通過所在的主機上設(shè)置的代理模塊,發(fā)送給本機。
5.根據(jù)權(quán)利要求5所述的一種解釋內(nèi)存數(shù)據(jù)塊的數(shù)據(jù)結(jié)構(gòu)的方法,其特征在于,所述的結(jié)構(gòu)類型,為C語言中的Struct類型,或C++、Java中的Class類型。
6.根據(jù)權(quán)利要求1所述的一種解釋內(nèi)存數(shù)據(jù)塊的數(shù)據(jù)結(jié)構(gòu)的方法,其特征在于,第2)步驟中所述的結(jié)構(gòu)樹由結(jié)構(gòu)描述文件生成的,它的每個葉子節(jié)點都代表結(jié)構(gòu)的一個成員變量;如果結(jié)構(gòu)中有嵌套,該節(jié)點將由許多子節(jié)點構(gòu)成,每個子節(jié)點代表子結(jié)構(gòu)的各個成員變量;每一個節(jié)點或字節(jié)點設(shè)定有它所對應(yīng)的成員變量的屬性表。
7.根據(jù)權(quán)利要求1或2、5、8所述的一種解釋內(nèi)存數(shù)據(jù)塊的數(shù)據(jù)結(jié)構(gòu)的方法,其特征在于,所述的結(jié)構(gòu)樹生成過程,和對內(nèi)存數(shù)據(jù)塊的各個字節(jié)進行解釋的過程是同步完成的,當每生成一個節(jié)點,該節(jié)點對應(yīng)的內(nèi)存數(shù)據(jù)就被讀取并解釋;當結(jié)構(gòu)樹生成后,就可從字節(jié)流中的字節(jié)定位結(jié)構(gòu)成員變量,或從結(jié)構(gòu)的成員變量定位字節(jié)流的某段字節(jié)。
8.根據(jù)權(quán)利要求1所述的一種解釋內(nèi)存數(shù)據(jù)塊的數(shù)據(jù)結(jié)構(gòu)的方法,其特征在于,第1)步驟的具體執(zhí)行程序如下1)打開描述文件讀取文件頭中的結(jié)構(gòu)描述注釋,并以此作為節(jié)點名創(chuàng)建根節(jié)點,并將當前節(jié)點壓入堆棧作為下面節(jié)點的父節(jié)點,并對文件中讀取下一個變量屬性表作判斷;2)對文件中讀取下一個變量屬性表的結(jié)果作判斷,若失敗,則退出,若成功,則按照變量屬性表的內(nèi)容在上一層節(jié)點中添加葉子節(jié)點,和將該節(jié)點和變量屬性表綁定,并作結(jié)構(gòu)的判斷;3)對結(jié)構(gòu)作判斷,若是,則返回將當前節(jié)點壓入堆棧作為下面節(jié)點的父節(jié)點的程序,若否,則對是否結(jié)構(gòu)的最后一個變量作判斷;4)對是否結(jié)構(gòu)的最后一個變量作判斷,若否,則返回對文件中讀取下一個變量屬性表作判斷的程序,若是,則將當前父節(jié)點退出堆棧,并返回對文件中讀取下一個變量屬性表作判斷的程序。
9.根據(jù)權(quán)利要求1所述的一種解釋內(nèi)存數(shù)據(jù)塊的數(shù)據(jù)結(jié)構(gòu)的方法,其特征在于,第2)步驟的具體執(zhí)行程序如下1)如果有新行則處理新行,如果沒有則讀取下一行,并對文件是否結(jié)束作判斷;2)對文件是否結(jié)束作判斷,若是,則退出,若否,則裁剪注釋部分(保留變量定義后的注釋)重新組成一行,并作是否常量定義的判斷;3)對是否常量定義作判斷,若是,則將常量和它的值加入哈希表,返回如果有新行則處理新行,如果沒有則讀取下一行的程序,若否,則作是否結(jié)構(gòu)定義開始的判斷;4)對是否結(jié)構(gòu)定義開始作判斷,若是,則壓入堆棧,生成lstVar作為當前變量列表,和將lstVar及結(jié)構(gòu)名壓入哈希表hsStruct備查,并返回如果有新行則處理新行,如果沒有則讀取下一行的程序;若否,則對是否結(jié)構(gòu)定義結(jié)束作判斷;5)對是否結(jié)構(gòu)定義結(jié)束作判斷,若是,則彈出堆棧,將變量和結(jié)構(gòu)名稱加入當前結(jié)構(gòu)對應(yīng)的變量列表,并返回如果有新行則處理新行,如果沒有則讀取下一行的程序,若否,則對是否變量定義作判斷;6)對是否變量定義作判斷,若否,則返回如果有新行則處理新行,如果沒有則讀取下一行的程序,若是,則將變量名稱和變量屬性表加入當前哈希表,并返回如果有新行則處理新行,如果沒有則讀取下一行的程序。
全文摘要
本發(fā)明涉及一種解釋內(nèi)存數(shù)據(jù)塊的數(shù)據(jù)結(jié)構(gòu)的方法,以滿足對內(nèi)存數(shù)據(jù)進行解釋,及時確定故障定位,快速解決通訊故障等多方面的需要。它包括下列步驟1.由結(jié)構(gòu)分析器分析源文件,生成類型結(jié)構(gòu)的描述文件,并存放在解釋內(nèi)存數(shù)據(jù)進程所在的主機上。2.獲取內(nèi)存數(shù)據(jù)塊的字節(jié)流數(shù)據(jù),選擇與該內(nèi)存數(shù)據(jù)塊對應(yīng)的結(jié)構(gòu)描述文件,并生成一個結(jié)構(gòu)樹,和顯示該內(nèi)存數(shù)據(jù)塊每個字節(jié)的意義,從而解釋該內(nèi)存數(shù)據(jù)塊。本發(fā)明用于通訊技術(shù)領(lǐng)域。
文檔編號H04L12/26GK1501624SQ0214530
公開日2004年6月2日 申請日期2002年11月15日 優(yōu)先權(quán)日2002年11月15日
發(fā)明者徐曉光 申請人:深圳市中興通訊股份有限公司