Linux 네트워크 고급 프로그래밍-libco 코 루틴 이벤트 중심 및 스케줄링

1. 코 루틴의 "차단"과 스레드의 "비 차단"

스레드의 모든 코 루틴은 기본적으로 이전 장의 코드에서 직렬로 실행됩니다 : https://blog.csdn.net/qq_44065088/article/details/109272505

Producer 코 루틴 함수에서 1 동안 대기 하도록 poll 함수가 호출되고 Consumer 에서 생산자 신호 기다리기 위해 co_cond_timedwait 함수 도 호출되는 것을 볼 수 있습니다. 코 루틴의 관점에서 볼 때 이러한 대기는 동기식 (동기식 ) 및 차단 ( blocking ) 인 것처럼 보이지만 기본 스레드의 관점에서는 비 차단 (비 차단)입니다. 예 : 스레드의 생산자-소비자 모델에서 pthread_cond_wait도 스레드를 차단합니다. 스레드 의 관점에서는 스레드가 차단 된 것처럼 보이지만 커널의 관점에서는 차단되지 않습니다. 커널은 다음을 수행 할 수 있습니다. 다른 스레드를 실행하도록 서둘러 예약하십시오. 동일한 추론 : 코 루틴은 동일합니다. 코 루틴 자체의 경우 차단이 실현되지만 아래 스레드의 경우 다른 코 루틴 기능을 실행하는 데 바쁠 수 있습니다. 

이 예에서 Consumer 코 루틴이 co_cond_timedwait 함수를 "block"으로 호출 하면 스레드가 Producer 스케줄링 재개 했을 수 있으며 그 반대의 경우도 마찬가지입니다. 그러면이 스레드가 코 루틴의 "스케줄링"을 담당하는 곳은 어디입니까? 코 루틴 자체를 실행하는 스레드입니다 [따라서이 스레드가 실제로 차단되면 결과적으로 코 루틴의 전체 프레임 워크가 작동을 중지하고 차단됩니다 ].

 

커널 차단을 방지하려면 커널에서 제공하는 비 차단 IO 메커니즘에 의존하고 fcntl을 사용하여 소켓 파일 설명자를 nonBlock으로 설정해야합니다. 물론 libco도 매우 신중합니다.이 비 블록 프로세스를 캡슐화하고 "동기 차단"인 것처럼 가장합니다. (co_cond_timedwait ()와 동일). 실제로 go 언어는이 작업을 수행하지만 libco의 위장은 더 철저하고 기만적이므로 눈이 종종 우리를 속일 수 있습니다. Libco는 dlsym 메커니즘을 통해 다양한 네트워크 IO 관련 시스템 호출을 연결하여 사용자가 "동기화" 할 수 있도록합니다. "" 메소드는 읽기, 쓰기, 연결과 같은 함수를 직접 사용하므로 생산자 소비자 코 루틴 함수에서 co_enable_hook_sys () 가 호출되는 것을 볼 수 있습니다 . [ 호출 후 후크 시스템 호출 함수가 켜지고 구현이 필요합니다. 파일 설명자를 noblock으로 설정하십시오. 그렇지 않으면 스레드 차단에 빠집니다. ]

프로세스에 대해 간략히 설명하겠습니다. 소켓 연결이있는 경우 먼저 non-blocking으로 설정 한 다음 코 루틴을 시작하여 링크를 처리하고 코 루틴 호출은이 새로운 연결에서 읽기를 시도합니다. 데이터, 현재 호출 된 읽기가 연결되어 있으므로 눈에 띄지 마십시오 . 네 가지로 작동합니다 .

  • 1.이 읽기 (가짜 몽키 킹)는 현재 코 루틴을 타이머에 등록하여 향후 읽기 시간 초과를 처리합니다.
  • 2. epoll_ctl ()을 호출하여 현재 실행 환경의 epoll 인스턴스에 자신을 등록합니다. 등록 프로세스의 두 단계 모두 콜백 함수를 지정해야하며, 이는 향후 현재 코 루틴을 "깨우기"하는 데 사용됩니다.
  • 3. co_yield_env 함수를 호출하여 CPU 를 포기합니다 .
  • 4. 메인 코 루틴 epoll_wait ()가 읽기 작업의 파일 디스크립터를 읽을 수 있음을 알고 있으면 원래 읽기 코 루틴이 등록한 콜백 을 실행하여 깨 웁니다.
작업 코 루틴은 깨어 난 후 , 원래 glibc 호출 할 교체 후크 내에 있는 진정한 read () 시스템 호출 입니다. 이때 파일 디스크립터 I / O가 준비된 것이 정상적인 epoll_wait 이면 데이터를 읽고 초과 근무이면 -1을 반환 합니다. 간단히 말해, 외부 사용자의 눈에이 read () 는 차단 시스템 호출과 거의 동일한 동작을 보여줍니다.

 

 

 2. 메인 코 루틴과 코 루틴의 "스케줄링"

 메인 코 루틴 [메인 스레드가 co_create와 같은 함수를 호출하는 것과 같은 이유, 프로세스가 저하됨]

