scheduler 예제코드 질문..

okasira의 이미지

안녕하세요.
[리눅스 커널 내부구조]라는 책을 통하여 리눅스 운영체제, 커널 기초에 대하여 공부하고 있는 대학생입니다.
지금은 이 책의 Chapter 10, 2번째인 scheduler 코드를 보면서 이해하고 있는데요.
사용자가 thread_create()함수를 통해 임의의 쓰레드를 만들고 thread_switch(), schedule()함수등을 통해 context switching을 어플리케이션 레벨에서 구현하는 코드입니다.
코드파일은 글에도 올렸지만 압축파일로 전체내용을 첨부하였습니다. (출처: 교학사 홈페이지)

이 코드내 thread_switch()함수는 inline asembly를 이용해 현재 레지스터 값을 push 및 pop, scheduling하는 함수입니다.
그리고 이 thread_switch()함수를 parent_task()라는 함수에서 SIGUSR1에 대한 handler로 지정하였고(sigaction함수 이용), fork()를 통해 자식프로세스에서
1초마다 부모에게 SIGUSR1 신호를 주게끔하여 context switching을 하도록 구현하였습니다.
핸들러 처리가 끝나고 사용자정의 thread구조체 내에 있는 callback function에 돌아가 그 callback function 처리를 하여 printf()를 하게 되는데,

어떻게 pop이 끝나고 thread_create()의 전달인자인 callback함수가 수행되는지 궁금하여 gcc debugger 프로그램인 gdb를 통해 추적하다가
디버거로 시그널 핸들러처리가 1step씩 추적이 안되어 게시판에 글을 남기게 되었습니다...
어떻게 callback 함수로 넘어가는지 궁금합니다..(현재 running task의 retaddr변수값)
또한 gdb에서 어떻게 설정해야 디버깅 도중 1초마다 signal이 발생하여 handler가 발생해도 handler code를 1step씩 debugging 할수 있는지 알려주시면 감사하겠습니다!

구동은 VMWare로 centos 6.6(32bit)에서 하였습니다.

예제 내 task_struct에 해당하는 사용자정의 구조체

typedef struct task_info_tag {
	unsigned long stack[THREAD_STACKSIZE];
	unsigned long sp;
 
	int task_id;
 
	TaskStatus status;
 
	struct task_info_tag *next;
	struct task_info_tag *prev;
}*TaskInfo;

parent_task()
void parent_task(void *context)
{
	// signal 처리를 위한 정보를 위한 구조체
	struct sigaction act;
	sigset_t masksets;
	pid_t pid;
 
	// signal set 초기화
	sigemptyset( &masksets );
	// signal handler로 thread_switch() 등록
	act.sa_handler = thread_switch;
	act.sa_mask = masksets;
	act.sa_flags = SA_NODEFER;
 
	// signal 수신 때 취할 action 설정
	sigaction( SIGUSR1, &act, NULL );
 
	if( ( pid = fork() ) ==  0 ) {
		while(1) {
			sleep(1);
			kill( getppid(), SIGUSR1 );
		}
	} else{
		while (1) {
			// child_task가 1개 남았을 때, 즉, parent_task만 남았을 때
			if ( gh_sch.child_task == 1 ){
				kill( pid, SIGINT );
				break;
			}
		};
	}
}

thread_create()
TaskInfo thread_create(TaskFunc callback, void *context)
{
	TaskInfo taskinfo;
	// task를 위한 공간 할당
	taskinfo = malloc(sizeof(*taskinfo));
	memset(taskinfo, 0x00, sizeof(*taskinfo));
 
	{
		struct frame *f = (struct frame *)&taskinfo->stack[THREAD_STACKSIZE - sizeof(struct frame)/4];
		// taskinfo로 할당된 공간 중 stack부분 뒤쪽에 frame을 위한 공간으로 할당
		// 이에 task가 수행되면서 stack공간을 활용
		int i;
		for(i = 0; i < THREAD_STACKSIZE; ++i) {	// stack overflow check
			taskinfo->stack[i] = i;
		}
		memset(f, 0, sizeof(struct frame));
		f->retaddr = (unsigned long)callback;
		f->retaddr2 = (unsigned long)thread_kill;
		f->data    = (unsigned long)context;
		taskinfo->sp      = (unsigned long)&f->flags;
		f->ebp     = (unsigned long)&f->eax;
	}
	// task 생성에 따라 gh_sch에 child task가 늘었음을 표시
	gh_sch.child_task ++;
	// gh_sch.child_task 값으로 task_id 할당
	taskinfo->task_id = gh_sch.child_task;		
	// task 생성시 TASK_READY로 상태를 설정함
	taskinfo->status = TASK_READY;				
	// taskinfo구조체들의 linkedlist에 새 thread의 taskinfo 구조체를 삽입
	task_insert(taskinfo);	
 
	return taskinfo;
}

