본문 바로가기

컴퓨터/운영체제(OS)

cpu 스케줄링

요즘의 운영체제는 다중프로그래밍을 지원하지요???
한번에 여러개의 프로세스가 동시에 실행되도록 함으로써 CPU의 노는 시간을
줄이고 자원사용의 효율성을 최대화 시키기 위해서요....

그런데 문제는 언제나 CPU는 한번에 한개의 프로세스밖에 이용할수 없다는 겁니다.
만약 하나의 프로세스만을 지원하는 운영체제라면 하나의 프로세스가 CPU를 사용하다
입출력 요청을 수행한다면 I/O 가 끝날때까지 CPU는 놀 수밖에 없잖아요..
그래서 다중프로그래밍은 이처럼 한 프로세스가 대기 상태에 있을때 그 프로세스한테서
CPU를 뺏어서 다른 프로세스에게 사용하도록 하는 겁니다.

이러한 다중프로그래밍 운영체제에서 CPU를 어떻게 여러개의 프로세스들에게 효율적으로
분배해줄까 하는 문제가 바로 CPU 스케줄링입니다.
CPU 스케줄링을 구현하는 알고리즘은 여러개가 있고요....
자세한 내용은 운영체제 책을 참고하시는게 좋겠네요...


http://www.odb.co.kr/jaryo/os_cpu_schedul.htm



우선, 리눅스의 선점형 멀티태스킹 때문에 사용자 모드의 프로세스가 타이밍을
정확히 제어하는지 보증할 수 없다는 것에 주의합니다.
무엇보다도, 사용자 모드의 프로세스는 멀티 태스킹과 리눅스의 선점적인 특성 때문에
정확한 타이밍을 보장받을 수 없다는 것을 말하고 싶습니다.
님 컴퓨터의 프로세스가 10밀리 초에서 (로드 가 크게 걸리는 시스템에서)
수 초동안 스케쥴링에서 제외될 수 있습니다.
그러나, I/O 포트를 사용하는 대부분의 응용 프로그램에서 이는 별로 문제가
되지 않습니다. 이러한 문제를 최소화 하려면 여러분의 프로세스를 nice 명령어로
높은 우선 순위를 부 여할 수 있는데 이것을 바로 cpu 스케쥴링이라 부릅니다.
CPU는 사용자가 계산을 요구하면 그 작업에 대한 header 를 가지고
작업을 메모리에서 수행합니다.
메모리에서 작업을 하는 동안 사용자가 또다른 요구를 하게 되면
CPU는 우선되는 값을 체크합니다.
나중에 사용자가 요구한 계산값이 우선권이 더 높으면 CPU는 처음에 사용자가 요구한 계산값을
메모리사용량을 줄이고 나중에 요구한 값에 더 많은 메모리 영역을
분배하여 계산을 하도록 합니다.
이렇게 작업영역을 배분해주는 것이 스케줄링이라고 보시면 됩니다.


