1. 커널 소스를 써야 한다. (최소한 드라이버 모듈)
2. 아래와 같이 File operation만 치환하려고 했는데,
    i_fop가 const로 정의되어 있어서 (linux/fs.h ln642) 에러만 난다.
    code) filp->f_dentry->d_inode->i_fop->llseek = my_llseek;
3. 결국 Block device driver 형태로 만드는 수 밖에 없는데,
    시간을 더 쓸 수가 없어서 이쯤에서 pending. 다음 기회에.
Posted by 배트
,
잊을까봐 기록한다아아

컴파일
    $ gcc -g <source_file> -o <exec_file>
gdb 실행
    $ gdb <source_file>
gdb 종료
    (gdb) quit
도움말
    (gdb) help

프로그램 실행
    (gdb) run <arg> "<arg>"…
프로그램 종료
    (gdb) kill

프로그램 중지
    CTRL+C
프로그램 속행 (재시작인가?)
    (gdb) continue
라인단위 트레이스
    (gdb) next
    (gdb) step
    (gdb) finish
변수 확인
    (gdb) print <var>
변수 변경
    (gdb) set <var> = <value>
    or (gdb) set var <var> = <value>
함수 호출 (실행 스레드가 있을텐데 어떻게?)
    (gdb) call <func_name>(<args>)

콜스택 보기
    (gdb) backtrace
콜스택에서 프레임 선택
    (gdb) frame <frame_num>
콜스택 정보, 변수, 인자 보기
    (gdb) info frame
    (gdb) info locals
    (gdb) info args

브레이크포인트 설정
    (gdb) break <line_num>
    or (gdb) break <source_file>:<line_num>
    or (gdb) break < func_name >
    or (gdb) break <class>::<func_proto>
임시 브레이크 (어떻게 쓰는 건지...)
    (gdb) tbreak
브레이크포인트 보기
    (gdb) info breakpoints
브레이크포인터 비활성화
    (gdb) disable <breakpnt_num>
브레이크포인터 일정 회수 건너뛰기
    (gdb) ignore <breakpnt_num> <number>

변수의 쓰기 Watchpoint 설정 (변수의 스코프에서 브레이크 중)
    (gdb) watch <var>
변수의 읽기 Watchpoint 설정 (변수의 스코프에서 브레이크 중)
    (gdb) rwatch <var>
변수의 읽기/쓰기 Watchpoint 설정 (변수의 스코프에서 브레이크 중)
    (gdb) awatch <var>
Watchpoint 비활성화
    (gdb) disable <watchpoint_num>
    (Watchpoint는 info breakpoints 명령어로 확인 가능)

