'KIPA_2006_DeviceDriver'에 해당되는 글 20건

  1. 2006/09/02 게으른 엔지니어 make_request 함수
  2. 2006/09/02 게으른 엔지니어 Block device 프로그래밍시 고려할 종류들...(? 제목이 좀 이상함... ㅋㅋㅋ)
  3. 2006/08/28 게으른 엔지니어 IT SoC 강의장
  4. 2006/08/11 게으른 엔지니어 Linux Kernel 2.6에서 모듈 컴파일 하기 위한 Makefile 작성법
  5. 2006/08/10 게으른 엔지니어 MMU의 존재 이유...
  6. 2006/08/10 게으른 엔지니어 GPIO 에 대한 설명
  7. 2006/08/09 게으른 엔지니어 컴파일러 또는 툴 체인에 대하여...
  8. 2006/08/09 게으른 엔지니어 인터럽트 발생시 할일들
  9. 2006/08/09 게으른 엔지니어 리눅스에서 모듈 개발(2.4와 2.6의 차이)
  10. 2006/08/08 게으른 엔지니어 sys_exit()
#define BSK_SECTOR_SIZE                512 // sector는 한번에 읽고 쓸 수 있는 크기
#define BSK_SIZE                (4*1024*1024) // 전체 크기
#define BSK_SIZE_KB                (BSK_SIZE/1024)
#define BSK_SECTOR_TOTAL        (BSK_SIZE/BSK_SECTOR_SIZE)
#define BSK_AHEAD                2 // 근처에 있는 것도 읽힐 가능성이 높으로므로 미리 읽을 것이 몇개인지 표시

교재 큰 페이지로 76 페이지를 보면, Block device의 경우 5개의 전역 변수를 가지고 있다.


nt bsk_init( void )
{
 int lp;

 printk( "[M] block device driver module is loaded.\n" );
 bsk_major = register_blkdev( 0, "bsk", &bsk_fops );
 printk( "[M] major number is %d\n", bsk_major );

 blk_queue_make_request( BLK_DEFAULT_QUEUE(bsk_major), &bsk_make_request ); // make_request 함수 등록

 read_ahead[bsk_major] = BSK_AHEAD;

 bsk_size = BSK_SIZE_KB;
 blk_size[bsk_major] = &bsk_size; // 항상 포인터로넘겨 줘야 함

 bsk_ramdisk = vmalloc( BSK_SIZE );

 return 0;
}