자세히 설명하자면
일반 사용자 모드의 프로세스를 정확한 타이밍으로 수행시키려 한다면,
사용자 모드의 `리얼 타임' 기능이 지원되어야 합니다. 리눅스 2.x 커널에서는
소프트 리 얼 타임 기능이 지원되어야 합니다
타이밍 호출을 시작해서, 몇초동안 지연하려면, 최선의 선택은 sleep(3)를 사용하는 것입니다.
최소한 수십 초를 지연하려면 (최소 지연 시간이 10 밀리초 정도 될 때),
usleep(3)가 그렇게 동작하게 되죠. 이러한 기능은 CPU에게 다른 프로세스를
수행하도록 하므로, CPU 타임이 낭비되는 일이 없도록 만들어주죠

50밀리초 이하로 지연할 때는 (프로세서나 머신, 시스템 부하에 좌우되지만), 리눅스 스케쥴러는
제어권을 돌려 받기 전에 최소한 10-30 밀리초 정도 소모하기 때문에 CPU를 포기할 수는 없어요.
이러한 이유 때문에, 아주 작은 지연 시간을 둘 때, usleep(3)은 매개변수에 지정한 것 보다
최소 10밀리초 정도 더 지연을 합니다.

짧은 지연 시간을 할당할 경우 (보통 50밀리초 정도 될 것입니다.)
다양하게 쓰일 수 있는 방법은 udelay()를 사용하는 것인데, /usr/include/asm/delay.h (linux/include/asm-i386/delay.h) 에
정의되어 있습니다. udelay()는 매개변수를 하나 만 주었을 때 지연하는데 몇 마이크로 초 정도의 시간이 걸리고, 아무것도 리턴 하지 않습니다. 매개변수에서 지정한 것보다 몇 마이크로초 정도 더 걸릴 수도 있 는데, 이는 얼마나 기다려야 하는지 계산하는 오버헤드에서 비롯된 것입니다.
커널 밖에서 udelay()를 사용하기 위해서, 님은 정확한 값으로 정의한 unsigned long 변수인 loops_per_sec를 필요로 할 것입니다. 제가 아는 한, 이 값을 커널에서 얻는 가장 빠른 길은 /proc/cpuinfo의 BogoMips를 읽어 500000을 곱하는 것입니다.

리눅스 커널 2.0.x 시리즈에서, 새로운 시스템 호출인 nanosleep(2)은
매우 짧은 시간 동안 잠들거나 지연하도록 할 수 있습니다.
이것은 프로세스가 소프트 리얼 타임 스케쥴링(sched_setscheduler(2)을 사용)을 지정한다면
지연 시간이 2 밀리초 이하일 때 udelay(2)를 사용하고,
다른 경우에는 (usleep()와 같이) sleep을 호출합니다. 님께서는 nanosleep()를 사용하기 위해서 loops_per_sec 변수를 필요로 하지는 않을 것인데, 이 시스템 호출이 그 값을
커널에서 가져오기 때문입니다.

수 마이크로초동안 지연하는 또다른 방법은 포트 I/O입니다. 포트 0x80에 몇 바이 트를
읽고 쓰려면 프로세서 종류나 속도에 관계없이 정확히 1 마이크로초 정도 기다려야 합니다.
몇 마이크로초동안 기다리기 위해서 여러 번 호출할 수 있습니다.
이 포트 출력은 표준 머신에 대해서는 심각한 부작용이 없다고 확신합니다.)
(그리고 커널 드라이버도 이를 사용합니다.) 이는 {in|out}[bw]_p()가 지연하는 방법이죠
사실, 대부분의 범위 0-0x3ff번의 포트에서 쓰는 포트 I/O 명령은 거의 정확히
1 마이크로초 정도 점유하므로, 예를 들어 님이 병렬 포트를 정확히 사용하려면
포트에 지연 시간을 주기 위해서 추가로 inb()를 호출합니다.
님이 프로그램이 돌아갈 프로세서의 종류나 클럭 속도를 알고 있다면,
특정 한 어셈블러 명령에서 오는 지연 시간보다 더 짧은 시간을 써넣을(hard-code) 수 있어요
(그렇지만 기억할 것이 있다면 프로세스는 언제나 수행될 수 있으므로, 그 지연 시간은
실제로 더 길어질 수 있다는 것이죠). 아래의 표에서 내부 프로 세서 속도는 클럭 사이클의
수를 결정합니다.
예를 들어, 50 MHz의 프로세서 (486DX-50 또는 486DX2-50)의 클럭 사이클은 1/50000000이다.


--------------------------------------------------------------------------------

명령 i386 클럭 사이클 i486 클럭 사이클
nop 3 1
xchg %ax,%ax 3 3
or %ax,%ax 2 1
mov %ax,%ax 2 1
add %ax,0 2 1


--------------------------------------------------------------------------------
표의 nop와 xchg 명령은 부작용이 없습니다. 나머지 명령은 플래그 레지스터를
변경 할 수 있지만, gcc가 그것을 발견한다고 문제가 되지는 않습니다.
이를 사용하려면, 프로그램에서 asm("명령") 을 호출합니다.
위의 테이블에서 문 법에 있는 명령을 줍니다 여러 명령을 넣으려면asm("명령 ; 명령 ; 명령")
이 됩니다. asm()은 gcc가 인라인 어셈블리로 변환하여 함수 호출 오버헤드가 없습니다.
펜티엄에서, 다음과 같은 C 코드로, 최근에 리부트 하였을 때부터 경과한 클럭 사이클의 수를
얻을 수 있습니다.
extern __inline__ unsigned long long int rdtsc()
{ unsigned long long int x; __asm__ volatile (".byte 0x0f, 0x31" : "=A" (x)); return x; }

인텔 x86아키텍처에서 한 클럭 사이클보다 더 짧은 지연 시간을 내기는 불가능 합니다.

출처 : 한국리눅스 협의회 참조 ( htp://www.kldp,org)


제주삼다수, 2L,... 오뚜기 진라면 매운... 상하목장 유기농 흰... 남양 프렌치카페 카... 고려인삼유통 홍삼 ... 종근당건강 오메가3... 요이치 카링 유무선...