Linux内核源码-sys_listen()
来源:互联网 发布:戎美女装淘宝店 编辑:程序博客网 时间:2024/06/10 07:42
(本文部分参考了《Linux内核源代码情景分析》)
操作SYS_LISTEN 设定server插口,是由 sys_listen()实现的。
需要了解的是“有连接”模式的插口天生就是按 client/server 的模式运转的,只有在一个 server 插口和一个 client 插口之间建立起连接。区分这两种插口的方法是:只要在插口创建以后为其调用了 listen(),这个插口就成为 server 插口了。凡是 server 插口都不能主动去与别的插口建立连接,而只能被动地通过 accept()接受来自 client 插口的连接请求。而 client 插口则相反,不能调用 accept()来接受连接请求,而只能主动地通过 connect()提出连接请求。
listen系统调用:asmlinkage long sys_listen(int fd, int backlog)的操作与sys_bind()十分类似,都是根据打开文件号 fd 找到插口的 socket 数据结构( inode 的一部分),进而通过结构中的指针 ops执行相应的回调函数。sys_listen的回调函数是:inet_listen(),在此函数中会首先判断传进来的插口类型和状态,合法后会调动tcp_listen_start()函数开始侦听,其实是相当简单的:除状态本身的变化以外,就是设置(或改变几个参数),主要就是最大队列长度sk_max_ack_backlog。
函数调用过程如图所示:
下面是完整代码:
inet_listen代码如下:
/* 将一个socket转入listen状态 */int inet_listen(struct socket *sock, int backlog){ struct sock *sk = sock->sk; unsigned char old_state; int err; lock_sock(sk); err = -EINVAL;/* 检测系统调用的状态和类型,如果不合法则退出 *//**只有插口的类型为 SOCK_STREAM,即“有连接”模式的插口,并且已经为其 bind()了插口地址,才允许 listen()。 *对于符合这些条件的插口也不是什么时候都可以调用 listen()的。*插口的 sock结构中有个成分 state,用来实现一种“有限状态机”。只有当这个状态机处于 TCP_CLOSE 或 TCP_LISTEN这两种状态时才可以对其调用 listen()。*在前面 sock_create()的代码中可以看到在创建一个插口时要调用函数 sock_init_data()对分配的sock数据结构进行初始化,在那里state被设置成 TCP_CLOSE。*状态TCP_CLOSE 表示插口只是刚刚建立,尚未宣布成为 server 插口;* TCP_LISTEN 则表示插口已经设置成 server 插口,当尚未建立起连接,并且不是在等待来自 client 一方的连接请求。*只有在这两种状态下才允许改变插口的参数(主要是连接请求队列的容量)。*/ if (sock->state != SS_UNCONNECTED || sock->type != SOCK_STREAM) goto out; old_state = sk->sk_state; if (!((1 << old_state) & (TCPF_CLOSE | TCPF_LISTEN))) goto out; if (old_state != TCP_LISTEN) { err = tcp_listen_start(sk);/* 开始侦听 */ if (err) goto out; } /* 设置传输控制块的连接队列长度上限 */ sk->sk_max_ack_backlog = backlog; err = 0;out: release_sock(sk); return err;}
tcp_listen_start()代码如下:
int tcp_listen_start(struct sock *sk){ struct inet_sock *inet = inet_sk(sk); struct tcp_sock *tp = tcp_sk(sk); struct tcp_listen_opt *lopt; /* 初始连接队列长度上限 */ sk->sk_max_ack_backlog = 0; sk->sk_ack_backlog = 0; tp->accept_queue = tp->accept_queue_tail = NULL; /* 初始化传输控制块中与延时发送ACK有关的数据结构 */ rwlock_init(&tp->syn_wait_lock); tcp_delack_init(tp); /* 为管理连接请求块的散列表分配存储空间,如果失败则退出 */ lopt = kmalloc(sizeof(struct tcp_listen_opt), GFP_KERNEL); if (!lopt) return -ENOMEM; memset(lopt, 0, sizeof(struct tcp_listen_opt)); for (lopt->max_qlen_log = 6; ; lopt->max_qlen_log++) if ((1 << lopt->max_qlen_log) >= sysctl_max_syn_backlog) break; /* 计算哈希表的哈希种子 */ get_random_bytes(&lopt->hash_rnd, 4); /* 将散列块与传输控制块绑定 */ write_lock_bh(&tp->syn_wait_lock); tp->listen_opt = lopt; write_unlock_bh(&tp->syn_wait_lock); sk->sk_state = TCP_LISTEN;/* 设置控制块的状态 */ if (!sk->sk_prot->get_port(sk, inet->num)) {/* 进行端口绑定 */ inet->sport = htons(inet->num);/* 设置网络字节序的端口号 */ sk_dst_reset(sk);/* 清除路由缓存 */ sk->sk_prot->hash(sk);/* 将传输控制块添加到侦听散列表中 */ return 0; } /* 绑定失败,设置其状态 */ sk->sk_state = TCP_CLOSE; /* 解除侦听连接请求块与传输控制块的绑定 */ write_lock_bh(&tp->syn_wait_lock); tp->listen_opt = NULL; write_unlock_bh(&tp->syn_wait_lock); kfree(lopt);/* 释放侦听连接请求块 */ return -EADDRINUSE;}
阅读全文
0 0
- Linux内核源码-sys_listen()
- 网络编程常用接口的内核实现----sys_listen()
- 网络编程常用接口的内核实现----sys_listen()
- 看Linux内核源码
- linux 内核源码结构
- linux内核源码组织
- linux内核源码阅读
- Linux内核源码
- linux内核源码结构
- linux内核源码学习
- linux内核源码结构
- Linux内核在线源码
- Linux内核源码分析
- linux内核源码编译
- 编译linux内核源码
- linux内核源码
- Linux内核源码目录
- linux 内核源码树
- 剑指offer--二叉树中和为某一值的路径
- win10配置Java环境变量
- 设计模式初探-适配器模式
- vsftp.conf 、user_list、ftpusers 配置文件
- Python科学计算三维可视化(1) ——TVTK库入门
- Linux内核源码-sys_listen()
- 笨方法学Python 习题 35: 分支和函数
- POJ 3544 Journey with Pigs
- SVN(TortoiseSVN)详细教程(四)--创建分支合并相互操作
- IO流的小例子
- 最大乘积
- mysql分区效率测试
- Numpy之array用法
- python批量kill某一个进程