static int bsk_make_request( request_queue_t *q, int rw, struct buffer_head *sbh )
{
 char *pbsk_data;

 if ( ( (sbh->b_rsector*BSK_SECTOR_SIZE) + sbh->b_size ) >= BSK_SIZE )
   goto fail;

 pbsk_data = bsk_disk + (sbh->b_rsector*BSK_SECTOR_SIZE);

 switch ( rw )
 {
   case READA:
   case READ:
     memcpy( sbh->b_data, pbsk_data, sbh->b_size ); // 실제 하드웨어인 경우 I/O 처리해야 할 부분
     printk( "[M] Read....    " );
     break;

   case WRITE:
     memcpy( pbsk_data, sbh->b_data, sbh->b_size ); // 실제 하드웨어인 경우 I/O 처리해야 할 부분
     mark_buffer_uptodate( sbh, 1 );
     printk( "[M] Write....    " );
     break;

   default:
     goto fail;
 }
크리에이티브 커먼즈 라이센스
Creative Commons License
이올린에 북마크하기
받은 트랙백이 없고, 댓글이 없습니다.

댓글+트랙백 RSS :: http://www.cipher.pe.kr/tt/cipher/rss/response/50

댓글+트랙백 ATOM :: http://www.cipher.pe.kr/tt/cipher/atom/response/50

Block device driver


===================================================================================
1. 장치가 하나인 경우

2. 장치가 여러개인 경우

3. 장치가 여러개이면서 partition이 있는 경우
===================================================================================


===================================================================================
1. make_request
  ==> 속도가 빠른 매체로 즉각 반응

2. request
  ==> 속도가 느린 매체, 즉 하드 디스크 같은 경우, 즉각적인 반응을 하지 않음.
===================================================================================


===================================================================================
* Block device인 경우 read/write의 작업이 언제 끝나는지를 kernel에게 알려 줘야 함. 이럴때 만약 디바이스가 인터럽트를 제공하면 그것을 사용하면 되지만, 그렇지 않을 경우 끝나는 시간을 추즉하여 timer로 알려 줘야 한다. 왜냐하면 작업이 시작되면 blocking 이 되기 때문이다.(이 이유는 좀 더 확실히 확인해야 함.)
1. 디바이스가 인터럽트를 제공하는지 여부

2. 디바이스가 인터럽트를 제공하지 않는 경우
=> timer(시간을 guess한다) 를 통해서 디바이스가 처리를 끝냈음을 kernel에게 알려 줘야함
===================================================================================


매체에 대한 I/O 처리


를 하면 전체적으로 block device에 대한 프로그래밍이 끝나는 경우이다.
크리에이티브 커먼즈 라이센스
Creative Commons License
이올린에 북마크하기
받은 트랙백이 없고, 댓글이 없습니다.

댓글+트랙백 RSS :: http://www.cipher.pe.kr/tt/cipher/rss/response/49

댓글+트랙백 ATOM :: http://www.cipher.pe.kr/tt/cipher/atom/response/49

지난 토요일(8월26일)부터 4주간 토요일마다 교육이 있다.
오금역 4번 출구를 나가면 IT SoC 아카데미라는 곳에서 강의 받았다. 수원에서 보면 거리상 얼마 되지 않는듯 한데... 바로 가는 차편이 없어서 가다보니, 잠실까지 가서 지하철 타고 천호역에서 5호선으로 갈아타서 오금역에 내리는 것이다. 토요일 아침이니 차가 많이 막힐 이유도 없는데... 아침에 7시에 나왔는데, 도착하니 9시였다... 총 교육 시간이 8시간 정도인데, 왕복으로 드는 시간이 총 4시간이니 정말 힘들게 다닌다고 해야 겠지...

교육 내용 자체는 그럭 저럭 괜찮았다. 나야 뭐 디바이스 드라이버 프로그래머가 되기 위해서 듣는게 아니고, 어떤것인지 알기 위해서 듣는 것이니, 충분히 괜찮은 교육이었다. 넘 자주 쉬고 해서 조금 귀찮기도 했지만...

블럭 디바이스가 어떤 것인지 이제 감이 잡히기 시작하는데... 언제 여기에 또 강의 들은 내용과 이해한 내용들을 적을 날이 오겠지... 강의장이 아예 인터넷이 안되서 어떻게 해볼 수가 없다. 그냥 교재에 열심히 적는 수 밖에...
크리에이티브 커먼즈 라이센스
Creative Commons License
이올린에 북마크하기
받은 트랙백이 없고, 댓글이 없습니다.

댓글+트랙백 RSS :: http://www.cipher.pe.kr/tt/cipher/rss/response/44

댓글+트랙백 ATOM :: http://www.cipher.pe.kr/tt/cipher/atom/response/44

2.6에서 모듈을 컴파일 하는 방법이 2.4와 많이 달라 졌다.

2.4에서 sk.c를 sk.o 로 만들기 위한 Makefile
-------------------------------------------------------------------------
CC = gcc
INCLUDEDIR = /usr/include
CFLAGS += -D__KERNEL__ -I$(INCLUDEDIR) -Wall -Wstrict-prototypes\
  -Wno-trigraphs -O2 -fno-strict-aliasing -fno-common\
  -fno-common -pipe -msoft-float -DMODULE

All: sk.o sk_app

sk.o: sk.c
      $(CC) $(CFLAGS) -c -o sk.o sk.c
sk_app: sk_app.c
      $(CC) -o sk_app sk_app.c
clean:
      rm -rf *.o sk_app
-------------------------------------------------------------------------
커널과 관련한 컴파일시에 standard header 파일을 쓰지 않으므로 위와 같이 INCLUDEDIR 와 같이 변수를 지정하여 직접 어디에 있는 header 파일을 가져 와야 하는지 알려 준다.
CC와 CFLAGS는 Makefile 에 지정된 예약어 이다.
CFLAGS에 지정되어야 하는 필수 옵션
1. -DMODULE : -D는 #define을 뜻하는 것이므로 C Pre Processor가 해결하는 부분으므로 소스 안에 위에 #define MODULE 이라고 해도 된다. 소스에 직접 적는 것 보다 Makefile에 적는게 가장 편하다. 스타일 문제...
2. -D__KERNEL__ : 위와 같은 내용이다. __KERNEL__을 define 하는 것이다. 이 부분을 define을 하지 않으면, 커널에서 사용할 수 있는 함수를 사용할 수 없다.
3. -O2 : -O는 최적화를 뜻한다. 2는 최적화 레벨을 뜻한다. 디바이스 드라이버나 커널 컴파일에서 최적화가 뜻하는 것은 인라인 어셈을 확장해서 쓸 수 있다는 뜻이다.
4. -Wall : 경고 메시지를 전부 출력하라는 뜻
5. -I : 사용할 헤더 파일들이 어디 있는지 알려주기 위한 옵션

gcc의 옵션중에 -c 는 object 파일을 만드는 옵션인데, -o 옵션으로 output 파일의 이름을 안 적어 주더라도 자동으로 소스 파일의 이름과 같은 object 파일을 만들어 준다.

gcc -c -o hello.o hello.c

gcc -c hello.c 가 같은 output인 hello.o를 만들어 준다.

$@

$<
기호를 설명해 보자면

sk_app: sk_app.c
      $(CC) -o sk_app sk_app.c

위와 같은 구문이 있으면, sk_app: sk_app.c 에서 sk_app가 target이고 sk_app.c는 dependency를 나타낸다. $<는 dependency 부분에 있는 내용을 대신할 수 있다. $@는 target을 나타낸다. 즉 위 구문을 새로 쓰면
sk_app: sk_app.c
      $(CC) -o $@ $<
로 바꿀 수 있다.
특히나 $< 는 dependency 부분에 파일의 갯수가 많을 경우에 편리하게 사용할 수 있다.

2.6 에서 모듈을 컴파일 하기 위한 Makefile
=========================================================
CC := arm-linux-gcc
KDIR := /root/XHYPER270/kernel/linux-2.6.11-h270-tku_v1.1

TEST_TARGET = test
TEST_OBJS = $(TEST_TARGET).o
TEST_SRC = $(TEST_TARGET).c

obj-m := skeleton.o

build : $(TEST_TARGET)
    make -C $(KDIR) SUBDIRS=`pwd` modules

$(TEST_TARGET) : $(TEST_OBJS)
    $(CC) -o $@ $(TEST_OBJS)

clean :
    rm -rf *.o *.ko *.mod.c
    rm -f $(TEST_TARGET)
=========================================================

2.4와 비교해 보면
----------------------------------------------------------
obj-m := skeleton.o

build : $(TEST_TARGET)
  make -C $(KDIR) SUBDIRS=`pwd` modules
----------------------------------------------------------
부분이 바뀌었다.

커널 컴파일 과정을 보면
1. make menuconfig
2. make zImage
3. make modules
4. make modules_install

위에 따로 적은 부분을 풀이해 보면
obj-m := skeleton.o 는 모듈만 만들겠다는 얘기를 미리 하는 거다.
make -C   make -C $(KDIR) SUBDIRS=`pwd` modules
의 의미는 make에 -C는 change directory를 의미한다. 그러므로 kernel 디렉토리에 있는 Makefile 을 써서 module을 만든다는 얘기이다. 즉 위에 3번 인 make modules를 하는 것과 같은 효과를 나타낸다.
여기서 잊지 말것은 2.6에서 만든 모듈은 .ko로 나온다는 점이다.
크리에이티브 커먼즈 라이센스
Creative Commons License
이올린에 북마크하기
받은 트랙백이 없고, 댓글이 없습니다.

댓글+트랙백 RSS :: http://www.cipher.pe.kr/tt/cipher/rss/response/28

댓글+트랙백 ATOM :: http://www.cipher.pe.kr/tt/cipher/atom/response/28

오늘 받은 강의에 의하면 SoC 내부에 있는 MMU는 Virtual Address를 Physical Address로 바꿔주는 기능을 하는 것이다.

                                Address Bus                          Address Bus
ARM  architecture  =====================> MMU  ====================>  Memory Controller
                                   32 bit                                     26 bit
                                                               변환
                                      Virtual Address  ------> Physical Address
                                   예) 0xf1 00 00 00                  0x10 00 00 00
                                                                            0001 0000 0000 ... (binary)
