bind()系統調用是給套接字分配一個本地協議地址,對於網際協議,協議地址是32位IPv4地址或128位IPv6地址與16位的TCP或UDP端口號的組合。如果沒有通過bind()來指定本地的協議地址,在和遠端通信時,內核會隨機給套接字分配一個IP地址和端口號。bind()系統調用通常是在網絡程序的服務器端調用,而且是必須的。如果TCP服務器不這麼做,讓內核來選擇臨時端口號而不是捆綁眾所周知的端口,客戶端如何發起與服務器的連接?
一、sys_bind()
bind()系統調用對應的內核實現是sys_bind(),其源碼及分析如下:
/*
* Bind a name to a socket. Nothing much to do here since it's
* the protocol's responsibility to handle the local address.
*
* We move the socket address to kernel space before we call
* the protocol layer (having also checked the address is ok).
*/
SYSCALL_DEFINE3(bind, int, fd, struct sockaddr __user *, umyaddr, int, addrlen)
{
struct socket *sock;
struct sockaddr_storage address;
int err, fput_needed;
/*
* 以fd為索引從當前進程的文件描述符表中
* 找到對應的file實例,然後從file實例的private_data中
* 獲取socket實例。
*/
sock = sockfd_lookup_light(fd, &err, &fput_needed);
if (sock) {
/*
* 將用戶空間的地址拷貝到內核空間的緩沖區中。
*/
err = move_addr_to_kernel(umyaddr, addrlen, (struct sockaddr *)&address);
if (err >= 0) {
/*
* SELinux相關,不需要關心。
*/
err = security_socket_bind(sock,
(struct sockaddr *)&address,
addrlen);
/*
* 如果是TCP套接字,sock->ops指向的是inet_stream_ops,
* sock->ops是在inet_create()函數中初始化,所以bind接口
* 調用的是inet_bind()函數。
*/
if (!err)
err = sock->ops->bind(sock,
(struct sockaddr *)
&address, addrlen);
}
fput_light(sock->file, fput_needed);
}
return err;
}
sys_bind()的代碼流程如下圖所示:
sys_bind()首先調用sockfd_lookup_light()查找套接字對應的socket實例,如果沒有找到,則返回EBADF錯誤。在進行綁定操作之前,要先將用戶傳入的本地協議地址從用戶空間拷貝到內核緩沖區中,在拷貝過程中會檢查用戶傳入的地址是否正確。如果指定的長度參數小於0或者大於sockaddr_storage的大小,則返回EINVAL錯誤;如果在調用copy_from_user()執行拷貝操作過程中出現錯誤,則返回EFAULT錯誤。在上述的准備工作都完成後,調用inet_bind()函數(即sock->ops->bind指向的函數,參見注釋)來完成綁定操作。