thread_switch()
static unsigned long spsave, sptmp;
void thread_switch()
{
	asm(    "push %%eax\n\t"
		"push %%ebx\n\t"
		"push %%ecx\n\t"
		"push %%edx\n\t"
		"push %%esi\n\t"
		"push %%edi\n\t"
		"push %%ebp\n\t"
		"push %%ebp\n\t"
		"mov %%esp, %0" 
		: "=r" (spsave) 
	);
 
	gh_sch.running_task->sp = spsave;
 
	scheduler();	
	sptmp = gh_sch.running_task->sp;
 
	asm(	"mov %0, %%esp\n\t" 
		"pop %%ebp\n\t"
		"pop %%ebp\n\t"
		"pop %%edi\n\t"
		"pop %%esi\n\t"
		"pop %%edx\n\t"
		"pop %%ecx\n\t"
		"pop %%ebx\n\t"
		"pop %%eax\n\t"
		::"r" (sptmp)
	); //끝나고 현재 running_task에 대한 callback function을 수행하게됩니다. (retaddr1)
}

callback function

void test_func_one(void* context)
{
	int i = 0;
	while (1) 
	{
		i++;
		printf("TASK 1 : %5d\n", i);
		sleep(1);
		if ( i == 15 ){
			break;
		}
	}
}

main함수

// my_scheduler의 main 함수
int main(void )
{
	thread_init();
 
	thread_create(test_func_one, NULL); //첫 인자: callback function
	thread_create(test_func_two, NULL);
	thread_create(test_func_three, NULL);
 
	thread_wait();//parent_task()호출
 
	return 0;
}

cf) Chap10내에 2_Sched 폴더에 가면 헤더파일, make파일, 메인파일, c파일 모두 있습니다.

File attachments: 
첨부파일 크기
Package icon Chap10.zip39.87 KB
bushi의 이미지

[schoi0@sel-schoi0-d2 2_Sched]$ make clean
Clean...
rm -rf main.o schedule.o ./my_scheduler
[schoi0@sel-schoi0-d2 2_Sched]$ make
gcc -I. -I../ -c -o main.o main.c -D_GNU_SOURCE -g -O0 -Wall -m32
gcc -I. -I../ -c -o schedule.o schedule.c -D_GNU_SOURCE -g -O0 -Wall -m32
schedule.c: In function ‘parent_task’:
schedule.c:202:17: warning: assignment from incompatible pointer type [enabled by default]
  act.sa_handler = thread_switch;
                 ^
Linking...
gcc -o my_scheduler  main.o schedule.o   -m32
[schoi0@sel-schoi0-d2 2_Sched]$ 
[schoi0@sel-schoi0-d2 2_Sched]$ gdb my_scheduler 
GNU gdb (GDB) Fedora 7.7.1-21.fc20
Copyright (C) 2014 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.  Type "show copying"
and "show warranty" for details.
This GDB was configured as "x86_64-redhat-linux-gnu".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>.
Find the GDB manual and other documentation resources online at:
<http://www.gnu.org/software/gdb/documentation/>.
For help, type "help".
Type "apropos word" to search for commands related to "word"...
Reading symbols from my_scheduler...done.
(gdb) handle SIGUSR1
Signal        Stop	Print	Pass to program	Description
SIGUSR1       Yes	Yes	Yes		User defined signal 1
(gdb) handle SIGUSR1 nostop
Signal        Stop	Print	Pass to program	Description
SIGUSR1       No	Yes	Yes		User defined signal 1
(gdb) handle SIGUSR1 noprint
Signal        Stop	Print	Pass to program	Description
SIGUSR1       No	No	Yes		User defined signal 1
(gdb) run
Starting program: /home/schoi0/net/sched/2_Sched/my_scheduler 
Detaching after fork from child process 483.
TASK 1 :     1
TASK 1 :     2
			TASK 2 : 510
			TASK 2 : 520
						TASK 3 : 1001
			TASK 2 : 530
