'김정인'에 해당되는 글 3건

  1. 2010.06.24 Linux Kernel & Device Driver [3] (2)
  2. 2010.06.23 Linux Kernel & Device Driver [2] (4)
  3. 2010.06.22 Linux Kernel & Device Driver [1] (5)

Naver Perl Community & Study Cafe


2010.06.24 11:15

Linux Kernel & Device Driver [3]


드디어 마지막 날입니다.

오늘의 배울 내용은 아래와 같습니다.


, 디바이스 드라이버.

- 파일시스템

(가상파일시스템, 다형성)

- 레이어드 개발법

- 커널 내 시스템 콜 추가

- 커널 모듈 프로그래밍

- 디바이스드라이버 개요

- 인터럽트, 문자 디바이스 드라이버 구현


파일 시스템을 어떤식으로 접근해서 알려주실지 기대가 됩니다^^

대부분 커널 소스를 직접 까면서 보여주시니

단순이 그림이 아닌 눈으로 보이는 부분이 확실하니

휘발성이 아닌 비 휘발성으로 머릿속에 오래 남아 있을 듯 하네요 :)




- cat 명령어 짜보기

1
2
3
4
5
6
7
8
9
10
11
12
#include <unistd.h>
#include <fcntl.h>
int main(void){

	int fd,ret;
        char buff[1024];
	fd = open("cat.c",O_RDONLY);
	while(ret = read(fd, buff, sizeof buff ) )
		write( 1, buff, ret);
	close(fd);
	return 0;
}

원본 소스 :  http://codepad.org/uJ0X8Bfh

원래 cat 은 키보드로 입력되어서 그 값을 출력하는 형태이기 때문에

while(ret = read( fd , buff, sizeof buff ) )
  write( 1, buff, ret);

이 소스에서 fd 가 아닌 키보드( 0 ) 으로 설정을 해야합니다.

그리고 파일을 지정하지말고 인자값으로 받아오기 위해서는

메인함수를 int main( int argc , char argv) 이와 같이 바꿔줘야 하며,

리다이렉션(redirection) 까지 되게 하기 위해서는

dup함수를 사용해야 합니다.

dup 함수는 JoinC에 잘 정의되어 있군요 :)

http://www.joinc.co.kr/modules/moniwiki/wiki.php/Site/system_programing/File/dup




커널에서 파일을 처리하는 구조체를 보면 아래와 같습니다.

http://lxr.linux.no/linux+v2.6.34/include/linux/fdtable.h#L43


/*
* The embedded_fd_set is a small fd_set,
* suitable for most tasks (which open <= BITS_PER_LONG files)
*/
struct embedded_fd_set {
        unsigned long fds_bits[1];
};

struct fdtable {
        unsigned int max_fds;
        struct file ** fd;      /* current fd array */
        fd_set *close_on_exec;
        fd_set *open_fds;
        struct rcu_head rcu;
        struct fdtable *next;
};

/*
 * Open file table structure
 */
struct files_struct {
  /*
   * read mostly part
   */
        atomic_t count;
        struct fdtable *fdt;
        struct fdtable fdtab;
  /*
   * written part on a separate cache line in SMP
   */
        spinlock_t file_lock ____cacheline_aligned_in_smp;
        int next_fd;
        struct embedded_fd_set close_on_exec_init;
        struct embedded_fd_set open_fds_init;
        struct file * fd_array[NR_OPEN_DEFAULT];
};

여기서 중요한건 struct file ** fd 부분입니다.

실제로 파일에 대한 포인터가 여기에 담겨있고 이것을 사용하여 파일을 다룹니다.

위의 소스코드에서 read 함수가 일어났다면 sys_read 함수를 호출하게 되는데

sys_read(3,buff,1024);라고 한다면

결국 2중 포인터인 fd 가 fd[3] 을 호출하게 됩니다.

fd 자체가 file 구조체의 2중 포인터 변수이기 때문에 

file 구조체도 한번 살펴봐야 합니다.

file 구조체는 http://lxr.linux.no/linux+v2.6.34/include/linux/fs.h#L913

여기서 확인할 수 있으며