위의 예에서 Physical address를 binary로 바꾸었을때 31 30 29 28 27 26 등의 6개(위 그림에서 Address Bus로 연결되지 않은 6bit)는 Memory Controller와 연결될때 CSx로 된다. 즉 A26 -> CS0, A27 -> CS1, A28 -> CS2, ... A31 -> CS5로 연결된다.

Memory Controller의 경우 서로 다른 형식의 다양한 메모리를 사용하기 위해서 존재 하는 것이다.
크리에이티브 커먼즈 라이센스
Creative Commons License
이올린에 북마크하기
받은 트랙백이 없고, 댓글이 없습니다.

댓글+트랙백 RSS :: http://www.cipher.pe.kr/tt/cipher/rss/response/25

댓글+트랙백 ATOM :: http://www.cipher.pe.kr/tt/cipher/atom/response/25

GPIO는 General Purpose Input Output의 약자이다.

Intel 에서 판매하는 PXA255의 경우 GPIO의 핀이 총 81개가 있다. 이를 표현하기 위해서 총 81비트가 필요하다.
     GPDR2   GPDR1         GPDR0                      
31 ... 14 ... 0 31 ... 0 31 ...        1      0
     GP80                           GP01  GP00

     GPSR2   GPSR1         GPSR0                      

     GPCR2   GPCR1         GPCR0                      

