歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
您现在的位置: Linux教程網 >> UnixLinux >  >> Linux編程 >> Linux編程

網絡編程常用接口的內核實現----sys_bind()

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指向的函數,參見注釋)來完成綁定操作。

Copyright © Linux教程網 All Rights Reserved