여기서 가장 중요한 3개의 값만 확인하면 됩니다.


unsigned int            f_flags;   
fmode_t                 f_mode;   // 현재 모드 read 혹은 write 모드
loff_t                  f_pos;         // 파일의 현재 위치

flag 설명을 놓쳤.. ㅡ_ㅠ

그리고 이 구조체의 마지막 에는 inode 값을 가지고 있으며,

이 inode 값은 모두 같은 inode 값을 가지고 있습니다.

inode 구조체에 대한 설명은 인터넷에 많으니 패스~


- Tip 1

파일을 read함수로 읽어올 때 파일의 끝이 EOF 일때를 체크하여 파일을 읽어오는 것이 아닙니다.

거대한 정보 구조체 inode에서 확인해 보면 i_size 라는 값이 있는데

이 값을 이용하여 읽은 사이즈와 전체 사이즈를 계속 비교하면서 그 값이 0이 될때

파일을 그만 읽어야 하는지 판단합니다.



- inode 구조체 안의 i_mode 의 16비트 플래그

16비트의 플래그를 그림과 함께 잘 설명해 주셨습니다.

파일 모드 : 파일과 관계된 접근과 실행 권한을 저장하는 16비트 플래그

비트 내용
12-14 파일 형식(일반, 디렉터리, 문자 또는 블록 특별, 선입선출 파이프)
9-11 실행 플래그
8 소유자 읽기 허가
7 소유자 쓰기 허가
6 소유자 실행 허가
5 그룹 읽기 허가
4 그룹 쓰기 허가
3 그룹 실행 허가
2 다른 사용자 읽기 허가
1 다른 사용자 쓰기 허가
0 다른 사용자 실행 허가

출처 : 위키피디아 - http://ko.wikipedia.org/wiki/%EC%95%84%EC%9D%B4%EB%85%B8%EB%93%9C

직접 파일을 일일이 설정하시면서 설명해 주셨는데

9~11 까지의 실행 플래그는 setuid, setgid , sticky bit 를 의미하며

이 비트를 켜고 끄는 이유에 대해서도 이야기를 들었습니다.




- 디렉토리는 하드링크를 걸수 없다(?)


ls 명령어 중 재귀 호출을 하는 -R 명령을 이용하면

모든 디렉토리의 값들을 다 보여주기 때문에

디렉토리에 하드링크를 걸어버리면 이 옵션에서 무한 반복이 돌게 됩니다.

그래서 디렉토리는 -S 심볼릭 링크를 걸어야 합니다.



마지막 날이라

커널 컴파일을 한 뒤에 부팅까지 하는 부분도 보여주셨는데

역시 어떻게 튈지 모르는 부분이라 조심스럽게 부팅을 시키셨습니다. :)



3일동안의 강의가 끝났습니다.

강사님의 무한한 지식을 확인할 수 있었고,

커널에 대한 기반지식을 가지게 되었습니다.

수업에 월드컵도 있었고,

서울이 연고지가 아니기에

집중력이 좀 덜하지 않나.. 하는 아쉬움이 있네요^^;





강의를 해주신 김정인 강사님께 정말 감사드립니다.

신고
Trackback 0 Comment 2
2010.06.23 10:27

Linux Kernel & Device Driver [2]


교육의 둘째날입니다 :)

photo

사진출처 : http://news.joins.com/article/483/4262483.html?ctg=14



새벽4시쯤 깨어 축구를 시청했는데

보자말자 한골~ 그리고 후반에 또 한골~

아슬했지만 한국이 16강에 가게되어

너무나 좋더군요^^

16강 상대가 우루과이라 할만하다는 생각도 들구요~ ㅋㅋ


하지만 저는 교육을 받으로 왔으니

교육에 관련된 이야기를 또 해보겠습니다.




오늘은 어제 이야기를 들었던 Signal 에 대해서 더 세부적으로 배웠습니다.

햐.. Signal 이 이렇게 많은 기능을 가지고 있다니..

perl 을 하면서도 signal 은 그냥 죽었을 때 다시 처리할때나 쓰는 줄 알았는데

심도있게 알아야 한다는것을 알았습니다.