GAFR2_U       GAFR2_L           GAFR1_U      GAFR1_L          GAFR0_U     GAFR0_L
 
위와 같이 32비트 가 세개가 연속으로 있으면서 제일 오른쪽 부터 번호가 붙는다. 앞에 남겨진 비트는 버린다. 그리고 각각의 32 비트를 위에 처럼 이름지어 GPDR0, GPDR1, GPDR2 로 이름 붙여 놨다.
이와 같은게 위와 같이 이름을 바꾸어서(GPSR?, GPCR? 등) 필요한 갯수 만큼 있다.
MUX의 경우 선택을 하기 위한 신호가 2 비트가 필요하다. 그래서 번호를 붙이기를 각 번호에 2 비트씩을 할당해 놨다. GAFR?_U/L 이 거기에 붙인 이름이다. 똑같이 GP0의 경우 첫번째와 두번째 비트를 합쳐서 부른다.
PXA255에서는 이러한 것들도 레지스터라고 부른다. 실제로 cpu의 아키텍처에서 보면 레지스터의 경우 보통 플립-플롭 회로로 구현한다. 이럴 경우 주소가 필요없이 하드웨어에 직접 억세스 해서 값을 가져오면 되지만, 위에 있는 것을 물리적으로 회로를 구성하면 엄청난 작업이 된다. 이렇게 적는 레지스터가 여기에만 있는게 아니고 수도 없이 나오기 때문이다. PXA255에서는 레지스터라고는 하지만 실제로 하드웨어로 존재하는 레지스터가 아니므로, 메모리상에 특정 주소를 이용해서 사용한다. 그래서 GPIO의 경우 미리 지정된 주소가 있고, 그 주소를 억세스 함으로서 사용할 수 있다.

크리에이티브 커먼즈 라이센스
Creative Commons License
이올린에 북마크하기
받은 트랙백이 없고, 댓글이 없습니다.

댓글+트랙백 RSS :: http://www.cipher.pe.kr/tt/cipher/rss/response/23

댓글+트랙백 ATOM :: http://www.cipher.pe.kr/tt/cipher/atom/response/23

툴 체인(컴파일러)에 대해서 강의를 정리 해 보겠다.

툴 체인(크로스 컴파일러)의 구성 요소
1. Compiler : 확장자(.c, .f90...)에 따라 프로그램이 다르게 불린다, cpp0, cc1
2. Bin Utilities : as, ld
3. Library(Glibc) : libc.so, libc.a
4. Debugger(GDB) -- 옵션

cpp(C PreProcessor) : C 소스를 입력 받아서 C 소스를 아웃풋으로 하는 프로그램, output이 .i 로 나옴

cpp를 사용해 보자.
gcc -E -o hello.i hello.c

위 명령어를 치면 전처리기가 처리한 소스 코드를 볼 수 있다. 문법 검사 같은 것은 없고 전처리기의 치환을 볼 수 있다. 실제로 컴파일 되기 전에 이 소스 코드로 변환되어서 사용된다.

gcc -S hello.c

위 명령어는 어셈블리로 변환된 소스를 보겠다는 내용인데, 여기서 문법 체크를 하게 된다. 위 명령어의 소스는 hello.s 로 변환되어 보여 준다.

이 다음에 어셈블리어가 실제로 기계어로 변환되어 진다.