메모리 확인
    (gdb) x/<FTM> [<address> or <symbol>]
    (<FTM> in s(string), c(character), 4c(4 characters), t(32bits binaries), 3x(24bytes in hex)
레지스터 확인
    (gdb) info registers
크래시 후, 코어 파일 디버그
    $ gdb <coredumped_file>
    (gdb) core core
명령어 단위 트레이스
    (gdb) nexti
    (gdb) stepi
함수 디스어셈블
    (gdb) disassemble [<address> or <symbol>]


참고자료: RMS's gdb Debugger Tutorial, http://www.unknownroad.com/rtfm/gdbtut/gdbtoc.html
Posted by 배트
,
우분투는 다른 배포판 리눅스와는 달리 패키지를 통해 커널 설치가 가능하다.
컴파일 한 후 deb 패키지로 만든 커널 패키지는
  1. 커널의 설치/제거가 용이하고
  2. 배포가 가능하다.

아래는  http://ubuntu.or.kr/viewtopic.php?f=9&t=4543 에 나온 커널 컴파일 방법을
그대로 본인의 VM(VMware 6.x)에서 시연한 것으로, 2.6.28-14-generic에서 2.6.30.4로 업데이트 했다.


미리 해야 할 일
  1. 루트 계정의 패스워드 만들기
    우분투는 처음 설치하면 루트 계정의 패스워드가 없는 상태다.
    아래와 같이 명령을 넣어 루트 계정의 패스워드를 만들자.
    $ sudo passwd root
  2. 설치된 하드웨어와 리눅스 커널 버전 확인
    의례적으로 하는 작업이다.
    혹시나 하드웨어 설정이 잘못될 경우를 대비해 정보를 백업해놓자.
    $ uname -r
    $ lspci
  3. 커널 컴파일에 필요한 보조 패키지 설치
    개발용 리눅스라면 왠만한 패키지들을 설치되어 있을 것이다.
    "이 정도 패키지들은 다 설치되어 있어!"라고 자부하시는 분은
    커널 컴파일 진행 중에 패키지가 없다는 메시지가 나올 때 마다 설치해도 좋다.
    $ sudo apt-get install build-essential bin86 kernel-package wget libncurses5 libncurses5-dev


커널 다운로드
 (아래의 작업들 부터는 루트 권한으로 진행합시다.)

  1. 경로 설정
    $ cd /usr/src
  2. 커널 다운로드
    $ wget http://kernel.org/pub/linux/kernel/v2.6/linux-2.6.30.4.tar.bz2
  3. 커널 압축 풀기
    $ tar -xvjf linux-2.6.30.4.tar.bz2
  4. 심볼릭 링크
    $ ln -s linux-2.6.30.4 linux
    $ cd linux
  5. 커널 패치
    새 패치가 있을 때만 진행한다. *를 그대로 입력하는 사람은 없겠지.
    $ wget http://www.kernel.org/pub/linux/kernel/v2.6/patch-2.6.30.*.bz2
    $ bzcat patch-2.6.30.*.bz2 | patch -p1


커널 컴파일

  1. 커널의 예전 설정 가져오기
    일일이 설정을 해주지 않아도 되는 매우 편리한 작업.
    $ cp /boot/config-`uname -r` .config
    $ make oldconfig
    or
    기본 커널 설정 파일을 생성해 처음부터 일일이 설정해주는 방법도 있다. 물론 비추.
    $ make i386_defconfig or $ make x86_64_defconfig
  2. 커널 설정 조정
    가장 예민한 작업이면서도, 새 커널로 업데이트하는 가장 큰 이유이기도 하다.
    앞의 과정에서 커널의 예전 설정을 그대로 사용하고,
    아주 높은 버전으로 업데이트 하는게 아니라면 크게 손 대지 않아도 된다.
    단, 커널해킹의 커널디버깅을 없애면 커널 용량을 많이 줄일 수 있다.
    $ make menuconfig (터미널 상의 GUI)
    or
    $ make xconfig (GUI with X-Window, QT 패키지 필요)
  3. 컴파일 및 deb 패키지 생성
    필요하다면 컴파일 타겟에 kernel-headers와 linux_source도 추가한다.
    컴파일이 진행되는 화면이 보이면 가슴이 두근두근 할 것이다.
    시간이 매우 오래 걸리므로 수면을 취하거나, 독서를 하거나, 운동을 하고 오는게 바람직하다.
    $ make-kpkg  clean
    $ make-kpkg --initrd --revision=386 kernel_image kernel_headers linux_source modules_image
  4. 패키지 파일 확인
    생성이 안됐으면 담배 한대 피우고 나서 커널 컴파일을 다루는 다른 정보를 찾는게 좋다.
    usr/src/linux-image-2.6.30.4_394_i386.deb
  5. 대망의 새 커널 설치
    이 시점에서 VM을 백업해놓는게 좋다. 손가락 아파서 이 방법까지 적기는 싫다.
    $ cd /usr/src
    $ dpkg -i linux-image-2.6.30.4_394_i386.deb
    몇 가지 작업을 하더니 패키지처럼 간단히 설치가 되었다!
  6. 커널 버전 확인
    재부팅을 해보자.
    패키지로 설치하면 GRUB에 새 커널을 추가하는게 아니라 기존의 커널을 대체한다.
    그러므로 부팅 중에 손댈 작업은 없다.
    $ uname -r
    새로운 버전이 보이는가? 성공이다!
  7. 장치 드라이버 새로 설치
    새 커널에서는 몇몇 장치 드라이버를 새로 설치해야한다.
    패키지로 제공하는 드라이버를 사용할 수 없다고 하는데,
    VMware용 드라이버는 커널 소스에 내장되어 모듈로 컴파일이 되었는지,
    사운드카드 말고는 정상적으로 작동했다.
    새 드라이버가 필요하신 분은 제조사 사이트에서 다운받아 사용받기를 권장한다.


주위에 도움이 되길 바라며...

Posted by 배트
,

프로그램에 두 줄의 함수 호출만 추가하여
std i/o를  redirection하는 함수를 만들려다..
결국 만들었습니다!!
아..재밌는데 더 하긴 귀찮네요.

// redirection_in.cc
// redirection in을 간단히 사용하기 위한 함수를 정의
// 키보드 입력을 파일로 대체!

#include <fcntl.h>
#include <sys/io.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <string.h>
#include <sys/wait.h>

int redirect_original_stdin;

int redirect_stdin_to_file( char * fname )
{
    // stdin을 keyboard에서 read pipe로 대체하는 함수
   
    // 1. pipe 초기화
    // 2. fork로 자식 프로세스 생성
    //    부모 - stdin(키보드)을 복제, stdin을 read pipe로 대체, read pipe 닫기, 0을 리턴
    // 여기서부턴 자식 프로세스만
    // 3. 파일 오픈
    // 4. 파일 내용을 write pipe에 기록
    // 5. 파일 닫고
    // 6. write pipe 닫고
    // 7. 종료(return 말고 exit)
   
    int pid, result;
    int redirect_fdpipe[2];
   
    if( pipe(redirect_fdpipe) == -1 )
        return 0;
       
    pid = fork();
    if( pid>0 )
    {
        close( redirect_fdpipe[1] );
        redirect_original_stdin = dup( STDIN_FILENO );
        dup2( redirect_fdpipe[0], STDIN_FILENO );
        close( redirect_fdpipe[0] );

        return 0;
    }
    else if( pid==0 )
    {
        // 이 자식 프로세스가 입력 파일을 읽어서
        // 부모 프로세스의 stdin에게 전달한다.
       
        int n,i;
        const int BUFFSIZE = 100;
        char buf[BUFFSIZE];
       
        close( redirect_fdpipe[0] );
       
        FILE * file;   

        file = fopen( fname, "r" );
        if( file==NULL )
            exit(0);
       
        i = 0;
        while( (n=fread(buf,1,BUFFSIZE,file))>=0 )
        {
            if( n>0 )
            {
                write( redirect_fdpipe[1], buf, n );
                i = 0;
            }
            else
            {
                if( ++i>10 )
                    break;
            }
        }
       
        close( redirect_fdpipe[1] );
        exit(0);
    }
}

int redirect_stdin_restore()
{
    // stdin을 read pipe에서 keybooard로 복구하는 함수
   
    // 1. 버퍼에 있는 데이터들 전부 처리(fflush)
    // 2. stdin (파이프) 닫기
    // 3. 백업한 stdin의 descriptor을 이용해 stdin을 키보드로 복구(dup2)
   
    int result;
   
    fflush( stdin );    // 이거없음 곤란한 경우 가끔 발생
       
    result = close( STDIN_FILENO );
    if( result<0 )
        return -1;

    result = dup2( redirect_original_stdin, STDIN_FILENO );   
    if( result<0 )
        return -1;
       
    return 0;
}

int main(int argc, char **argv)
{
    int result;
   
    // 예제 1
    // file을 stdin 입력으로-
    result = redirect_stdin_to_file( "input.dat" );
    if( result<0 )
    {
        printf("redirect_stdin_to_file error\n");
        return 0;
    }
   
    // 아래 fork는 redirection이 아니라 서브 프로그램을 실행하기 위한 절차
    int pid = fork();
    if( pid==0 )
    {
        execl( "./sinout", "/sinout", NULL );
        exit(0);
    }
    else if( pid>0 )
    {
        int status;
        wait( &status );
    }
    else
    {
        // error!
    }
   
    result = redirect_stdin_restore();
    if( result<0 )
    {
        printf("redirect_stdin_restore error\n");
        return 0;
    }
   
    return 0;
}

Posted by 배트
,

프로그램에 두 줄의 함수 호출만 추가하여
std i/o를  redirection하는 함수를 만들려다..
결국 만들었습니다!!
아..재밌는데 더 하긴 귀찮네요.

// redirection_out.cc
// redirection out을 간단히 사용하기 위한 함수를 정의

#include <fcntl.h>
#include <sys/io.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <string.h>
#include <sys/wait.h>

int redirect_original_stdout;
FILE * redirect_stream;

int redirect_stdout_to_file( char * fname, int append=0 )
{
    // stdout을 screen에서 file로 대체하는 함수
   
    // 1. 파일 열기(open)
    // 2. stdout의 descriptor 백업(dup)
    // 3. stdout을 file로 대체(dup2)
    // 4. 파일 닫기(close)
   
   
    int result;
    char openflag[5];
   
    if( append )
        strcpy( openflag, "a" );
    else
        strcpy( openflag, "w+" );

    redirect_stream = fopen( fname, openflag );
    if( redirect_stream==NULL )
        return -1;
   
    if( append )
        fseek( redirect_stream, 0, SEEK_END );           
   
    redirect_original_stdout = dup( STDOUT_FILENO );
    if( redirect_original_stdout<0 )
        return -1;
   
    result = dup2( fileno( redirect_stream ), STDOUT_FILENO );
    if( result<0 )
        return -1;
   
    result = fclose( redirect_stream );
    if( result!=0 )
        return -1;
   
    return 0;
}

int redirect_stdout_restore()
{
    // stdout을 file에서 screen으로 복구하는 함수
   
    // 1. 버퍼에 있는 데이터들 모두 처리(fflush)
    // 2. stdout (파일) 닫기
    // 3. 백업한 stdout의 descriptor을 이용해 stdout을 화면으로 복구(dup2)
   
    int result;
   
    fflush( stdout );    // 이거없음 곤란한 경우 가끔 발생
           
    result = close( STDOUT_FILENO );
    if( result<0 )
        return -1;

    result = dup2( redirect_original_stdout, STDOUT_FILENO );   
    if( result<0 )
        return -1;
       
    return 0;
}

int main(int argc, char **argv)
{
    int result;
    int appendmode;
   
    if( argc==2 && atoi(argv[1])==1 )
        appendmode = 1;
    else
        appendmode = 0;

    // 예제 1
    // stdout으로의 출력을 file로-
    result = redirect_stdout_to_file( "output.txt", appendmode );
    if( result<0 )
    {
        printf("redirect_stdout_to_file error\n");
        return 0;
    }
   
    // 파일에 쓰기 1
    printf( "this is file output 1 (buffered)\n" );
   
    // 파일에 쓰기 2
    char * msg = "this is file output 2 (direct)\n";
    write( STDOUT_FILENO, msg, strlen(msg) );
   
    result = redirect_stdout_restore();
    if( result<0 )
    {
        printf("redirect_stdout_restore error\n");
        return 0;
    }
   
    return 0;
}

Posted by 배트
,

dup, dup2

開發 - Computer/Linux 2009. 4. 10. 12:04

문득 궁금해져서...새벽에 삽질했습니다.

부모 프로세스가 stdin또는 stdout을 다른 I/O로 redirection 했다면,
fork로 만든 자식 프로세스나 exec로 실행한 서브 프로그램도
stdin, stdout을 redirection한 I/O를 유지하고 있을까?
-> yes!



// dupout.cc
// 자식/서브 프로세스가 부모 프로세스의 stdin/stdout의 특성을 물려받는지 알아보기 위한 테스트

#include <fcntl.h>
#include <io.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <string.h>
#include <sys/wait.h>

int main(int argc, char **argv)
{
    //int fd = open("flist.cc", O_RDONLY);
    int fd = open( "a.txt", _O_CREAT|_O_RDWR|_O_TRUNC, S_IRWXU|S_IRWXG|S_IRWXO );
    if( fd<=0 )
    {
        char * errmsg = "Cannot created file\n";
        write( STDOUT_FILENO, errmsg, strlen(errmsg) );
        return 0;
    }

    // 예제 1
    // STDOUT_FILENO의 입력을 File로

    // STDOUT_FILENO으로의 입력을 fd로 redirection
    int tempFd = dup(STDOUT_FILENO);
    dup2( fd, STDOUT_FILENO );

    // STDOUT_FILENO에 기록 - fd에 기록된다
    char * msg1 = "this is file output\n";
    write( STDOUT_FILENO, msg1, strlen(msg1) );
   
    // 자식 프로세스 생성
    // fork로 생성한 자식 프로세스가 부모 프로세스의 stdin/stdout 성질을 물려받는가?
    int pid = fork();
    if( pid>0 )
    {
        // parent
        int stat=0;
        wait(&stat);
    }
    else if( pid==0 )
    {
        // child
        char * msgch = "this is message from the child\n";
        write( STDOUT_FILENO, msgch, strlen(msgch) );

        // 한번 더 자식 프로세스 생성, 증손이네ㅋㅋ       
        // execl로 실행한 서브 프로세스가 부모 프로세스의 stdin/stdout 성질을 물려받는가?
        int pid = fork();
        if( pid==0 )
            // Child
            execl( "/bin/ls", "/bin/ls", NULL );
        return 0;
    }
    else
    {
        // error
    }
   
    close( fd );

    // STDOUT_FILENO 복구
    dup2( tempFd, STDOUT_FILENO );
    close( tempFd );
   
    // STDOUT_FILENO에 기록 - 화면에 출력된다
    char * msg2 = "this is screen output\n";
    write( STDOUT_FILENO, msg2, strlen(msg2) );
   
    return 0;
}

Posted by 배트
,