- 커널 컴파일하는 방법



kernel.org 에서 접속하여 원하는 커널 버전을 다운 받은 후에

Linux 에서 다운을 받도록 합니다.


다운을 받는 방법은 웹으로 받아도 되고 ftp로 받아도 되고 wget으로 받아도 상관이 없습니다 :)

실행환경 Linux 는 CentOS 이고

Kernel 버전은 2.6.29.1 입니다.



압축을 풀어주고 난 후에

심볼링크를 걸어줍니다.



커널 컴파일을 하기 이전에 .config 파일을 설정해 줘야 하는데

이부분에서 사람들이 많이 막힌다고 합니다.

이 파일은 아래 이미지에 따른 경로에 있으며

아래 작업을 안했을 시 커널 컴파일을하더라도 죽어버릴 확률이 있다고 하네요 :)



그 후에 menuconfig 를 해주는데 아래와 같이 하나씩 컴파일이 되고


파란화면의 설정창을 확인할 수 있습니다.


menuconfig 설정 뒤에는 친절하게  아래와 같은 문구가 뜨는데 make 를 하라는 뜻입니다.



make 를 할때는 아래와 같이 입력해주시면 됩니다.


# make

# make modules_install

# make install


일일이 기다렸다 명령어를 입력하기 힘드니
 
;(클론) 을 이용하여 한줄로 쭉~ 써주시면 됩니다.


이제 컴파일이 되며 약 1시간 정도 시간이 소요 됩니다. ( 너무 오래 걸려 ㅡ_ㅠ )


커널 컴파일 할때 중요한점은

용량이 넉넉해야 한다는 점입니다.

Vmware 일 경우에는 적어도 15GB 이상으로 하드용량을 잡아주셔야 합니다.

아닐 경우에는 용량이 부족해서 간단한 파일조차 컴파일이 안되는 경우가 ㅡ_ㅠ







-alarm 함수


alarm 함수를 실습을 했는데

위의 코드는 3초 뒤에 프로그램이 "자명종시계" 라고 출력되며 자동 종료됩니다.


위의 코드를 이용하여 알람을 이용하여 3초 미만으로 입력되면 입력된 값이 출력되고

3초 이상 입력하지 않으면 자동 종료되는 코드입니다.

여기서 alarm(0) 의 리턴값에 따라 입력시간이 얼마나 남았는지도 확인 할 수 있습니다.

3번 입력받는 코드를 예제로 다시한번 완성해 보겠습니다.



위의 예제를 실행하면 아래와 같으며 남은 시간이 나오고

타임아웃이 걸리면 자동으로 종료됩니다.



그런데 위에서 exit(0) 으로 프로그램을 종료해버리면 너무 각박해 보임으로

다음으로 넘어가게 만들어야 합니다.

이때 goto 문을 쓰면 되지 않으므로 이때 setjump 를 사용하여 코드를 작성합니다.

아래는 setjump 의 코드 예제입니다.



윈도우에서 try catch 와 비슷한 예제라고 생각하면 되고,

longjmp 가 예외를 일으키는 부분입니다.

재밌는것을 많이 배우네요~


이제 이 소스와 위의 alarm 예제를 합쳐서 입력을 받을 때 타임아웃이 걸리면

예외사항으로 넘어가게 할 수 있다는 것을 알 수 있습니다.




실전 프로젝트에서 예제처리에서 이렇게 타임아웃을 넣을 곳이 많은데

이런식으로 setjump 를 이용합니다.



한번에 알아들어야 하고, 강사님이 코딩속도가 너무 빠르신지라

수업 따라가기가 쉽지많은 않네요

( 실습시간도 마땅히 없고 ㅡ_ㅠ )

이렇게 간단히 정리해두고 다음 기회에 확실하게 다시 실습해보는게 좋을 듯합니다.




신고
Trackback 0 Comment 4
2010.06.22 10:20

Linux Kernel & Device Driver [1]



운이좋게도 Linux Kernel 교육을 신청했는데 제가 담첨이 되어

2박3일간 리눅스 커널 & 디바이스 드라이버 교육을 받을 수 있게 되었습니다.


