Linux網路協議棧 socket建立(3)

2021-06-07 14:29:44 字數 4751 閱讀 8944

五、初始化 sk

分配完成 sk 後,另乙個重要的功能就是初始化它,sk 的成員相當複雜,其主要的初始化工作是在函式 sock_init_data()中完成的: 

void sock_init_data(struct socket *sock, struct sock *sk) 

else 

sk->sk_sleep        =        null; 

rwlock_init(&sk->sk_dst_lock); 

rwlock_init(&sk->sk_callback_lock); 

sk->sk_state_change        =        sock_def_wakeup; 

sk->sk_data_ready        =        sock_def_readable; 

sk->sk_write_space        =        sock_def_write_space; 

sk->sk_error_report         =        sock_def_error_report; 

sk->sk_destruct                =        sock_def_destruct; 

sk->sk_sndmsg_page        =        null; 

sk->sk_sndmsg_off        =        0; 

sk->sk_peercred.pid         =        0; 

sk->sk_peercred.uid        =        -1; 

sk->sk_peercred.gid        =        -1; 

sk->sk_write_pending        =        0; 

sk->sk_rcvlowat                =        1; 

sk->sk_rcvtimeo                =        max_schedule_timeout; 

sk->sk_sndtimeo                =        max_schedule_timeout; 

sk->sk_stamp.tv_sec     = -1l; 

sk->sk_stamp.tv_usec    = -1l; 

atomic_set(&sk->sk_refcnt, 1); 

}sock 結構中,有三個重要的雙向佇列,分別是 sk_receive_queue、sk_write_queue和 sk_error_queue。從它們的名字就可以看出來其作用了。 佇列並非採用通用的 list_head 來維護,而是使用 skb_buffer 佇列: 

struct sk_buff_head ;

這樣,佇列中指向的每乙個 skb_buffer,就是乙個資料報,分別是接收、傳送和投遞錯誤。 

剩餘的就是初始化其它成員變數了。後面再來專門分析這些成員的作用。 

inet_create 函式中,除了初始化 sk 成員的值,還有一部份**,是初始化乙個 inet的東東: 

inet = inet_sk(sk); 

inet->uc_ttl        = -1; 

inet->mc_loop        = 1; 

inet->mc_ttl        = 1; 

inet->mc_index        = 0; 

inet->mc_list        = null;[/code] 

inet 是乙個 struct inet_sock 結構型別,來看它的定義: 

struct inet_sock

只留意它的第乙個成員就足夠了。 

我們說 sock 是面向使用者態呼叫,而 sk 是面向核心驅動呼叫的,那 sk 是如何與協議棧互動的呢?對於每乙個型別的協議,為了與 sk 聯絡起來,都定義了乙個 struct ***_sock 結構,***是協議名,例如:

struct tcp_sock  

struct udp_sock ; 

struct raw_sock ;

很明顯,它們的結構定構是「af_inet一般屬性+自己的私有屬性」,因為它們的第乙個成員總是 inet。  

呵呵,現在回頭來照一下起初在 af_inet.c 中,封裝協議註冊的時候,size成員,對於 tcp 而言: 

.obj_size                = sizeof(struct tcp_sock), 

其它協議類似。 

以 obj_size 來確定每個 slab 快取項分配的大小,所以,我們就可說,每次申請分配的,實際上是乙個 struct ***_sock 結構大小的結構。因為都是定義於上層結構的第乙個成員,可以使用強制型別轉換來使用這塊分配的記憶體空間。例如: 

inet = inet_sk(sk); 

static inline struct inet_sock *inet_sk(const struct sock *sk) 

struct tcp_sock *tp = tcp_sk(sk); 

static inline struct tcp_sock *tcp_sk(const struct sock *sk) 

ok,inet_create()執行完,乙個 socket 套接字基本上就建立完畢了,剩下的就是與檔案系統掛鉤,回到最初的 sys_socket()函式中來,它在呼叫完 sock_create()後,緊接著呼叫 sock_map_fd()函式: 

int sock_map_fd(struct socket *sock) 

sprintf(name, "[%lu]", sock_inode(sock)->i_ino); 

this.name = name; 

this.len = strlen(name); 

this.hash = sock_inode(sock)->i_ino; 

