代码语言:c复制
static int sock_bind(int fd, struct sockaddr *umyaddr, int addrlen)
{
struct socket *sock;
int i;
char address[MAX_SOCK_ADDR];
int err;
if (fd < 0 || fd >= NR_OPEN || current->files->fd[fd] == NULL)
return(-EBADF);
// 通过文件描述符找到对应的socket,见socket函数源码分析的视图
if (!(sock = sockfd_lookup(fd, NULL)))
return(-ENOTSOCK);
if((err=move_addr_to_kernel(umyaddr,addrlen,address))<0)
return err;
if ((i = sock->ops->bind(sock, (struct sockaddr *)address, addrlen)) < 0)
{
return(i);
}
return(0);
}
```
可见bind函数是直接调用底层af_inet.c的inet_bind
```c
// 给socket绑定一个地址
static int inet_bind(struct socket *sock, struct sockaddr *uaddr,
int addr_len)
{
struct sockaddr_in *addr=(struct sockaddr_in *)uaddr;
struct sock *sk=(struct sock *)sock->data, *sk2;
unsigned short snum = 0 /* Stoopid compiler.. this IS ok */;
int chk_addr_ret;
/* check this error. */
if (sk->state != TCP_CLOSE)
return(-EIO);
if(addr_len<sizeof(struct sockaddr_in))
return -EINVAL;
// raw是链路层,不需要端口
if(sock->type != SOCK_RAW)
{ // 已经绑定了端口
if (sk->num != 0)
return(-EINVAL);
snum = ntohs(addr->sin_port);
/*
* We can't just leave the socket bound wherever it is, it might
* be bound to a privileged port. However, since there seems to
* be a bug here, we will leave it if the port is not privileged.
*/
// 端口无效则随机获取一个非root才能使用的端口
if (snum == 0)
{
snum = get_new_socknum(sk->prot, 0);
}
// 小于1024的端口需要超级用户权限
if (snum < PROT_SOCK && !suser())
return(-EACCES);
}
// 判断ip
chk_addr_ret = ip_chk_addr(addr->sin_addr.s_addr);
if (addr->sin_addr.s_addr != 0 && chk_addr_ret != IS_MYADDR && chk_addr_ret != IS_MULTICAST)
return(-EADDRNOTAVAIL); /* Source address MUST be ours! */
if (chk_addr_ret || addr->sin_addr.s_addr == 0)
sk->saddr = addr->sin_addr.s_addr;
if(sock->type != SOCK_RAW)
{
/* Make sure we are allowed to bind here. */
cli();
for(sk2 = sk->prot->sock_array[snum & (SOCK_ARRAY_SIZE -1)];
sk2 != NULL; sk2 = sk2->next)
{
/* should be below! */
if (sk2->num != snum)
continue;
// 端口已经被使用,没有设置可重用标记,比如断开了解后在2msl内是否可以重用
if (!sk->reuse)
{
sti();
return(-EADDRINUSE);
}
if (sk2->num != snum)
continue; /* more than one */
if (sk2->saddr != sk->saddr)
continue; /* socket per slot ! -FB */
// 被监听的端口不能同时被使用
if (!sk2->reuse || sk2->state==TCP_LISTEN)
{
sti();
return(-EADDRINUSE);
}
}
sti();
// 保证该sk不在sock_array队列里
remove_sock(sk);
// 挂载到sock_array里
put_sock(snum, sk);
// tcp头中的源端口
sk->dummy_th.source = ntohs(sk->num);
sk->daddr = 0;
sk->dummy_th.dest = 0;
}
return(0);
}
由代码中可以看到,bind函数主要做的事情是判断一下端口和ip和可用性,然后把sock结构体挂载到协议对应的sock_array哈希列表中。