Linux fd 系列 — socket fd 是什么?
socket fd 長什么樣子?
什么是 socket fd ?粗糙的來講,就是網(wǎng)絡(luò) fd,比如我們最常見的 C/S 客戶端服務(wù)端的編程模式,就是網(wǎng)絡(luò)通信的一種方式。撇開底層和協(xié)議細(xì)節(jié),網(wǎng)絡(luò)通信和文件讀寫從接口上有本質(zhì)區(qū)別嗎?
其實沒啥區(qū)別,不就是讀過來和寫過去嘛,簡稱 IO 。我們先看一下 socket fd 是什么樣子的?隨便找了個進程
root@ubuntu:~#?ll?/proc/1583/fd
total?0
lrwx------?1?root?root?64?Jul?19?12:37?7?->?socket:[18892]
lrwx------?1?root?root?64?Jul?19?12:37?8?->?socket:[18893]
這里我們看到 fd 7、8 都是一個 socket fd,名字:socket:[18892]
整數(shù)句柄后面一般會跟一些信息,用于幫助我們了解這個 fd 是什么。舉個例子,如果是文件 fd,那么箭頭后面一般是路徑名稱?,F(xiàn)在拆解一下這個名字:- socket :標(biāo)識這是一個 socket 類型的 fd
[18892]
?:這個是一個 inode 號,能夠唯一標(biāo)識本機的一條網(wǎng)絡(luò)連接;
/proc/net/tcp
文件。這個文件里面能看到所有的 tcp 連接的信息。root@ubuntu:~#?grep?-i?"18892"?/proc/net/tcp
??18:?00000000:1F93?00000000:0000?0A?00000000:00000000?00:00000000?00000000?????0????????0?18892?1?ffff880197fba580?100?0?0?10?0?????????????????????
root@ubuntu:~#?grep?-i?"18893"?/proc/net/tcp
??28:?00000000:1F7C?00000000:0000?0A?00000000:00000000?00:00000000?00000000?????0????????0?18893?1?ffff880197fbad00?100?0?0?10?0????
知識點又來了,/proc/net/tcp
這個文件記錄了 tcp 連接的信息,這份信息是非常有用的。包含了 TCP 連接的地址(16進制顯示),inode 的信息,連接的狀態(tài)等等。socket fd 是什么?
環(huán)境聲明:
Linux 內(nèi)核版本 4.19?為了方便,如果沒特意說明協(xié)議,默認(rèn) TCP 協(xié)議;
socket
可能你還沒反應(yīng)過來,中文名:套接字 是不是更熟悉點。Linux 網(wǎng)絡(luò)編程甚至可以叫做套接字編程。有些概念你必須捋一捋 。我們思考幾個小問題:socket 跟 tcp/ip 有什么區(qū)別?就不該把這兩個東西放在一起比較討論,就不是一個東西。tcp/ip 是網(wǎng)絡(luò)協(xié)議棧,socket 是操作系統(tǒng)為了方便網(wǎng)絡(luò)編程而設(shè)計出來的編程接口而已。理論基礎(chǔ)是各種網(wǎng)絡(luò)協(xié)議,協(xié)議棧呀,啥的。但是如果你要進行網(wǎng)絡(luò)編程,落到實處,對程序猿來講就是 socket 編程。對于網(wǎng)絡(luò)的操作,由?socket?體現(xiàn)為?open?->?read/write?->close?這樣的編程模式,這個統(tǒng)一到文件的一種形式。socket 的 open 就是 socket(int domain, int type, int protocol)
,和文件一樣,都是獲取一個句柄。
網(wǎng)絡(luò)模型一般會對應(yīng)到兩種:
- 完美理論的 OSI 七層模型;
- 現(xiàn)實應(yīng)用的 5 層模型;
- 客戶端和服務(wù)端都用
socket
調(diào)用創(chuàng)建套接字; - 服務(wù)端用
bind
綁定監(jiān)聽地址,用listen
把套接字轉(zhuǎn)化為監(jiān)聽套接字,用accept
撈取一個客戶端來的連接; - 客戶端用
connect
進行建連,用write/read
進行網(wǎng)絡(luò) IO;
socket fd 的類型
上面我們提到了套接字,這是我們網(wǎng)絡(luò)編程的主體,套接字由
socket()
系統(tǒng)調(diào)用創(chuàng)建,但你可知套接字其實可分為兩種類型,監(jiān)聽套接字和普通套接字。而監(jiān)聽套接字是由 listen()
把 socket fd 轉(zhuǎn)化而成。?1???監(jiān)聽套接字
對于監(jiān)聽套接字,不走數(shù)據(jù)流,只管理連接的建立。
accept
將從全連接隊列獲取一個創(chuàng)建好的 socket( 3 次握手完成),對于監(jiān)聽套接字的可讀事件就是全連接隊列非空。對于監(jiān)聽套接字,我們只在乎可讀事件。?2???普通套接字
普通套接字就是走數(shù)據(jù)流的,也就是網(wǎng)絡(luò) IO,針對普通套接字我們關(guān)注可讀可寫事件。在說 socket 的可讀可寫事件之前,我們先捋順套接字的讀寫大概是什么樣子吧。套接字層是內(nèi)核提供給程序員用來網(wǎng)絡(luò)編程的,程序猿讀寫都是針對套接字而言,那么
write( socketfd, /* 參數(shù) */)
和 read( socketfd, /* 參數(shù) */)
都會發(fā)生什么呢?- write 數(shù)據(jù)到 socketfd,大部分情況下,數(shù)據(jù)寫到 socket 的內(nèi)存 buffer,就結(jié)束了,并沒有發(fā)送到對端網(wǎng)絡(luò)(異步發(fā)送);
- read socketfd 的數(shù)據(jù),也只是從 socket 的 內(nèi)存 buffer 里讀數(shù)據(jù)而已,而不是從網(wǎng)卡讀(雖然數(shù)據(jù)是從網(wǎng)卡一層層遞上來的);
- socketfd 可讀:其實就是 socket buffer 內(nèi)有數(shù)據(jù)(超過閾值 SO_RCLOWAT );
- socketfd 可寫:就是 socket buffer 還有空間讓你寫(閾值 SO_SNDLOWAT );
socket fd 為什么能具備“文件”的語義,從而和 eventfd,ext2 fd 這樣的句柄一樣,統(tǒng)一提供對外 io 的樣子?
核心就是:sockfs ,這也是個文件系統(tǒng),只不過普通用戶看不見,這是只由內(nèi)核管理的文件系統(tǒng),位于 vfs 之下,為了封裝 socket 對上的文件語義。
//?net/socket.c
static?int?__init?sock_init(void)
{
????//?注冊?sockfs?文件系統(tǒng)
????err?=?register_filesystem(