TASK 1 :     4
			TASK 2 : 540
TASK 1 :     5
			TASK 2 : 550
						TASK 3 : 1003
TASK 1 :     6
...
...
okasira의 이미지

다운받아 실행해주셨군요! 감사합니다.
이 코드에 따르면 SIGUSR1 시그널에 따른 handler인 thread_switch가 끝난 상태가 inline asembly명령인 pop명령이 마지막일 것입니다.
이 상태에서 어떻게 현재 running_task의 retaddr1으로 넘어가 "TASK 1: ", "TASK 2 : "등이 출력되는지 알고 싶네요.. 궁금증은 여전히 ㅠ..

gdb내 handle 명령과 nostop noprint를 통하여 핸들러가 확인되도 디버깅이 끊기지 않고 결과가 출력되는건 이해했으나
어떻게 해야 gdb내에서 handler함수내 코드(이 경우, thread_switch가 되겠군요)를 1 line씩 실행가능할까요? 그래야 핸들러내에서 레지스터나 변수값의 변화를 순차적으로 추적할 수 있을텐데요...

bushi의 이미지

문서를 보시던가 검색을 조금이라도 하시는 게 좋겠습니다.
전의 것도 그렇고, 구글에서 검색하면 결과물의 상위 세번째 안에 전부 있는 내용들입니다.

(gdb) handle SIGUSR1 nostop
Signal        Stop	Print	Pass to program	Description
SIGUSR1       No	Yes	Yes		User defined signal 1
(gdb) break thread_switch
Breakpoint 1 at 0x8048835: file schedule.c, line 124.
(gdb) run
Starting program: /home/schoi0/net/sched/2_Sched/my_scheduler 
Detaching after fork from child process 2758.
 
Program received signal SIGUSR1, User defined signal 1.
 
Breakpoint 1, thread_switch () at schedule.c:124
124		gh_sch.running_task->sp = spsave;
Missing separate debuginfos, use: debuginfo-install glibc-2.18-19.fc20.i686
(gdb) set print pretty on
(gdb) p gh_sch
$1 = {
  child_task = 4, 
  running_task = 0x804b008, 
  root_task = 0x804b008
}
(gdb) p/x *gh_sch.running_task
$2 = {
  stack = {0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xa, 0xb, 0xc, 
    0xd, 0xe, 0xf, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 
    0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 
    0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, 
    0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 
    0x3e, 0x3f, 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 
    0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 
    0x56, 0x57, 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, 0x60, 0x61, 
    0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 
    0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 
    0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 
    0x86, 0x87, 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f, 0x90, 0x91, 
    0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 
    0x9e, 0x9f, 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 
    0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf, 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 
    0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf, 0xc0, 0xc1, 
    0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7...}, 
  sp = 0x804bfdc, 
  task_id = 0x1, 
  status = 0x0, 
  next = 0x804c020, 
  prev = 0x0
}
(gdb) 

댓글 달기

Filtered HTML

  • 텍스트에 BBCode 태그를 사용할 수 있습니다. URL은 자동으로 링크 됩니다.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>
  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.

BBCode

  • 텍스트에 BBCode 태그를 사용할 수 있습니다. URL은 자동으로 링크 됩니다.
  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param>
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.

Textile

  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • You can use Textile markup to format text.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>

Markdown

  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • Quick Tips:
    • Two or more spaces at a line's end = Line break
    • Double returns = Paragraph
    • *Single asterisks* or _single underscores_ = Emphasis
    • **Double** or __double__ = Strong
    • This is [a link](http://the.link.example.com "The optional title text")
    For complete details on the Markdown syntax, see the Markdown documentation and Markdown Extra documentation for tables, footnotes, and more.
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>

Plain text

  • HTML 태그를 사용할 수 없습니다.
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.
  • 줄과 단락은 자동으로 분리됩니다.
댓글 첨부 파일
이 댓글에 이미지나 파일을 업로드 합니다.
파일 크기는 8 MB보다 작아야 합니다.
허용할 파일 형식: txt pdf doc xls gif jpg jpeg mp3 png rar zip.
CAPTCHA
이것은 자동으로 스팸을 올리는 것을 막기 위해서 제공됩니다.