Linux 커 널 분석의 8 - 프로 세 스 스케줄 링 과 프로 세 스 전환 과정

저자: 야 오 카 이 젠
오리지널 작품 전재 출처 를 밝 혀 주 십시오.
MOOC 과정http://mooc.study.163.com/course/USTC-1000029000
1. 프로 세 스 스케줄 링 의 시기
Linux 시스템 에서 프로 세 스 스케줄 링 은 보통 중단 처리 과정 에서 발생 합 니 다 (시계 중단, I / O 중단, 시스템 호출 과 이상 포함). schedule () 시스템 호출 을 직접 호출 하거나 사용자 상태 로 돌아 갈 때 needresched 태그 호출 schedule ().프로 세 스 스케줄 링 은 주동 적 으로 스케줄 링 할 수도 있 고 수 동적 으로 스케줄 링 할 수도 있다.
커 널 스 레 드 (특수 한 프로 세 스, 사용자 상태 없 음) 는 schedule () 를 직접 호출 하여 프로 세 스 전환 을 할 수 있 고 중단 처리 과정 에서 스케줄 링 을 할 수 있 습 니 다. 즉, 커 널 스 레 드 는 특수 한 프로 세 스 로 서 주동 적 으로 스케줄 링 을 할 수도 있 고 수 동적 으로 스케줄 링 을 할 수도 있 습 니 다.사용자 상태 프로 세 스 는 주동 적 인 스케줄 링 을 실현 할 수 없습니다. 커 널 상태 에 빠 진 후의 특정한 시점 을 통 해 스케줄 링 을 할 수 있 습 니 다. 즉, 중단 처리 과정 에서 스케줄 링 을 할 수 있 습 니 다.schedule () 호출 은 다음 과 같 습 니 다.
asmlinkage __visible void __sched schedule(void)
2866{
2867	struct task_struct *tsk = current;
2868
2869	sched_submit_work(tsk);
2870	__schedule();
2871}
그 중schedule () 함수 호출 은 프로 세 스 스케줄 링 의 상세 한 작업 을 수행 합 니 다.
next = pick_next_task(rq, prev);
프로 세 스 를 선택 하여 스케줄 링 합 니 다. 프로 세 스 를 선택 한 스케줄 링 알고리즘 의 실현 은 모두 pick 에 있 습 니 다.next_task 에서 알고리즘 은 여기 서 중요 하지 않 습 니 다. 어떤 알고리즘 이 든 그 역할 은 프로 세 스 를 선택 하여 전환 하 는 것 입 니 다.프로 세 스 를 선택 한 후 프로 세 스 전환 을 진행 합 니 다.
2. 프로 세 스 전환
재schedule () 함수 에서 전환 을 준비 하 는 프로 세 스 를 선택 한 후 context 를 호출 합 니 다.switch () 프로 세 스 컨 텍스트 전환 을 진행 합 니 다.그 실현 은 다음 과 같다.
static inline void
2336context_switch(struct rq *rq, struct task_struct *prev,
2337	       struct task_struct *next)
2338{
2339	struct mm_struct *mm, *oldmm;
2340
2341	prepare_task_switch(rq, prev, next);
2342
2343	mm = next->mm;
2344	oldmm = prev->active_mm;
2345	/*
2346	 * For paravirt, this is coupled with an exit in switch_to to
2347	 * combine the page table reload and the switch backend into
2348	 * one hypercall.
2349	 */
2350	arch_start_context_switch(prev);
2351
2352	if (!mm) {
2353		next->active_mm = oldmm;
2354		atomic_inc(&oldmm->mm_count);
2355		enter_lazy_tlb(oldmm, next);
2356	} else
2357		switch_mm(oldmm, mm, next);
2358
2359	if (!prev->mm) {
2360		prev->active_mm = NULL;
2361		rq->prev_mm = oldmm;
2362	}
2363	/*
2364	 * Since the runqueue lock will be released by the next
2365	 * task (which is an invalid locking op but in the case
2366	 * of the scheduler it's an obvious special-case), so we
2367	 * do an early lockdep release here:
2368	 */
2369	spin_release(&rq->lock.dep_map, 1, _THIS_IP_);
2370
2371	context_tracking_task_switch(prev, next);
2372	/* Here we just switch the register state and the stack. */
2373	switch_to(prev, next, prev);
2374
2375	barrier();
2376	/*
2377	 * this_rq must be evaluated again because prev may have moved
2378	 * CPUs since it called schedule(), thus the 'rq' on its stack
2379	 * frame will be invalid.
2380	 */
2381	finish_task_switch(this_rq(), prev);
2382}
pre 인 자 는 현재 전환 할 프로 세 스 입 니 다. next 인 자 는 전환 할 프로 세 스 를 선택 한 것 입 니 다. 이 함수 에서 switch 를 호출 했 습 니 다.to () 함수 스 택 교체:
#define switch_to(prev, next, last)					\
32do {									\
33	/*								\
34	 * Context-switching clobbers all registers, so we clobber	\
35	 * them explicitly, via unused output variables.		\
36	 * (EAX and EBP is not listed because EBP is saved/restored	\
37	 * explicitly for wchan access and EAX is the return value of	\
38	 * __switch_to())						\
39	 */								\
40	unsigned long ebx, ecx, edx, esi, edi;				\
41									\
42	asm volatile("pushfl
\t" /* save flags */ \ 43 "pushl %%ebp
\t" /* save EBP */ \ 44 "movl %%esp,%[prev_sp]
\t" /* save ESP */ \ 45 "movl %[next_sp],%%esp
\t" /* restore ESP */ \ 46 "movl $1f,%[prev_ip]
\t" /* save EIP */ \ 47 "pushl %[next_ip]
\t" /* restore EIP */ \ 48 __switch_canary \ 49 "jmp __switch_to
" /* regparm call */ \ 50 "1:\t" \ 51 "popl %%ebp
\t" /* restore EBP */ \ 52 "popfl
" /* restore flags */ \ 53 \ 54 /* output parameters */ \ 55 : [prev_sp] "=m" (prev->thread.sp), \ 56 [prev_ip] "=m" (prev->thread.ip), \ 57 "=a" (last), \ 58 \ 59 /* clobbered output registers: */ \ 60 "=b" (ebx), "=c" (ecx), "=d" (edx), \ 61 "=S" (esi), "=D" (edi) \ 62 \ 63 __switch_canary_oparam \ 64 \ 65 /* input parameters: */ \ 66 : [next_sp] "m" (next->thread.sp), \ 67 [next_ip] "m" (next->thread.ip), \ 68 \ 69 /* regparm parameters for __switch_to(): */ \ 70 [prev] "a" (prev), \ 71 [next] "d" (next) \ 72 \ 73 __switch_canary_iparam \ 74 \ 75 : /* reloaded segment registers */ \ 76 "memory"); \ 77} while (0) 78
그 중
"pushfl
\t" /* save flags */ \ 43 "pushl %%ebp
\t" /* save EBP */ \ 44 "movl %%esp,%[prev_sp]
\t" /* save ESP
먼저 전 환 된 프로 세 스 의 CPU 실행 정 보 를 저 장 했 습 니 다. 플래그 비트, 스 택 포인터 와 스 택 포인터 등 이 있 습 니 다. 이 어 전 환 된 프로 세 스 가 전 환 될 때의 실행 주 소 를 eip 로 저장 하고 실행 할 프로 세 스 의 eip 도 초기 화 했 습 니 다.
 "movl $1f,%[prev_ip]
\t" /* save EIP */ \ 47 "pushl %[next_ip]
\t" /* restore EIP */
이 새 프로 세 스 가 전 환 된 프로 세 스 라면 새 프로 세 스 는 레이 블 1 에서 시작 하여 이전에 스 택 에 있 던 포인터 와 플래그 위 치 를 복원 합 니 다.
		     "1:\t"						\
51		     "popl %%ebp
\t" /* restore EBP */ \ 52 "popfl
" /* restore flags */ \ 53 \

총결산
Linux 에서 실행 중인 사용자 상태 프로 세 스 X 가 중단 되 었 습 니 다. 커 널 상태 로 전환 해 야 하기 때문에 사용자 상태의 스 택 을 커 널 스 택 에 누 르 고 이 어 CPU 가 현장 을 저장 해 야 합 니 다.중단 처리 중 또는 중단 되 기 전에 schedule () 을 호출 하면 switchto 가 관건 적 인 프로 세 스 상하 문 전환 을 했 습 니 다.새로운 사용자 상태 프로 세 스 Y 는 상기 어 셈 블 리 코드 의 레이 블 1 에서 시작 합 니 다 (프로 세 스 Y 가 전환 되 었 다 고 가정 합 니 다). 이 어 프로 세 스 Y 는 CPU 현장 을 복원 하고 커 널 스 택 에 저 장 된 정 보 를 사용자 상태 스 택 으로 압축 하여 사용자 상태 프로 세 스 Y 를 계속 실행 합 니 다. 이것 은 Linux 의 일반적인 프로 세 스 스케줄 링 과 전환 메커니즘 입 니 다.
인 터 럽 트 처 리 를 통 해 프로 세 스 스케줄 을 진행 할 때 사용자 상태 프로 세 스 와 커 널 스 레 드 간 에 서로 전환 하고 커 널 스 레 드 간 에 서로 전환 합 니 다.커 널 스 레 드 전환 사이 에 schedule () 을 주동 적 으로 호출 합 니 다. 프로 세 스 컨 텍스트 간 전환 만 있 고 컨 텍스트 전환 을 중단 하지 않 습 니 다.

좋은 웹페이지 즐겨찾기