file 이라는 명령은 실행 파일(object나 executable)이 어떤 형식에 맞게 만들어 졌는지 확인할 수 있다.
file hello
위 명령처럼 사용한다.

nm 이라는 명령은 Symbol table을 보여 준다.
nm hello

as -o hello.o hello.s

이 다음에 ld 가 실제 실행 파일을 만들어 준다.

즉 정리하자면,

1. gcc -E -o hello.i hello.c : 프리프로세서를 통과한 c 소스 코드 보여 줌
2. gcc -S hello.c : hello.c 로부터 어셈블리어를 만들어 줌
3. as -o hello.o hello.s : 어쎔블리어로 목적 코드를 만들 수 있음.
4. gcc -o hello hello.c : 실행 파일을 만든다.

objcopy -S hello 를 사용하면 심볼 데이타를 다 날려 버려서 실행 파일의 크기를 작게 만들 수 있다.
objdump -D hello 를 쓰면 역 어셈블을 볼 수 있다.

3의 과정과 4의 과정에서 나오는 output인 hello.o와 hello 를 file 명령과 nm 명령어로 보면 확실히 다르다는 것을 알 수 있다.


일반적으로 사용하는 메모리 영역을 보면

user application은

0000/0000 에서 0xc000/0000인 3G를 사용할 수 있다. 그리고 그 이후부터 0xffff/ffff 까지 커널이 존재한다. 그러므로 전체적으로 4G를 사용한다.

크리에이티브 커먼즈 라이센스
Creative Commons License
이올린에 북마크하기
받은 트랙백이 없고, 댓글이 없습니다.

댓글+트랙백 RSS :: http://www.cipher.pe.kr/tt/cipher/rss/response/22

댓글+트랙백 ATOM :: http://www.cipher.pe.kr/tt/cipher/atom/response/22

Interrupt 처리

1. 발생
2. 현재 CPU에 있는 명령을 끝까지 수행
3. CPU의 사항(register)을 SP에 저장

4. Interrupt Vector
5. Interrupt Service Routine(ISR)
   --> error 처리

6. SP의 값을 복구

1~3과 6은 CPU가 하는 일이므로 신경 안써도 되지만, 4부터 5까지는 처리해 줘야 함.
크리에이티브 커먼즈 라이센스
Creative Commons License
이올린에 북마크하기
받은 트랙백이 없고, 댓글이 없습니다.

댓글+트랙백 RSS :: http://www.cipher.pe.kr/tt/cipher/rss/response/21

댓글+트랙백 ATOM :: http://www.cipher.pe.kr/tt/cipher/atom/response/21

지금 실습을 하기 위해서 리눅스 상에서 모듈을 개발해서 컴파일 하고 했다.

모듈 프로그래밍에서 주의할 점은
#include <linux/module.h> 의 존재 여부이다.

이 헤더 파일은 모듈에 관련한 매크로 함수를 가지고 있다.

module_init(sk_init);
module_exit(sk_exit);

보통 모듈 프로그래밍을 하게 되면
static int init_module(void);
static void exit_module(void);

두 함수를 꼭 써서 초기화와 종료시 처리해야 할 일을 해야 했다.

위에 있는 <linux/module.h>를 include하면 위에 있는 module_xxxx() 함수로 등록하면 되도록 되어 있다.

2.4와 2.6은 모듈 컴파일 하는 방식이 바뀌었다.

2.6 에서 모듈을 컴파일 하면 확장자가 .ko로 생긴다. 이는 모듈로 컴파일한 파일과 일반적인 .o 파일과 구별이 안되서 2.6부터 모듈의 경우에 컴파일하면 .ko로 바뀌었다.

현재 2.6 에서 모듈을 컴파일 하지 못하고 있다.


2.4와 2.6에서 모듈 만드는 방법은 일단 Makefile 의 문법이 많이 바뀌었다. 그리고 모듈 파일안에 더이상 MOD_INC_USE_COUT와 MOD_DEC_USE_COUNT를 쓸 필요가 없어 졌다.

현재 강사도 정확하게 바뀐 점을 알려 주지 못하고 있다.
동작 되는 코드가 있으니 확인해 봐야 겠다.
동작되는 Makefile 파일을 아래에 적었다.
아래에서는 Hyper270 이라는 Hybus에서 판매하는 교육용 키트에서 돌아가는 모듈을 만드는 Makefile이다. 실제로 동작시켜서 키트에서 돌려 보았음.
skeleton.c 파일을 모듈로 컴파일해야 하고 test.c는 모듈이 제대로 올라 갔는지 확인하는 프로그램이다.