file->f_dentry = d_alloc(sock_mnt->mnt_sb->s_root, &this); 

if (!file->f_dentry)  

file->f_dentry->d_op = &sockfs_dentry_operations; 

d_add(file->f_dentry, sock_inode(sock)); 

file->f_vfsmnt = mntget(sock_mnt); 

sock->file = file; 

file->f_op = sock_inode(sock)->i_fop = &socket_file_ops; 

file->f_mode = fmode_read | fmode_write; 

file->f_flags = o_rdwr; 

file->f_pos = 0;                 fd_install(fd, file); 

} out: 

return fd; 

}這個函式的核心思想,在一開始,就已經分析過了。從程序的角度來講,乙個socket 套接字就是乙個特殊的,已開啟的檔案。前面分配好乙個 socket  後,這裡要做的就是將它與檔案系統拉上親戚關係。首先獲取乙個空閒的檔案描述符號和 file 結構。然後在檔案系統中分配乙個目錄項(d_alloc),使  其指向已經分配的 inode 節點(d_add),然後把其目錄項掛在 sockfs 檔案系統的根目錄之下。並且把目錄項的指標 d_op設定成指向 sockfs_dentry_operati,這個資料結構通過函式指標提供他與檔案路徑有關的操作: 

static struct dentry_operations sockfs_dentry_operations = ;

最後一步,就是將 file 結構中的 f_op 和 sock 結構中的 i_fop 都指向 socket_file_ops,它是乙個函式指標集,指向了 socket面向檔案系統的使用者態呼叫的一些介面函式: 

static struct file_operations socket_file_ops = ;

ok,到這裡,整個 socket 套接字的建立工作,就宣告完成了。

寫到這裡,可以為 socket 的建立下乙個小結了: 

1、所謂建立socket,對核心而言,最重要的工作就是分配 sock 與 sk; 

2、 sock 面向上層系統呼叫,主要是與檔案系統互動。通過程序的 current 指標的 files,結合建立 socket時返回的檔案描符述,可以找到內  核中對應的 struct file,再根據 file的 f_dentry可以找到對應的目錄項,而目錄項 struct dentry 中,有 d_inode 指標,指向與 sock 封裝在一起的 inode。sock 又與 sk指標互指,一一對應。在這串結構中,有兩個重要的函式集  指標,乙個是檔案系統 struct file 中的f_op指標,它指向了,對應的使用者態呼叫的read,write等操呼叫,但不支援open,另乙個是struct socket結構,即 sock 的ops 指標,它在 inet_create()中被置為 sock->ops = answer->ops; 指向具體協議型別的 ops;例如,inet_stream_ops、inet_dgram_ops 或者是 inet_sockraw_ops 等等。它用來支援上層的 socket 的其它 api 呼叫。 

3、sk 面向核心協議棧,協議棧與它的介面資料結構是 struct protoname_sock,該結構中包含了一般性的 inet 結構和自己的私有成員,struct inet_sock 的第乙個成員就是乙個 sk 指標,而分配的 sk,實際上空間大小是 struct protoname_sock,所以,這三者可以通過強制型別轉換來獲取需要的指標。  

java 基於TCP UDP協議的Socket程式設計

基於tcp協議的socket程式設計 服務端 public class serverlogin 6.關閉輸入流 scoket.shutdowninput 7.向客戶端發訊息 info 歡迎您,登陸成功!os.write info.getbytes bufferedreader.close 8.關閉輸...

SimpliciTI 網路協議棧

3.網路拓撲結構 4.節點型別 5.網路協議分層 5.2 nwk network 層 5.3 mrfi minimal rf inte ce 6.simpliciti 幀結構 7.網路協議應用 7.3 加入網路 join 0x03 7.4 安全 security 0x04 7.5 頻率捷變 freq...

《探尋linux協議棧》之一 linux協議棧概述

linux協議棧分層設計思想 linux分層究竟對報文做了什麼總結 本人所從事開發以來,一直在做資料面相關。資料面是乙個通訊裝置最終好不好用最直接的體現。因為乙個網路裝置,好不好用,資料 快不快,資料 穩定不穩定,全部都是使用者最直接體 現。所以工作八年以來,對linux核心協議棧業也積累了自己的一...