컨슈머 프로듀서가 차단되면 CPU 메인 코 루틴에 양보 합니다 .. 현재 메인 코 루틴은 무엇을하나요? 주 코 루틴은 co_eventloop () 함수에서 사용 중입니다. co_eventloop ( 이벤트 루프) 에서 "스케줄러"의 핵심 인 이 co_eventloop ()는 실제로 epoll / kqueue 이벤트 루프 (앞서 설명)를 기반으로하므로 코 루틴 스케줄링이벤트 구동 이 밀접하게 연결되어 있습니다. , 따라서 libco 는 네트워크 라이브러리이기 때문에 코 루틴 라이브러리가 아닙니다 . 배경 서버 프로그램에서 모든 논리는 I / O 네트워크의 주위에 회귀한다 , 와 같은 디자인 libco이 가지고 자신의 합리성을.

모두를위한 그림을 그릴 게

고 루틴 1이 블로킹 수율로 인해 CPU를 메인 스레드로 전달하면 이벤트에 의해 구동되어 어떤 고 루틴에 IO 이벤트가 있는지 확인하고 CPU의 실행 능력은 재개시 응답하는 고 루틴으로 전달됩니다. 실행 후 CPU를 yield의 메인 코 루틴으로 되돌립니다 ( 이 프로세스가 반복됨 ).

 

3. stCoEpoll_t 구조

stCoRoutineEnv_t 구조를 분석 할 때 언급되지 않은 stCoEpoll_t 유형 pEpoll 포인터 멤버 도 있습니다 . 마찬가지로 구성원 stCoRoutineEnv_t , 이러한 구조는 또한 동일한 스레드에 모든 코 루틴에 의해 공유 된 글로벌 리소스이다. stCoEpoll_t epoll 의 이벤트 루프 와 관련되어 있음을 이름에서 볼 수도 있습니다 . 이제 내부 필드를 살펴 봅니다.

 

struct stCoEpoll_t {  
       int iEpollFd; // epoll의 루트 파일 디스크립터는 분명히
       static const int _EPOLL_SIZE = 1024 * 10; // epoll_wait 의 세 번째 매개 변수로 [ epoll_wait가 반환 하는 최대 준비 이벤트 수 ]
       struct stTimeout_t * pTimeout; //  stTimeout_t 유형의 구조 포인터. 구조는 실제로 ⼀ 타임 휠 (타이밍 휠) 타이머 , 이상한 이름입니다.
       struct stTimeoutItemLink_t * pstTimeoutList; //  stTimeoutItemLink_t 유형의 구조에 대한 포인터. 포인터는 실제로 연결된 목록 헤드입니다. 항목 임시 저장 시간 초과 이벤트에 대한 목록입니다 .
       struct stTimeoutItemLink_t * pstActiveList; //  stTimeoutItemLink_t 유형의 구조에 대한 포인터. 또한 연결된 목록을 가리 킵니다. 이 목록은 epoll_wait 준비 이벤트 타이머 시간 초과 이벤트 를 저장하는 데 사용됩니다.
 
       co_epoll_res * result; // epoll_wait () 의 두 번째 매개 변수 , 즉 하나의 epoll_wait 에서 얻은 결과 집합을 캡슐화 합니다 .
};

 

우리는 타이머가 이벤트 중심 모델의 네트워크 프레임 워크에서 없어서는 안될 기능이라는 것을 알고 있습니다. 네트워크 I / O 타임 아웃, 타이밍 대기 (poll 또는 timedwait )를 포함한 타이밍 작업은 모두 이것에 의존합니다.

일반적으로 타이머 기능을 사용할 때는 먼저 타이머에 타이머 이벤트 (Timer Event)를 등록하고, 타이머 이벤트를 등록 할 때는 향후이 이벤트의 트리거 시간을 지정해야합니다. 트리거 시간에 도달하면 타이머 알림을 받게됩니다.

네트워크 프레임 워크의 타이머는 두 부분 으로 구성된 것으로 볼 수 있는데 , 첫 번째 부분은 등록 된 타이머 이벤트 저장하기 위한 데이터 구조 이고 두 번째 부분은 타이밍 알림 메커니즘 입니다.
타이머 의 첫 번째 부분 : 등록 된 타이머 이벤트를 저장 합니다 . 일반적으로 red-black 트리 [libevent의 타이머가 사용하는 바이너리 힙 / 큐] , 예를 들어 nginx ; 또 다른 일반적인 데이터 구조는 libco에서 사용하는 타임 휠입니다. 이 구조 . 물론 연결 목록을 직접 사용하여 달성 할 수도 있지만 시간 복잡성이 상대적으로 높고 타이밍 작업이 많을 때 프레임 워크의 성능 병목 현상이 쉽게 발생합니다.
타이머의 두 번째 부분 : 고정밀 타이밍 (마이크로 초 수준의 정확한) 알림 메커니즘 일반적으로 getitimer / setitimer와 같은 인터페이스가 사용됩니다. 신호를 처리해야하므로 더 문제가 발생합니다. epoll은 타이밍을 위해 libco 내부에서 직접 사용되며 유일한 차이점은 타이머 이벤트를 저장하는 데 시간 휠이 사용된다는 것입니다.

 

추천

출처blog.csdn.net/qq_44065088/article/details/109282354