강좌 & 팁
리눅스에서 어플을 작성하다 보면 스케줄에 대한 궁금증이 생긴다.
그건 이럴것이라고 생각만 하던 것을 테스트해 보기로 했다.
1. 스케줄시간이 1msec 일대 프로세스가 잛은 시간만 일하고 sleep 한다고 하면
대기하던 다른 프로세스가 곧바로 깨어나 작업을 할것인가?
준비물 및 조건
- EZ-S6410 보드
- linux 커널 2.6.29 HZ=1000
- 인터럽트등을 최소화 하기위해 이더넷케이블을 제거하고 이더넷도 내린다.
- 콘솔 인터럽트는 어쩔수 없다.
기초지식
- usleep() 함수를 호출할때 0 을 주면 스케줄 시간만큼 sleep 한다. (즉 1msec)
- usleep() 함수를 호출할때 스케줄시간보다 작을 값을 주면 스케줄 시간만큼 sleep 한다.
테스트 방법
메인 프로그램에서 2개의 쓰레드를 생성한 후 메인은 오랜시간 sleep 시킨다.
두개의 쓰레드를 모두 usleep 0으로 재우고 깨우고를 반복한다.
- usleep() 함수를 호출하기 전의 시간값과 깨어난 후의 시간값을 표출한다.
- 실제 sleep 한 시간을 계산한다.
- 첫번째 쓰레드가 sleep 에 들어간 시간과 두번째 쓰레드가 깨어난 시간을 측정한다.
실험 메세지
[root@falinux ~]$ ./test-schedule 0 0./test-schedule ver 0.1.0 (1)thread usleep( 0) 898548387 |
주황색을 보면 1번 쓰레드가 898548387 시간에 자고 898549079 에 깨어나 실제 sleep 한 시간은
692 usec 로 찍혔다. 이것은 쓰레드 실행을 위해 쓰레드 시작전 시간을 300usec 소모한 이후 쓰레드 함수가
실행되어 실제 쓰레드함수에 주어지 시간이 700usec 정도이기 때문에 이런 작은값이 찍혔다.
이후 이런 값은 나오기는 하지만 일반적인 값을 아니다.
연두색 로그를 보변 실제 sleep 한 시간은 904usec 이다.
이것은 스케줄 전환시간과 시간을 얻고 printf() 함수등을 호출하는 과정등에서 소모된 시간이 있어
실제 1000usec 만큼자지 않는 것이다.
노란색 로그가 실제 우리가 알고자 하는 문제, 즉 하나의 쓰레드(혹은 프로세스)가 자기 시간을
모두 소모하지 않고 sleep 하게 되면 다음 쓰레드(혹은 프로세스)는 언제 깨어나는 것인가를 알려주고 있다.
2번 쓰레드가 sleep 한 시간은 898,551,284 usec 이며 1번 쓰레드가 깨어난 시간은 898,552,059 usec 이다.
두 시간의 차는 775usec 이다.
결 론
다음 쓰레드 혹은 프로세스는 스케줄 시간만큼 대기후 실행된다.
그럼 남아있는 프로세스 시간은 누가 소모할 것인가?
남아 있는 시간은 현재 sleep 한 프로세스에 할당된 상태로 커널의 idle() 함수로 진입하여 이곳에서
실제 물리적인 core sleep 상태로 진입할 것이라고 예상한다.
만일 어플에서 while 등을 통해 busy 한 상태로 모든 시간을 점유한다면 소비전류가 증가하는 것을 볼수있다.
하지만 sleep 상태가 빈번한 경우 소비전류는 현저희 떨어진다.
몇개의 테스트를 진행하여 글을 더 쓰려고 했지만 스크롤 압박으로 실험하나로 끝냈다.,
다음글에 아래의 실험을 진행하고자 한다.
- 500usec, 900usec, 1000usec, 2500usec sleep 할 경우 실제 소모시간
- 하나의 프로세스가 busy 상태일때 다른 프로세스의 진행상태
아래는 실험에 사용된 소스이다.
/** @file main.c @date 2011/01/07 @author 오재경 freefrug@falinux.com FALinux.Co.,Ltd. @brief 리눅스 스케줄 정책에 대해 알아본다. @todo @bug @remark @warning */ //---------------------------------------------------------------------------- #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <fcntl.h> #include <unistd.h> #include <errno.h> #include <signal.h> #include <sys/ioctl.h> #include <sys/time.h> #include <sys/socket.h> #include <sys/types.h> #include <sys/stat.h> #include <sys/poll.h> #include <time.h> #include <pthread.h> struct thr_info { int id; unsigned long sleep_usec; }; unsigned long get_cur_usec(void) { struct timeval mytime; // 현재 시간을 얻어온다. gettimeofday(&mytime, NULL); // 24시간에 대한 usec 만을 돌려준다. return ( (mytime.tv_sec%(24*3600))*1000000 + mytime.tv_usec ); } //------------------------------------------------------------------------------ /** @brief 쓰레드 함수 @remark *///---------------------------------------------------------------------------- static void *thread_sleep_func( struct thr_info *info ) { unsigned long in_usec, out_usec; while(1) { in_usec = get_cur_usec(); printf( " (%d)thread usleep(%4ld) %ld\n", info->id, info->sleep_usec, in_usec ); usleep( info->sleep_usec ); out_usec = get_cur_usec(); printf( ">>(%d)thread wakeup(%4ld) %ld period=%ld\n", info->id, info->sleep_usec, out_usec, out_usec-in_usec ); } free( (void *)info ); return (void *)0; } //------------------------------------------------------------------------------ /** @brief 메인함수이다. @remark *///---------------------------------------------------------------------------- int main( int argc, char **argv ) { int loop; printf( "\n %s ver 0.1.0 \n\n", argv[0]); if ( 3 > argc ) { printf( " %s [usec] [usec] [usec] .... \n\n", argv[0] ); return 0; } for (loop=1; loop<argc; loop++) { pthread_t thr_id; pthread_attr_t thr_attributes; int thr_status; struct thr_info *info; info = (struct thr_info *)malloc( sizeof(struct thr_info) ); info->id = loop; info->sleep_usec = strtoul( argv[loop], NULL, 0 ); // 쓰레드 생성 : 프로세스와 분리 pthread_attr_init( &thr_attributes ); pthread_attr_setdetachstate( &thr_attributes, PTHREAD_CREATE_DETACHED ); thr_status = pthread_create( &thr_id, &thr_attributes, thread_sleep_func, (void *)info ); if ( thr_status != 0 ) { perror( "sleep pthread_create() " ); } } while(1) { sleep(10000); } return 0; }