교육장소는 아이오 교육센터 ( 서울시 금천구 가산동 )에 위치하여 있으며,

김정인 강사 (아이오 교육센터 원장, 아임구루 대표이사 love1770@empal.com ) 님께

교육을 받을 수 있었습니다.



아래는 첫째날의 교육내용입니다.


6/22일(화)

실습환경구축
,

커널 구조 분석

- 리눅스 커널 개요

- 커널 자료구조 접근

- 커널모드와 유저모드

- 부팅과정 및 초기화 코드 이해

 

프로세스 관리

- 멀티테스킹 기본 원리 이해

- 프로세스 컨텍스트 스위칭

- Kernel 2.6  O(1) 스케줄링 분석

- 프로세스 생성, 실행, 소멸 과정 이해



PPT 의 경우에는 회사내부의 자료같아 올리지 않고 간간히 실습 화면을 올려서 포스팅 하겠습니다.

실시간으로 정리한 내용을 올리므로 내용에 짜임새가 부족할 것 같습니다^^; ( 양해 부탁드려요~ )




출처 : http://www.rwc.uc.edu/thomas/Intro_Unix_Text/OS_Organization.html


첫번째 시간에는 커널 구조 분석에 대해 설명을 들었습니다.


UNIX Operating System 에 대하여 이론적으로 먼저 설명을 들었는데,

Operating System의 단편화된 이미지를 양파껍데기에 비유한 표현이 재미있었습니다 :)


UNIX 의 History 역사도 간단히 설명해 주셨는데

BSD 나 AT&T System V 사에 관련된 역사 이야기도 해주셨습니다.

(이런 내용은 어디에서 읽을 수 있을까요? ㅋ)



아래는 강의 중 유용한 정보들을 정리해 보았습니다.


- 리눅스의 오픈소스


기업에서 오픈소스를 쓸 때, 오픈소스를 사용했다고 기재해야하며

오픈소스를 쓴 부분에 대해서 기업도 소스를 공개해야 한다는 부분을

설명해 주셨습니다.

기업에서 오픈소스라고 함부러 썼다가 고소당하기도 합니다.


그러므로 기업에서는 오픈소스를 쓰면서 회사의 내부의 기술을 지켜내야 함으로

오픈소스 팀이 따로 있다네요... ^^;;




- 프로그램과 프로세서의 차이


프로세서 :  메모리에 할당해서 운영되고 있는 프로그램을 의미.

프로그램 :  컴파일되어 실행할 수 있도록 만들어진 파일.




fork() 함수와 스레드의 차이점을 설명해 주셨습니다.

정리하려니 블로그에 정리 된 내용들이 많네요^^;

fork는 자식 프로세서를 하나 더 만들어 주는것이고

스레드와 프로세스의 차이는 전역변수의 공유 유무가 가장 중요한것을 짚고 넘어가야 합니다. :)





출처 : http://www.joong.org/?cat=7




Process State Transition 에 대해서 자세하게 설명을 해 주셨습니다.

중요한 부분이라 그런지 생성되는 시뮬레이션을 통하여 예를들어 설명을 들었습니다.



운영체제에 대해서 재밌는 이야기를 하셨는데

윈도우가 가려지고 다시 보여질때 재빨리 다시 그려주는 것과,

그리고 지속적으로 반복시키면서 멀티 테스킹처럼 속이는 것 등등

"사용자의 편의성을 위해 얼마나 사기를 잘 때리느냐"가 중요하다고 이야기 하셨습니다. ㅋㅋ




윈도우 운영체제를 열심히 공부했던 기억이 있어 

리눅스 커널이 생소하고 궁금했는데, 막상 수업을 들었을때는

구조가 비슷해서 그런지 쉽게 이해가 잘 갔습니다. (내공 쌓은 보람이 있는 ㅡ_ㅠ)



강의중에 실습을 하나 하게 되었습니다.




- 초강력 프로세서 만들기

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#include <unistd.h>

void my_sig(int signo)
{
// printf("CTRL+C\n");
 printf("signal %d",signo);
}
int main()
{
    signal(SIGINT,my_sig);
    signal(SIGQUIT,my_sig);
    signal(SIGKILL,my_sig);
    while(1){
       pause();
    }
    return 0;
}

