11.3 TCP 커 널 동기 화
프로 세 스 용 locksock 신청 잠 금:
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 wake_up(&sk->sk_lock.wq); //
2317 spin_unlock_bh(&sk->sk_lock.slock);
2318 }
소프트 인 터 럽 트 사용 bhlock_sock_nested 자전거 자물쇠 신청, bh 사용unlock_sock 자 물 쇠 를 방출 합 니 다.TCP 가 어떻게 서로 다른 유형의 사용자 들 사이 에서 데이터 동기 화 를 실현 하 는 지 분석 해 보 자.
11.3.2 프로 세 스 간
프로 세 스 T1 먼저 lock 호출sock_nested 함수 잠 금 획득, sk - > sk 설정lock. owned = 1 후 socket 에 접근 하기;프로 세 스 T2 호출 locksock_nested 함수 시 호출lock_sock 함수:
1832 static void __lock_sock(struct sock *sk)
1833 __releases(&sk->sk_lock.slock)
1834 __acquires(&sk->sk_lock.slock)
1835 {
1836 DEFINE_WAIT(wait);
1837
1838 for (;;) {
1839 prepare_to_wait_exclusive(&sk->sk_lock.wq, &wait,
1840 TASK_UNINTERRUPTIBLE); // TASK_UNINTERRUPTIBLE, CPU ,
1841 spin_unlock_bh(&sk->sk_lock.slock);
1842 schedule(); // CPU
1843 spin_lock_bh(&sk->sk_lock.slock);
1844 if (!sock_owned_by_user(sk))
1845 break;
1846 }
1847 finish_wait(&sk->sk_lock.wq, &wait);
1848 }
DEFINE_WAIT 는 수면 이 벤트 를 정의 합 니 다.889 #define DEFINE_WAIT_FUNC(name, function) \
890 wait_queue_t name = { \
891 .private = current, \
892 .func = function, \
893 .task_list = LIST_HEAD_INIT((name).task_list), \
894 }
895
896 #define DEFINE_WAIT(name) DEFINE_WAIT_FUNC(name, autoremove_wake_function)
T2 는 1842 줄 의 schedule 을 실행 한 후 수면 상태 로 들 어 갑 니 다. prepare 에 있 기 때 문 입 니 다.to_wait_exclusive 함수 에 프로 세 스 상태 설정:
81 void
82 prepare_to_wait_exclusive(wait_queue_head_t *q, wait_queue_t *wait, int state)
83 {
84 unsigned long flags;
85
86 wait->flags |= WQ_FLAG_EXCLUSIVE;
87 spin_lock_irqsave(&q->lock, flags);
88 if (list_empty(&wait->task_list))
89 __add_wait_queue_tail(q, wait); // wait sk->sk_lock.wq.task_list
90 set_current_state(state); //
91 spin_unlock_irqrestore(&q->lock, flags);
92 }
T1 실행 releasesock 잠 금 해제 시 wake 실행up 깨 우기 T2, wakeup 은 봉 인 했 습 니 다wake_up 함수 의 매크로,wake_up 깨 우기 동작 을 수행 합 니 다:3159 static void __wake_up_common(wait_queue_head_t *q, unsigned int mode,
3160 int nr_exclusive, int wake_flags, void *key)
3161 {
3162 wait_queue_t *curr, *next;
3163
3164 list_for_each_entry_safe(curr, next, &q->task_list, task_list) { //
3165 unsigned flags = curr->flags;
3166
3167 if (curr->func(curr, mode, wake_flags, key) && //curr->func DEFINE_WAIT autoremove_wake_function
3168 (flags & WQ_FLAG_EXCLUSIVE) && !--nr_exclusive)
3169 break;
3170 }
3171 }
...
3183 void __wake_up(wait_queue_head_t *q, unsigned int mode,
3184 int nr_exclusive, void *key)
3185 {
3186 unsigned long flags;
3187
3188 spin_lock_irqsave(&q->lock, flags);
3189 __wake_up_common(q, mode, nr_exclusive, 0, key);
3190 spin_unlock_irqrestore(&q->lock, flags);
3191 }
autoremove_wake_function 호출 defaultwake_function 함수, defaultwake_function 함수 호출 tryto_wake_up 깨 우기 T2:1484 static int
1485 try_to_wake_up(struct task_struct *p, unsigned int state, int wake_flags)
1486 {
...
1514 p->state = TASK_WAKING; // CPU
...
즉, 두 프로 세 스 가 같은 socket 을 연이어 방문 한 후에 방문 하면 잠 을 자고 먼저 방문 한 사람 이 자 물 쇠 를 풀 어야 깨 어 나 방문 할 수 있 는 기회 가 생 긴 다 는 것 이다.깨 우 는 순 서 는 줄 을 서 는 순서 다.11.3.3 소프트 인 터 럽 트 사이
하나의 CPU 는 같은 시간 에 하나의 소프트 인 터 럽 트 만 실행 할 수 있 기 때문에 소프트 인 터 럽 트 간 의 병렬 접근 은 서로 다른 CPU 사이 에서 만 가능 합 니 다.소프트 인 터 럽 트 에 사용 되 는 자 물 쇠 는 자전 자물쇠 이 고 두 번 째 소프트 인 터 럽 트 가 이 자 물 쇠 를 신청 할 때 자물쇠 소유자 가 자 물 쇠 를 풀 때 까지 팽팽 한 순환 을 수행 합 니 다.CPU 가 소프트 인 터 럽 트 컨 텍스트 에 오래 머 물 수 없 기 때문에 (그렇지 않 으 면 CPU 의 다른 작업 을 수행 할 수 없습니다) 이 자 물 쇠 를 사용 하면 가장 빠 른 속도 로 자 물 쇠 를 얻 을 수 있 습 니 다.자 물 쇠 를 얻 은 후의 방문 도 시간 이 너무 길 어 서 는 안 되 며, 특히 잠 을 잘 수 없다.두 개 이상 의 소프트 인 터 럽 트 가 동시에 하나의 socket 에 접근 하 는 경우: 패키지 소프트 인 터 럽 트 와 타이머 시간 초과 가 동시에 발생 하고, irqloadbalance 를 열 때 같은 네트워크 카드 로 받 은 패 키 지 는 서로 다른 CPU 에서 동시에 처리 되 며, 서로 다른 네트워크 카드 로 도착 하 는 요청 은 같은 listen scoket 에 접근 합 니 다.
11.3.4 프로 세 스 와 소프트 인 터 럽 트 사이
소프트 인 터 럽 트 의 실행 우선 순위 가 높 습 니 다. 프로 세 스 가 실행 되 는 임의의 시간 에 소프트 인 터 럽 트 에 의 해 중 단 될 수 있 습 니 다. (소프트 인 터 럽 트 를 닫 지 않 는 한)방문 의 선후 순서에 따라 두 가지 상황 이 있다.
(1) 소프트 인 터 럽 트 프로 세 스에 먼저 접근 한 후 접근
이 때 소프트 인 터 럽 트 는 자 물 쇠 를 가 져 왔 습 니 다. 프로 세 스 는 자 물 쇠 를 가 져 올 때 기 다 립 니 다. 소프트 인 터 럽 트 가 자 물 쇠 를 풀 때 프로 세 스 가 자 물 쇠 를 성공 적 으로 가 져 올 수 있 습 니 다.
(2) 프로 세 스 가 소프트 에 먼저 접근 하고 끊 긴 후에 접근 합 니 다.
프로 세 스 가 자동 잠 금 (소프트 인 터 럽 트, 소프트 인 터 럽 트 중단 방지) 을 가 져 올 때 sk - > sklock. owned 는 1 로 설정 한 후 자 물 쇠 를 풀 고 소프트 인 터 럽 트 를 열 고 socket 에 대한 접근 을 실행 합 니 다.이 때 소프트 인 터 럽 트 가 발생 하면 프로 세 스 의 실행 이 중 단 됩 니 다.소프트 인 터 럽 트 TCP 입구 함수 tcpv4_rcv 시:
1961 int tcp_v4_rcv(struct sk_buff *skb)
1962 {
...
2024 bh_lock_sock_nested(sk); //
2025 ret = 0;
2026 if (!sock_owned_by_user(sk)) { sk->sk_lock.owned 1
...
2039 } else if (unlikely(sk_add_backlog(sk, skb,
2040 sk->sk_rcvbuf + sk->sk_sndbuf))) {
2041 bh_unlock_sock(sk); //
2042 NET_INC_STATS_BH(net, LINUX_MIB_TCPBACKLOGDROP);
2043 goto discard_and_relse;
2044 }
2045 bh_unlock_sock(sk); //
프로 세 스 가 socket 을 잠 근 상태 에서 skb 는 skadd_backlog 함수 처리: 777 static inline __must_check int sk_add_backlog(struct sock *sk, struct sk_buff *skb,
778 unsigned int limit)
779 {
780 if (sk_rcvqueues_full(sk, skb, limit))
781 return -ENOBUFS;
782
783 __sk_add_backlog(sk, skb);
784 sk->sk_backlog.len += skb->truesize;
785 return 0;
786 }
에서sk_add_backlog 함 수 는 skb 를 backlog 대기 열 에 넣 습 니 다: 749 static inline void __sk_add_backlog(struct sock *sk, struct sk_buff *skb)
750 {
751 /* dont let skb dst not refcounted, we are going to leave rcu lock */
752 skb_dst_force(skb);
753
754 if (!sk->sk_backlog.tail)
755 sk->sk_backlog.head = skb;
756 else
757 sk->sk_backlog.tail->next = skb;
758
759 sk->sk_backlog.tail = skb;
760 skb->next = NULL;
761 }
skb 를 backlog 대기 열 에 넣 으 면 소프트 인 터 럽 트 가 되 돌아 오고 프로 세 스 가 실 행 될 수 있 습 니 다.프로 세 스 가 잠 금 을 풀 기 전에 모든 소프트 인 터 럽 트 는 skb 를 backlog 대기 열 에 넣 습 니 다.프로 세 스 가 release 를 호출 할 때sock 잠 금 해제 시 backlog 대기 열 이 비어 있 지 않 으 면release_sock: 1850 static void __release_sock(struct sock *sk)
1851 __releases(&sk->sk_lock.slock)
1852 __acquires(&sk->sk_lock.slock)
1853 {
1854 struct sk_buff *skb = sk->sk_backlog.head;
1855
1856 do {
1857 sk->sk_backlog.head = sk->sk_backlog.tail = NULL;
1858 bh_unlock_sock(sk); // ,
1859
1860 do {
1861 struct sk_buff *next = skb->next;
1862
1863 prefetch(next);
1864 WARN_ON_ONCE(skb_dst_is_noref(skb));
1865 skb->next = NULL;
1866 sk_backlog_rcv(sk, skb); // skb
1867
1868 /*
1869 * We are in process context here with softirqs
1870 * disabled, use cond_resched_softirq() to preempt.
1871 * This is safe to do because we've taken the backlog
1872 * queue private:
1873 */
1874 cond_resched_softirq(); // CPU, ;
1875
1876 skb = next;
1877 } while (skb != NULL);
1878
1879 bh_lock_sock(sk);
1880 } while ((skb = sk->sk_backlog.head) != NULL);
1881
1882 /*
1883 * Doing the zeroing here guarantee we can not loop forever
1884 * while a wild producer attempts to flood us.
1885 */
1886 sk->sk_backlog.len = 0;
1887 }
__release_sock 에서 backlog 대기 열 을 처리 하 는 방법 은 먼저 backlog 대기 열 에 있 는 모든 skb 를 개인 대기 열 로 옮 긴 다음 자전 자 물 쇠 를 풀 고 소프트 인 터 럽 트 를 닫 는 조건 에서 sk 를 호출 하 는 것 입 니 다.backlog_rcv 함수 처리 skb.skb 를 처리 할 때마다 CPU 를 한 번 씩 포기 합 니 다. 대기 열 에 skb 가 너무 많아 서 소프트 인 터 럽 트 가 너무 오래 걸 리 지 않도록 합 니 다.처리 하 는 동안 소프트 인 터 럽 트 가 발생 하면 skb 는 원리 적 인 backlog 대기 열 에 들 어가 현재 처리 하 는 대기 열 과 관계 가 없습니다.sk_backlog_rcv 는 skb 를 TCP 에 넣 어 처리 합 니 다: 790 static inline int sk_backlog_rcv(struct sock *sk, struct sk_buff *skb)
791 {
792 if (sk_memalloc_socks() && skb_pfmemalloc(skb))
793 return __sk_backlog_rcv(sk, skb); // sk->sk_backlog_rcv
794
795 return sk->sk_backlog_rcv(sk, skb); // tcp_v4_do_rcv
796 }
결국, backlog 대기 열 에 있 는 skb 는 tcpv4_do_rcv 함수 처리.한 마디 로 하면 프로 세 스 가 socket 을 먼저 잠 글 때 소프트 인 터 럽 트 는 skb 를 backlog 대기 열 에 넣 고 돌아 갈 수 있 을 뿐 socket 에 접근 할 수 없습니다.프로 세 스 가 socket 을 풀 때 backlog 대기 열 에 있 는 skb 를 처리 합 니 다.프로 세 스 가 socket 을 가지 고 있 는 시간 이 길 수록 backlog 대기 열 이 커지 고 너무 크 면 가방 을 잃 어 버 릴 수 있 습 니 다 (실제로 거의 발생 하지 않 습 니 다).이러한 병발 방식 을 사용 하면 socket 이 프로 세 스 와 소프트 인 터 럽 트 사이 의 병발 보 호 를 실현 할 뿐만 아니 라 소프트 인 터 럽 트 의 운행 에 도 영향 을 주지 않 습 니 다.프로 세 스 가 socket 에 접근 할 때 잠시 수면 을 취 합 니 다 (예 를 들 어 사용자 상태 와 커 널 사이 에서 데 이 터 를 전달 할 때). 심각 한 결 과 를 일 으 키 지 는 않 지만 장시간 수면 을 취 할 때 socket 을 방출 해 야 합 니 다 (예 를 들 어 메모 리 를 기다 릴 때).
이 내용에 흥미가 있습니까?
현재 기사가 여러분의 문제를 해결하지 못하는 경우 AI 엔진은 머신러닝 분석(스마트 모델이 방금 만들어져 부정확한 경우가 있을 수 있음)을 통해 가장 유사한 기사를 추천합니다:
k8spacket 및 Grafana를 통한 kubernetes용 TCP 패킷 트래픽 시각화보고 있지 않을 때 k8s 클러스터가 무엇을 하는지 알고 있습니까? 누가 그와 TCP 통신을 설정합니까? k8spacket 및 Grafana 를 사용하여 클러스터의 TCP 트래픽을 시각화할 수 있습니다. 설정된 연결...
텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
CC BY-SA 2.5, CC BY-SA 3.0 및 CC BY-SA 4.0에 따라 라이센스가 부여됩니다.