Makefile
=========================================================
CC := arm-linux-gcc
KDIR := /root/XHYPER270/kernel/linux-2.6.11-h270-tku_v1.1

TEST_TARGET = test
TEST_OBJS = $(TEST_TARGET).o
TEST_SRC = $(TEST_TARGET).c

obj-m := skeleton.o

build : $(TEST_TARGET)
    make -C $(KDIR) SUBDIRS=`pwd` modules

$(TEST_TARGET) : $(TEST_OBJS)
    $(CC) -o $@ $(TEST_OBJS)

clean :
    rm -rf *.o *.ko *.mod.c
    rm -f $(TEST_TARGET)
=========================================================

이렇게 현재 Linux에서 크로스 컴파일 한 후 minicom에서 zModem으로 키트로 옮기고,
minicom상에서 즉, 키트에 접속한 상태에서
#insmod skeleton.ko
Major No.를 알려 준다. 이것은 skeleton.c 내부에서 Major No.를 알려 주도록 코드를 만들었음.
#mknod /dev/SKELETON c MajorNo 0
이렇게 해서 사용할 수 있는 파일을 만들고
#./test
하면 올라와 있는 모듈을 테스트 할 수 있다.
테스트가 끝나면
#rmmod skeleton.ko
하면 완전히 모듈이 사라진다.
크리에이티브 커먼즈 라이센스
Creative Commons License
이올린에 북마크하기
받은 트랙백이 없고, 댓글이 없습니다.

댓글+트랙백 RSS :: http://www.cipher.pe.kr/tt/cipher/rss/response/20

댓글+트랙백 ATOM :: http://www.cipher.pe.kr/tt/cipher/atom/response/20

정상 종료와 비정상 종료 두가지가 있다.
이를 위해서 signal을 처리해줘야 한다. 각각의 signal에 따라 처리를 해줘야 된다.

493 asmlinkage long sys_exit(int error_code)
494 {
495         do_exit((error_code&0xff)<<8);
496 }



432 NORET_TYPE void do_exit(long code)
433 {
434         struct task_struct *tsk = current;
435
436         if (in_interrupt())
437 panic("Aiee, killing interrupt handler!");
438         if (!tsk->pid)
439                 panic("Attempted to kill the idle task!");
440         if (tsk->pid == 1)
441                 panic("Attempted to kill init!");
442         tsk->flags |= PF_EXITING;
443         del_timer_sync(&tsk->real_timer);
444
445 fake_volatile:
446 #ifdef CONFIG_BSD_PROCESS_ACCT
447         acct_process(code);
448 #endif
449         __exit_mm(tsk);
450
451         lock_kernel();
452         sem_exit();
453         __exit_files(tsk);
454         __exit_fs(tsk);
455         exit_sighand(tsk);
456         exit_thread();
457
458         if (current->leader)
459                 disassociate_ctty(1);
460
461         put_exec_domain(tsk->exec_domain);
462         if (tsk->binfmt && tsk->binfmt->module)
463                 __MOD_DEC_USE_COUNT(tsk->binfmt->module);
464
465         tsk->exit_code = code;
466         exit_notify();
467         schedule();
468         BUG();
469 /*
470 * In order to get rid of the "volatile function does return" message
471 * I did this little loop that confuses gcc to think do_exit really
472 * is volatile. In fact it's schedule() that is volatile in some
473 * circumstances: when current->state = ZOMBIE, schedule() never
474 * returns.
475 *
476 * In fact the natural way to do all this is to have the label and the
477 * goto right after each other, but I put the fake_volatile label at
478 * the start of the function just in case something /really/ bad
479 * happens, and the schedule returns. This way we can try again. I'm
480 * not paranoid: it's just that everybody is out to get me.
481 */
482         goto fake_volatile;
483 }
크리에이티브 커먼즈 라이센스
Creative Commons License
이올린에 북마크하기
받은 트랙백이 없고, 댓글이 없습니다.

댓글+트랙백 RSS :: http://www.cipher.pe.kr/tt/cipher/rss/response/19

댓글+트랙백 ATOM :: http://www.cipher.pe.kr/tt/cipher/atom/response/19