이 코드의 경우에는

kill -2 같은 경우에도 안죽고

이럴 경우에는 kill -9로 죽여야 가능합니다.



완성된 코드를 처음부터 보여주는 것 보다

프로그램을 짜는 과정을 보여주면서 재밌게 설명해 주셨습니다.



커널 컴파일에 대해 설명하기 위해,

기초부터 차근차근 gcc로 컴파일 하는 과정을 설명해 주시는데

너무나 깊고 자세하게 설명을 해주셔서

녹화를 하고 싶다는 생각이 들더군요.

gcc 에 대한 옵션들도 모르는 부분이 많다는것을 느꼈습니다.



-execlp 함수

1
2
3
4
5
6
7
8
9
10
#include <stdio.h>
#include <unistd.h>
int main(){
   if( fork() == 0 ){
     execlp( "ls", "ls" ,NULL );
   } 
   wait(0);
   printf(" > ");
   return 0;
}


system 함수는 커널에서는 사용할 수 없는 함수이기 때문에,

실제로는 execlp 라는 함수를 이용하여 코드를 작성해야 합니다.

fork 는 분신술에 해당한다면 execlp는 둔갑술에 해당됩니다.

그래서 fork() 함수를 사용하여 0일때 execlp 함수를 사용해주며

wait(0) 함수를 써서 프롬프트가 나중에 출력되도록 설정해 줘야 합니다.



밑의 링크에 execlp 타입이 잘 정리되어 있네요 :)

Link : http://forum.falinux.com/zbxe/?document_srl=408557



리눅스 커널을 분석하기에 좋은 사이트입니다.

http://lxr.linux.no/

노르웨이의 사이트인데 커널이 업데이트될 때마다 사이트도 같이 업데이트 되는데

여기서 소스코드들을 쉽게 확인이 가능하더군요.

실제 커널의 스케줄 소스들을 가지고 분석을 해 주셨는데,

지인분이 곁들여 설명하면 이부분이

리눅스 코드중 가장 지저분한 코드라고 합니다 ( goto문도 많이 보이고 ㅋㅋ)



- 좀비 프로세서의 예

http://codepad.org/sgr1No1r

 

#include <stdio.h>
#include <unistd.h>
#include <signal.h>
#include <sys/wait.h>
int count = 0;
void my_sig(int signo){
  int status =0;
  while( waitpid(-1 , &status , WNOHANG )  > 0){  
    if( (status & 0x7f) == 0){
       printf(" 정상종료 %d \n",(status>>8)&0xff );
    }else if( (status & 0xff00) == 0 ){
      printf("비정상종료 %d \n",(status>>8)&0xff );
    }
    printf("count = %d \n",++count); // 올바르게 죽는지 Check
  }
}

int main(void){
 int i;
 signal(SIGCHLD, my_sig);
 for(i=0;i<1000;i++)
 {
  if( fork() == 0){
    sleep(3);
    aboart();
    // exit(7)    // exit_code = 7;
  }  
 }
  while(1){
    printf(".\n");
    sleep(1);
 }  
}


자식은 죽었는데 부모가 살아 있을경우 좀비 프로세스가 발생되게 됩니다.

그래서 wait() 함수를 적절히 써야 하는데

자식의 exit 코드를 확인 할 수 없기 때문에,

wait 함수에서 reference 로 변수를 넘겨주면 자식의 exit 코드를 확인 할수 있습니다.

그런데 실제로 exit 값을 확인해보면 하위 비트가 아닌 상위비트로 출력되기 때문에

(status>>8)&0xff  이 코드를 통해 추출을 해주어 출력해 줍니다.

이 코드에 따라 정상 종료와 비정상 종료를 추출 할 수 있습니다.

while( waitpid(-1 , &status , WNOHANG )  > 0)

모든 wait 된 status를 확인하여 자식이 죽을 때 좀비를 생성시키지 않는 노하우 가 필요합니다.

이는 고성능 서버에서 꼭 필요한 개념입죠 :)


