11.3.1 锁的结构
由 2.1 Socket系统调用我们知道,一个TCP socket在内核有一个数据结构,这个数据结构是不能被两个及其以上的使用者同时访问的,否则就会由于数据不一致导致严重的问题。在Linux中,TCP socket的使用者有两种:进程(线程)和软中断。同一时间可能会有两个进程(线程),或位于不同CPU的两个软中断,或进程(线程)与软中断访问同一个socket。既然socket在同一时刻只能被一个使用者访问,那么互斥机制是如何实现的呢?是使用锁完成的。进程(线程)在访问socket之前会申请锁,访问结束时释放锁。软中断也是一样,但软中断所申请的锁与进程(线程)不同。TCP的内核同步就是靠锁实现的。由于TCP并不区分进程与线程,所以下面进程和线程一律用进程指代。进程用lock_sock申请锁:
1459 static inline void lock_sock(struct sock *sk)
1460 {
1461 lock_sock_nested(sk, 0);
1462 }
lock_sock_nested:
2284 void lock_sock_nested(struct sock *sk, int subclass)
2285 {
2286 might_sleep(); //说明调用本函数可能导致睡眠
2287 spin_lock_bh(&sk->sk_lock.slock); //申请自旋锁并关闭本地软中断
2288 if (sk->sk_lock.owned) //已有进程正在持有锁
2289 __lock_sock(sk);
2290 sk->sk_lock.owned = 1; //标记锁正在被进程、持有
2291 spin_unlock(&sk->sk_lock.slock); //释放自旋锁(注意软中断没有恢复)
2292 /*
2293 * The sk_lock has mutex_lock() semantics here:
2294 */
2295 mutex_acquire(&sk->sk_lock.dep_map, subclass, 0, _RET_IP_);
2296 local_bh_enable(); //开启软中断,允许软中断运行
2297 }
释放锁时使用release_sock:
2300 void release_sock(struct sock *sk)
2301 {
2302 /*
2303 * The sk_lock has mutex_unlock() semantics:
2304 */
2305 mutex_release(&sk->sk_lock.dep_map, 1, _RET_IP_);
2306
2307 spin_lock_bh(&sk->sk_lock.slock);
2308 if (sk->sk_backlog.tail) //backlog队列有skb
2309 __release_sock(sk); //处理backlog队列中的skb
2310
2311 if (sk->sk_prot->release_cb)
2312 sk->sk_prot->release_cb(sk); //执行因进程锁定socket而被延迟的软中断任务
2313
2314 sk->sk_lock.owned = 0; //标识进程释放锁
2315 if (waitqueue_active(&sk->sk_lock.wq)) //有进程在等待队列中
2316