• <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>
            posts - 200, comments - 8, trackbacks - 0, articles - 0

            轉(zhuǎn)自https://www.ibm.com/developerworks/cn/linux/l-ipc/part6/, 作者:鄭彥興

            一個套接口可以看作是進(jìn)程間通信的端點(diǎn)(endpoint),每個套接口的名字都是唯一的(唯一的含義是不言而喻的),其他進(jìn)程可以發(fā)現(xiàn)、連接并且 與之通信。通信域用來說明套接口通信的協(xié)議,不同的通信域有不同的通信協(xié)議以及套接口的地址結(jié)構(gòu)等等,因此,創(chuàng)建一個套接口時,要指明它的通信域。比較常 見的是unix域套接口(采用套接口機(jī)制實(shí)現(xiàn)單機(jī)內(nèi)的進(jìn)程間通信)及網(wǎng)際通信域。

            1、背景知識

            linux目前的網(wǎng)絡(luò)內(nèi)核代碼主要基于伯克利的BSD的unix實(shí)現(xiàn),整個結(jié)構(gòu)采用的是一種面向?qū)ο蟮姆謱訖C(jī)制。層與層之間有嚴(yán)格的接口定義。這里我們引用[1]中的一個圖表來描述linux支持的一些通信協(xié)議:


             

            我們這里只關(guān)心IPS,即因特網(wǎng)協(xié)議族,也就是通常所說的TCP/IP網(wǎng)絡(luò)。我們這里假設(shè)讀者具有網(wǎng)絡(luò)方面的一些背景知識,如了解網(wǎng)絡(luò)的分層結(jié)構(gòu),通常所說的7層結(jié)構(gòu);了解IP地址以及路由的一些基本知識。

            目前l(fā)inux網(wǎng)絡(luò)API是基于BSD套接口的(系統(tǒng)V提供基于流I/O子系統(tǒng)的用戶接口,但是linux內(nèi)核目前不支持流I/O子系 統(tǒng))。套接口可以說是網(wǎng)絡(luò)編程中一個非常重要的概念,linux以文件的形式實(shí)現(xiàn)套接口,與套接口相應(yīng)的文件屬于sockfs特殊文件系統(tǒng),創(chuàng)建一個套接 口就是在sockfs中創(chuàng)建一個特殊文件,并建立起為實(shí)現(xiàn)套接口功能的相關(guān)數(shù)據(jù)結(jié)構(gòu)。換句話說,對每一個新創(chuàng)建的BSD套接口,linux內(nèi)核都將在 sockfs特殊文件系統(tǒng)中創(chuàng)建一個新的inode。描述套接口的數(shù)據(jù)結(jié)構(gòu)是socket,將在后面給出。

            2、重要數(shù)據(jù)結(jié)構(gòu)

            下面是在網(wǎng)絡(luò)編程中比較重要的幾個數(shù)據(jù)結(jié)構(gòu),讀者可以在后面介紹編程API部分再回過頭來了解它們。

            (1)表示套接口的數(shù)據(jù)結(jié)構(gòu)struct socket

            套接口是由socket數(shù)據(jù)結(jié)構(gòu)代表的,形式如下: 


            1. struct socket
            2. {
            3. socket_state state; /* 指明套接口的連接狀態(tài),一個套接口的連接狀態(tài)可以有以下幾種
            4. 套接口是空閑的,還沒有進(jìn)行相應(yīng)的端口及地址的綁定;還沒有連接;正在連接中;已經(jīng)連接;正在解除連接。 */
            5.   unsigned long flags;
            6.   struct proto_ops ops; /* 指明可對套接口進(jìn)行的各種操作 */
            7.   struct inode inode; /* 指向sockfs文件系統(tǒng)中的相應(yīng)inode */
            8.   struct fasync_struct *fasync_list; /* Asynchronous wake up list */
            9.   struct file *file; /* 指向sockfs文件系統(tǒng)中的相應(yīng)文件 */
            10. struct sock sk; /* 任何協(xié)議族都有其特定的套接口特性,該域就指向特定協(xié)議族的套接口對
            11. 象。 */
            12.   wait_queue_head_t wait;
            13.   short type;
            14.   unsigned char passcred;
            15. };

            (2)描述套接口通用地址的數(shù)據(jù)結(jié)構(gòu)struct sockaddr

            由于歷史的緣故,在bind、connect等系統(tǒng)調(diào)用中,特定于協(xié)議的套接口地址結(jié)構(gòu)指針都要強(qiáng)制轉(zhuǎn)換成該通用的套接口地址結(jié)構(gòu)指針。結(jié)構(gòu)形式如下: 


            1. struct sockaddr {
            2.     sa_family_t    sa_family;    /* address family, AF_xxx    */
            3.     char        sa_data[14];    /* 14 bytes of protocol address    */
            4. };

            (3)描述因特網(wǎng)地址結(jié)構(gòu)的數(shù)據(jù)結(jié)構(gòu)struct sockaddr_in(這里局限于IP4):


            1. struct sockaddr_in
            2.   {
            3.     __SOCKADDR_COMMON (sin_);    /* 描述協(xié)議族 */
            4.     in_port_t sin_port;            /* 端口號 */
            5.     struct in_addr sin_addr;        /* 因特網(wǎng)地址 */
            6.     /* Pad to size of `struct sockaddr'. */
            7.     unsigned char sin_zero[sizeof (struct sockaddr) -
            8.              __SOCKADDR_COMMON_SIZE -
            9.              sizeof (in_port_t) -
            10.              sizeof (struct in_addr)];
            11.   };

            一般來說,讀者最關(guān)心的是前三個域,即通信協(xié)議、端口號及地址。

            3、套接口編程的幾個重要步驟:

            (1)創(chuàng)建套接口,由系統(tǒng)調(diào)用socket實(shí)現(xiàn):

            int socket( int domain, int type, int ptotocol);

            參數(shù)domain指明通信域,如PF_UNIX(unix域),PF_INET(IPv4),PF_INET6(IPv6) 等;type指明通信類型,如SOCK_STREAM(面向連接方式)、SOCK_DGRAM(非面向連接方式)等。一般來說,參數(shù)protocol可設(shè) 置為0,除非用在原始套接口上(原始套接口有一些特殊功能,后面還將介紹)。

            注:socket()系統(tǒng)調(diào)用為套接口在sockfs文件系統(tǒng)中分配一個新的文件和dentry對 象,并通過文件描述符把它們與調(diào)用進(jìn)程聯(lián)系起來。進(jìn)程可以像訪問一個已經(jīng)打開的文件一樣訪問套接口在sockfs中的對應(yīng)文件。但進(jìn)程絕不能調(diào)用 open()來訪問該文件(sockfs文件系統(tǒng)沒有可視安裝點(diǎn),其中的文件永遠(yuǎn)不會出現(xiàn)在系統(tǒng)目錄樹上),當(dāng)套接口被關(guān)閉時,內(nèi)核會自動刪除 sockfs中的inodes。

            (2)綁定地址

            根據(jù)傳輸層協(xié)議(TCP、UDP)的不同,客戶機(jī)及服務(wù)器的處理方式也有很大不同。但是,不管通信雙方使用何種傳輸協(xié)議,都需要一種標(biāo)識自己的機(jī)制。

            通信雙方一般由兩個方面標(biāo)識:地址和端口號(通常,一個IP地址和一個端口號常常被稱為一個套接口)。根據(jù)地址可以尋址到主機(jī),根據(jù)端口號則可以尋址到主機(jī)提供特定服務(wù)的進(jìn)程,實(shí)際上,一個特定的端口號代表了一個提供特定服務(wù)的進(jìn)程。

            對于使用TCP傳輸協(xié)議通信方式來說,通信雙方需要給自己綁定一個唯一標(biāo)識自己的套接口,以便建立連接;對于使用UDP傳輸協(xié)議,只需 要服務(wù)器綁定一個標(biāo)識自己的套接口就可以了,用戶則不需要綁定(在需要時,如調(diào)用connect時[注1],內(nèi)核會自動分配一個本地地址和本地端口號)。 綁定操作由系統(tǒng)調(diào)用bind()完成:

            int bind( int sockfd, const struct sockaddr * my_addr, socklen_t my_addr_len)

            第二個參數(shù)對于Ipv4來說,實(shí)際上需要填充的結(jié)構(gòu)是struct sockaddr_in,前面已經(jīng)介紹了該結(jié)構(gòu)。這里只想強(qiáng)調(diào)該結(jié)構(gòu)的第一個域,它表明該套接口使用的通信協(xié)議,如AF_INET。聯(lián)系socket系統(tǒng) 調(diào)用的第一個參數(shù),讀者可能會想到PF_INET與AF_INET究竟有什么不同?實(shí)際上,原來的想法是每個通信域(如PF_INET)可能對應(yīng)多個協(xié)議 (如AF_INET),而事實(shí)上支持多個協(xié)議的通信域一直沒有實(shí)現(xiàn)。因此,在linux內(nèi)核中,AF_***與PF_***被定義為同一個常數(shù),因此,在 編程時可以不加區(qū)分地使用他們。

            注1:在采用非面向連接通信方式時,也會用到connect()調(diào)用,不過與在面向連接中的 connect()調(diào)用有本質(zhì)的區(qū)別:在非面向連接通信中,connect調(diào)用只是先設(shè)置一下對方的地址,內(nèi)核為本地套接口記下對方的地址,然后采用 send()來發(fā)送數(shù)據(jù),這樣避免每次發(fā)送時都要提供相同的目的地址。其中的connect()調(diào)用不涉及握手過程;而在面向連接的通信方式 中,connect()要完成一個嚴(yán)格的握手過程。

            (3)請求建立連接(由TCP客戶發(fā)起)

            對于采用面向連接的傳輸協(xié)議TCP實(shí)現(xiàn)通信來說,一個比較重要的步驟就是通信雙方建立連接(如果采用udp傳輸協(xié)議則不需要),由系統(tǒng)調(diào)用connect()完成:

            int connect( int sockfd, const struct sockaddr * servaddr, socklen_t addrlen)

            第一個參數(shù)為本地調(diào)用socket后返回的描述符,第二個參數(shù)為服務(wù)器的地址結(jié)構(gòu)指針。connect()向指定的套接口請求建立連接。

            注:與connect()相對應(yīng),在服務(wù)器端,通過系統(tǒng)調(diào)用listen(),指定服務(wù)器端的套接口為監(jiān)聽套接口,監(jiān)聽每一個向服務(wù)器套接口發(fā)出的連接請求,并通過握手機(jī)制建立連接。內(nèi)核為listen()維護(hù)兩個隊列:已完成連接隊列和未完成連接隊列。

            (4)接受連接請求(由TCP服務(wù)器端發(fā)起)

            服務(wù)器端通過監(jiān)聽套接口,為所有連接請求建立了兩個隊列:已完成連接隊列和未完成連接隊列(每個監(jiān)聽套接口都對應(yīng)這樣兩個隊列,當(dāng)然, 一般服務(wù)器只有一個監(jiān)聽套接口)。通過accept()調(diào)用,服務(wù)器將在監(jiān)聽套接口的已連接隊列頭中,返回用于代表當(dāng)前連接的套接口描述字。

            int accept( int sockfd, struct sockaddr * cliaddr, socklen_t * addrlen)

            第一個參數(shù)指明哪個監(jiān)聽套接口,一般是由listen()系統(tǒng)調(diào)用指定的(由于每個監(jiān)聽套接口都對應(yīng)已連接和未連接兩個隊列,因此它的 內(nèi)部機(jī)制實(shí)質(zhì)是通過sockfd指定在哪個已連接隊列頭中返回一個用于當(dāng)前客戶的連接,如果相應(yīng)的已連接隊列為空,accept進(jìn)入睡眠)。第二個參數(shù)指 明客戶的地址結(jié)構(gòu),如果對客戶的身份不感興趣,可指定其為空。

            注:對于采用TCP傳輸協(xié)議進(jìn)行通信的服務(wù)器和客戶機(jī)來說,一定要經(jīng)過客戶請求建立連接,服務(wù)器接受連接請求這一過程;而對采用UDP傳輸協(xié)議的通信雙方則不需要這一步驟。

            (5)通信

            客戶機(jī)可以通過套接口接收服務(wù)器傳過來的數(shù)據(jù),也可以通過套接口向服務(wù)器發(fā)送數(shù)據(jù)。前面所有的準(zhǔn)備工作(創(chuàng)建套接口、綁定等操作)都是為這一步驟準(zhǔn)備的。

            常用的從套接口中接收數(shù)據(jù)的調(diào)用有:recv、recvfrom、recvmsg等,常用的向套接口中發(fā)送數(shù)據(jù)的調(diào)用有send、sendto、sendmsg等。


            1. int recv(int s, void *
            2.         buf, size_t 
            3.         len, int 
            4.         flags)
            5. int recvfrom(int s, void *
            6.         buf, size_t 
            7.         len, int 
            8.         flags, struct sockaddr *
            9.         from, socklen_t *
            10.         fromlen)
            11. int recvmsg(int s, struct msghdr *
            12.         msg, int 
            13.         flags)
            14. int send(int s,const void *
            15.         msg, size_t 
            16.         len, int 
            17.         flags)
            18. int sendto(int s, const void *
            19.         msg, size_t 
            20.         len, int 
            21.         flags const struct sockaddr *
            22.         to, socklen_t 
            23.         tolen)
            24. int sendmsg(int s, const struct msghdr *
            25.         msg, int 
            26.         flags)

            這里不再對這些調(diào)用作具體的說明,只想強(qiáng)調(diào)一下,recvfrom()以及recvmsg()可用于面向連接的套接口,也可用于面向非 連接的套接口;而recv()一般用于面向連接的套接口。另外,在調(diào)用了connect()之后,就應(yīng)給調(diào)用send()而不是sendto()了,因?yàn)?調(diào)用了connect之后,目標(biāo)就已經(jīng)確定了。

            前面講到,socket()系統(tǒng)調(diào)用返回套接口描述字,實(shí)際上它是一個文件描述符。所以,可以對套接口進(jìn)行通常的讀寫操作,即使用 read()及write()方法。在實(shí)際應(yīng)用中,由于面向連接的通信(采用TCP傳輸協(xié)議)是可靠的,同時又保證字節(jié)流原有的順序,所以更適合用 read及write方法。而非面向連接的通信(采用UDP傳輸協(xié)議)是不可靠的,字節(jié)流也不一定保持原有的順序,所以一般不宜用read及write方 法。

            (6)通信的最后一步是關(guān)閉套接口

            由close()來完成此項(xiàng)功能,它唯一的參數(shù)是套接口描述字,不再贅述。

            4、典型調(diào)用代碼:

            到處可以發(fā)現(xiàn)基于套接口的客戶機(jī)及服務(wù)器程序,這里不再給出完整的范例代碼,只是給出它們的典型調(diào)用代碼,并給出簡要說明。

            (1)典型的TCP服務(wù)器代碼:


            1. ... ...
            2. int listen_fd, connect_fd;
            3. struct sockaddr_in serv_addr, client_addr;
            4. ... ...
            5. listen_fd = socket ( PF_INET, SOCK_STREAM, 0 );

            6. /* 創(chuàng)建網(wǎng)際Ipv4域的(由PF_INET指定)面向連接的(由SOCK_STREAM指定,
            7. 如果創(chuàng)建非面向連接的套接口則指定為SOCK_DGRAM)
            8. 的套接口。第三個參數(shù)0表示由內(nèi)核確定缺省的傳輸協(xié)議,
            9. 對于本例,由于創(chuàng)建的是可靠的面向連接的基于流的套接口,
            10. 內(nèi)核將選擇TCP作為本套接口的傳輸協(xié)議) */

            11. bzero( &serv_addr, sizeof(serv_addr) );
            12. serv_addr.sin_family = AF_INET ; /* 指明通信協(xié)議族 */
            13. serv_addr.sin_port = htons( 49152 ) ; /* 分配端口號 */
            14. inet_pton(AF_INET, " 192.168.0.11", &serv_addr.sin_sddr) ;
            15. /* 分配地址,把點(diǎn)分十進(jìn)制IPv4地址轉(zhuǎn)化為32位二進(jìn)制Ipv4地址。 */
            16. bind( listen_fd, (struct sockaddr*) serv_addr, sizeof ( struct sockaddr_in )) ; 
            17. /* 實(shí)現(xiàn)綁定操作 */
            18. listen( listen_fd, max_num) ; 
            19. /* 套接口進(jìn)入偵聽狀態(tài),max_num規(guī)定了內(nèi)核為此套接口排隊的最大連接個數(shù) */
            20. for( ; ; ) {
            21. ... ...
            22. connect_fd = accept( listen_fd, (struct sockaddr*)client_addr, &len ) ; /* 獲得連接fd. */
            23. ... ...                    /* 發(fā)送和接收數(shù)據(jù) */
            24. }

            注:端口號的分配是有一些慣例的,不同的端口號對應(yīng)不同的服務(wù)或進(jìn)程。比如一般都把端口號21分配給 FTP服務(wù)器的TCP/IP實(shí)現(xiàn)。端口號一般分為3段,0-1023(受限的眾所周知的端口,由分配數(shù)值的權(quán)威機(jī)構(gòu)IANA管 理),1024-49151(可以從IANA那里申請注冊的端口),49152-65535(臨時端口,這就是為什么代碼中的端口號為49152)。

            對于多字節(jié)整數(shù)在內(nèi)存中有兩種存儲方式:一種是低字節(jié)在前,高字節(jié)在后,這樣的存儲順序被稱為低端字節(jié)序(little- endian);高字節(jié)在前,低字節(jié)在后的存儲順序則被稱為高端字節(jié)序(big-endian)。網(wǎng)絡(luò)協(xié)議在處理多字節(jié)整數(shù)時,采用的是高端字節(jié)序,而不 同的主機(jī)可能采用不同的字節(jié)序。因此在編程時一定要考慮主機(jī)字節(jié)序與網(wǎng)絡(luò)字節(jié)序間的相互轉(zhuǎn)換。這就是程序中使用htons函數(shù)的原因,它返回網(wǎng)絡(luò)字節(jié)序的 整數(shù)。

            (2)典型的TCP客戶代碼:


            1. ... ...
            2. int socket_fd;
            3. struct sockaddr_in serv_addr ;
            4. ... ...
            5. socket_fd = socket ( PF_INET, SOCK_STREAM, 0 );
            6. bzero( &serv_addr, sizeof(serv_addr) );
            7. serv_addr.sin_family = AF_INET ; /* 指明通信協(xié)議族 */
            8. serv_addr.sin_port = htons( 49152 ) ; /* 分配端口號 */
            9. inet_pton(AF_INET, " 192.168.0.11", &serv_addr.sin_sddr) ;
            10. /* 分配地址,把點(diǎn)分十進(jìn)制IPv4地址轉(zhuǎn)化為32位二進(jìn)制Ipv4地址。 */
            11. connect( socket_fd, (struct sockaddr*)serv_addr, sizeof( serv_addr ) ) ; /* 向服務(wù)器發(fā)起連接請求 */
            12. ... ...                            /* 發(fā)送和接收數(shù)據(jù) */
            13. ... ...

            對比兩段代碼可以看出,許多調(diào)用是服務(wù)器或客戶機(jī)所特有的。另外,對于非面向連接的傳輸協(xié)議,代碼還有簡單些,沒有連接的發(fā)起請求和接收請求部分。

            5、網(wǎng)絡(luò)編程中的其他重要概念

            下面列出了網(wǎng)絡(luò)編程中的其他重要概念,基本上都是給出這些概念能夠?qū)崿F(xiàn)的功能,讀者在編程過程中如果需要這些功能,可查閱相關(guān)概念。

            (1)、I/O復(fù)用的概念

            I/O復(fù)用提供一種能力,這種能力使得當(dāng)一個I/O條件滿足時,進(jìn)程能夠及時得到這個信息。I/O復(fù)用一般應(yīng)用在進(jìn)程需要處理多個描述 字的場合。它的一個優(yōu)勢在于,進(jìn)程不是阻塞在真正的I/O調(diào)用上,而是阻塞在select()調(diào)用上,select()可以同時處理多個描述字,如果它所 處理的所有描述字的I/O都沒有處于準(zhǔn)備好的狀態(tài),那么將阻塞;如果有一個或多個描述字I/O處于準(zhǔn)備好狀態(tài),則select()不阻塞,同時會根據(jù)準(zhǔn)備 好的特定描述字采取相應(yīng)的I/O操作。

            (2)、Unix通信域

            前面主要介紹的是PF_INET通信域,實(shí)現(xiàn)網(wǎng)際間的進(jìn)程間通信。基于Unix通信域(調(diào)用socket時指定通信域?yàn)?PF_LOCAL即可)的套接口可以實(shí)現(xiàn)單機(jī)之間的進(jìn)程間通信。采用Unix通信域套接口有幾個好處:Unix通信域套接口通常是TCP套接口速度的兩 倍;另一個好處是,通過Unix通信域套接口可以實(shí)現(xiàn)在進(jìn)程間傳遞描述字。所有可用描述字描述的對象,如文件、管道、有名管道及套接口等,在我們以某種方 式得到該對象的描述字后,都可以通過基于Unix域的套接口來實(shí)現(xiàn)對描述字的傳遞。接收進(jìn)程收到的描述字值不一定與發(fā)送進(jìn)程傳遞的值一致(描述字是特定于 進(jìn)程的),但是特們指向內(nèi)核文件表中相同的項(xiàng)。

            (3)、原始套接口

            原始套接口提供一般套接口所不提供的功能: 

            • 原始套接口可以讀寫一些用于控制的控制協(xié)議分組,如ICMPv4等,進(jìn)而可實(shí)現(xiàn)一些特殊功能。
            • 原始套接口可以讀寫特殊的IPv4數(shù)據(jù)包。內(nèi)核一般只處理幾個特定協(xié)議字段的數(shù)據(jù)包,那么一些需要不同協(xié)議字段的數(shù)據(jù)包就需要通過原始套接口對其進(jìn)行讀寫;
            • 通過原始套接口可以構(gòu)造自己的Ipv4頭部,也是比較有意思的一點(diǎn)。

            創(chuàng)建原始套接口需要root權(quán)限。

            (4)、對數(shù)據(jù)鏈路層的訪問

            對數(shù)據(jù)鏈路層的訪問,使得用戶可以偵聽本地電纜上的所有分組,而不需要使用任何特殊的硬件設(shè)備,在linux下讀取數(shù)據(jù)鏈路層分組需要創(chuàng)建SOCK_PACKET類型的套接口,并需要有root權(quán)限。

            (5)、帶外數(shù)據(jù)(out-of-band data)

            如果有一些重要信息要立刻通過套接口發(fā)送(不經(jīng)過排隊),請查閱與帶外數(shù)據(jù)相關(guān)的文獻(xiàn)。

            (6)、多播

            linux內(nèi)核支持多播,但是在默認(rèn)狀態(tài)下,多數(shù)linux系統(tǒng)都關(guān)閉了對多播的支持。因此,為了實(shí)現(xiàn)多播,可能需要重新配置并編譯內(nèi)核。具體請參考[4]及[2]。

            結(jié)論:linux套接口編程的內(nèi)容可以說是極大豐富,同時它涉及到許多的網(wǎng)絡(luò)背景知識,有興趣的讀者可在[2]中找到比較系統(tǒng)而全面的介紹。

            至此,本專題系列(linux環(huán)境進(jìn)程間通信)全部結(jié)束了。實(shí)際上,進(jìn)程間通信的一般意義通常指的是消息隊列、信號燈和共享內(nèi)存,可以是posix的,也可以是SYS v的。本系列同時介紹了管道、有名管道、信號以及套接口等,是更為一般意義上的進(jìn)程間通信機(jī)制。


            參考資料

            • Understanding the Linux Kernel, 2nd Edition, By Daniel P. Bovet, Marco Cesati , 對各主題闡述得重點(diǎn)突出,脈絡(luò)清晰。網(wǎng)絡(luò)部分分析集中在TCP/IP協(xié)議棧的數(shù)據(jù)連路層、網(wǎng)絡(luò)層以及傳輸層。

            • UNIX 網(wǎng)絡(luò)編程第一卷:套接口API和X/Open傳輸接口API,作者:W.Richard Stevens,譯者:楊繼張,清華大學(xué)出版社。不僅對套接口網(wǎng)絡(luò)編程有極好的描述,而且極為詳盡的闡述了相關(guān)的網(wǎng)絡(luò)背景知識。不論是入門還是深入研究, 都是不可多得的好資料。

            • Linux內(nèi)核源代碼情景分析(下),毛德操、胡希明著,浙江大學(xué)出版社,給出了unix域套接口部分的內(nèi)核代碼分析。

            • GNU/Linux編程指南,入門、應(yīng)用、精通,第二版,Kurt Wall等著,張輝譯
            品成人欧美大片久久国产欧美| 久久精品国产99久久久香蕉| 少妇人妻88久久中文字幕| 97视频久久久| 99久久精品午夜一区二区| 久久免费大片| 中文字幕一区二区三区久久网站 | 亚洲精品无码久久不卡| 欧美精品国产综合久久| 亚洲国产精品人久久| 亚洲精品乱码久久久久久按摩| AV色综合久久天堂AV色综合在| 久久久国产一区二区三区| 蜜臀av性久久久久蜜臀aⅴ麻豆| 无码AV中文字幕久久专区| 国产精品热久久毛片| 久久精品亚洲日本波多野结衣| 久久久久97国产精华液好用吗| 九九久久自然熟的香蕉图片| 国产精品成人久久久| 狠狠人妻久久久久久综合| 久久精品国产亚洲av高清漫画| 亚洲&#228;v永久无码精品天堂久久| 浪潮AV色综合久久天堂| 欧美日韩精品久久久久| 久久久久亚洲国产| 日产久久强奸免费的看| 国产毛片久久久久久国产毛片 | 中文字幕久久亚洲一区| 久久AAAA片一区二区| 国产高潮久久免费观看| 国产一区二区三区久久精品| WWW婷婷AV久久久影片| 国产精品久久久久天天影视| 国产午夜免费高清久久影院| 久久亚洲欧美国产精品| 久久无码人妻一区二区三区午夜 | 国产精品美女久久久免费| 色综合合久久天天综合绕视看 | av午夜福利一片免费看久久| 国内精品久久久久久野外|