- manpage Tip


강사님이 시스템 콜에 관련된 Man 페이지를 Vi에서 계속 쉽게 접근 하셨는데

어떻게 하셨는지 물어보니 해답이 2K 였습니다.

2번이 시스템 콜이고 K가 Man페이지를 보는 방법이라

Vi 에서 쉽게 슥삭슥삭하면서 강의하면서 man 페이지를 오갔습니다.

멋지네요 :)




리눅스에 커널에 메모리 부분에서

자료구조가 굉장히 많이 들어가는것을 확인 할 수 있엇습니다.

레드 블랙 트리라던지 , 링크드 리스트 등등

커널 분석을 할때, 기초가 잘혀 있는것이 중요하다 라는것을 새삼스레 느끼게 되네요.



- list_entry 함수


#define list_entry(ptr, type, member) \
          ((type *)((char *)(ptr)-(unsigned long)(&((type *)0)->member)))


http://lxr.linux.no/linux-old+v2.4.31/include/linux/list.h#L187

커널에 list_entry 라는 매크로 함수가 있는데

이는 구조체의 값에 따라 그 값을 확인할 수 있는 함수입니다.

이 커널 소스가 왜 이렇게 만들어 졌으며,

왜 이렇게 접근해야 하는지 자세하게 설명을 해 주셨습니다.

막상 위의 소스만 봐서는 쉣뜨;;;


- do_timer


void do_timer(struct pt_regs *regs)
{
       (*(unsigned long *)&jiffies)++;
#ifndef CONFIG_SMP
        /* SMP process accounting uses the local APIC timer */

        update_process_times(user_mode(regs));
#endif
        mark_bh(TIMER_BH);
        if (TQ_ACTIVE(tq_timer))
                mark_bh(TQUEUE_BH);
}


일반적인 시간은 1990년 1월 1일부터 현재까지의 초를 의미하는데

jiffies 같은 경우에는 시스템에 돌아가는 세부적인 시간을 의미합니다.



- update_process_times

위의 코드에서 함수를 따라가면 아래와 같은 코드가 나옵니다.


void update_process_times(int user_tick)
{
        struct task_struct *p = current;
        int cpu = smp_processor_id(), system = user_tick ^ 1;
        update_one_process(p, user_tick, system, cpu);
        if (p->pid) {
                if (--p->counter <= 0) {
                        p->counter = 0;
                        /*
                         * SCHED_FIFO is priority preemption, so this is
                         * not the place to decide whether to reschedule a
                         * SCHED_FIFO task or not - Bhavesh Davda
                         */
                        if (p->policy != SCHED_FIFO) {
                                p->need_resched = 1;
                        }
                }
                if (p->nice > 0)
                        kstat.per_cpu_nice[cpu] += user_tick;
                else
                        kstat.per_cpu_user[cpu] += user_tick;
                kstat.per_cpu_system[cpu] += system;
        } else if (local_bh_count(cpu) || local_irq_count(cpu) > 1)
                kstat.per_cpu_system[cpu] += system;
}

p->count 를 점점 감소시키면서 다 감소 되었을 때 우선순위를 높이는 코드입니다.

책에서 개념만 보던것이 실제로 소스코드를 보면서 확인하니

이해가 확확 되는군요 :)



이후로는 커널의 소스를 계속적으로 분석하면서

메모리 관리가 어떻게 이루어졌는지 설명 하였습니다.

그리고 어셈까지 분석을 해주시며 친절하게 설명을 해 주셨습니다.



실시간 정리를 하려고 했는데

수업내용이 점점 빨라져서 여기까지만 적겠습니다. ㅡ_ㅠ


두서없이 정리를 했는데...

강의는 정말로 좋았습니다.


하지만 중간중간에 실습이 있었으면 하는 아쉬움이 있습니다.

머릿속으로 생각할 시간이 좀 필요했는데...

슉슉~ 지나가버리니 나중에는 지쳐버리는 사람이 많더군요.



이제 1일차! 2일차에는 실습을 좀 할듯 합니다 :)

아자아자 화이팅!


 

신고
Trackback 0 Comment 5