윈도우 환경에서 사용 가능한 몇가지 hex editor가 존재하듯 유닉스/리눅스 환경에서도 몇가지 방법으로 바이너리 파일을 hex 모드로 읽고 편집할 수 있다. emacs가 이를 지원하는 대표적인 유닉스 환경에서의 에디터이지만, 여기서는 다른 방법을 소개한다.
먼저 바이너리 파일을 hex 모드로 읽기만 하는 경우에는 xdd나 od를 사용하면 된다.
xxd -g1 filename
or
od -tx1 -Ax filename
두 프로그램은 거의 동일한 포맷으로 출력을 보여주지만 od가 좀 더 포맷을 자유롭게 지정할 수 있다. -tx1는 type을 16진수로 해서 1바이트 단위로 표시하라는 뜻이며, -Ax는 Address표시를 16진수로 지정하는 옵션으로 위 예와같이 데이터와 어드레스의 표시 형식을 서로 다르게 지정할 수도 있다. od는 octal dump의 약자로 옵션을 주지 않을 경우 8진수로 출력된다.
od가 여러가지 타입으로 출력을 지원하는 것과 달리 xxd는 오직 16진수 형식만 지원한다. -g옵션으로 출력 바이트 단위를 지정해줄 수 있으며 기본값은 2바이트 단위이다.
바이너리 파일을 수정하기 위해서는 vi와 xxd를 함께 사용하면 된다. xxd는 -r 옵션을 제공하는데 이는 reverse 기능으로, 원래 xxd가 바이너리 파일을 16진수 텍스트 형태로 변경해주는 프로그램인데 -r옵션을 사용하여 실행하면 16진수 텍스트를 그대로 다시 바이너리로 변경해 주게 된다. 따라서 바이너리 파일을 헥사 에디팅하려면 vi에서 xxd를 사용하여 바이너리를 16진수 텍스트 형태로 바꾸에 표시하여 주고, 이를 수정한 후 xxd -r을 사용하여 다시 바이너리 형태로 바꾸어준 다음 저장하면 된다.
먼저 vi 실행 시 -b옵션을 사용하여 수정하고자 하는 바이너리 파일은 연다. 이 옵션을 사용하게 되면 vi에서 binary 파일을 열 때 문제를 야기할 수 있는 여러가지 vi 기능들이 모두 turn off 된다.
vi -b filename
이후 아래와 같이 파일을 hex 포맷으로 변환한다.
:%!xxd
이제 헥사 포맷으로 변환된 텍스트를 수정한다. 수정시에는 데이터를 insert나 delete하면 안된다. (아쉬운 제약 사항이지만 어쩔 수 없다.) 따라서 r 명령을 사용하여 데이터를 원하는 값으로 한자씩 수정하는 것이 좋다. 수정이 끝나면 아래 명령으로 이를 다시 바이너리 데이터로 바꾸면 된다.
:%!xxd -r
-r 옵션으로 xxd를 실행시에는 16진수로 표시되는 데이터 영역의 값들만 참조되기 때문에 vi에서 16진수 데이터영역 외에 주소 영역이나 아스키로 표시되는 영역을 수정하더라도 이는 무시된다.
이제 데이터를 저장하고 나가면 된다.
:wq
수정사항을 xxd나 od로 확인해 볼 수 있다.
od -tx1 -Ax filename more +20 -10
2009년 1월 21일 수요일
라이브러리 이해하기 1.
유닉스나 리눅스 환경에서 라이브러리는 크게 세가지로 나눌 수 있다.
- 정적 라이브러리
- 공유 라이브러리
- 동적 적재 라이브러리
정적 라이브러리는 .a로 끝나는 파일로 빌드 시 실행 파일에 포함되게 된다. 정적 라이브러리를 사용하는 프로그램을 만들기 위해서는 해당 라이브러리가 extern하는 함수가 선언된 헤더 파일과 해당 라이브러리가 필요하다.
공유 라이브러리는 .so로 끝나는 파일로 빌드 시 실행 파일에 포함되지 않는다. 따라서 여러 프로그램이 공유하는 기능을 공유 라이브러리로 만들 경우 디스크 공간을 아낄수 있고 공유 라이브러리에 버그가 존재할 경우 공유 라이브러리만 재배포하여 문제를 수정할 수 있다. 또한 공유 라이브러리가 이를 사용하는 프로세스에 의해 메모리에 로드되면, 해당 공유 라이브러리를 사용하는 또다른 프로세스가 실행되었을 때 이전 프로세스가 로딩해 놓은 내용을 공유하여 사용하기 때문에 메모리 사용량 또한 절약된다.
공유 라이브러리를 사용하는 프로그램을 만들기 위해서는 해당 라이브러리가 extern하는 함수가 선언된 헤더 파일과 해당 공유 라이브러리가 필요하다. 공유 라이브러리는 실행 파일이 실제로 시스템에서 실행되는 시점에 프로그램 링커 로더가 /lib, /usr/lib, 그리고 LD_LIBRARY_PATH등에서 해당 파일의 존재유무를 확인하고 이를 사용되게 된다. 공유 라이브러리는 앞서 말한바와 같이 링크되는 시점(해당 공유 라이브러리를 사용하는 어플리케이션을 빌드하는 시점)에 공유 라이브러리의 헤더와 라이브러리( .so)파일이 필요하다. 왜 실행파일에 포함되지 않는 라이브러리 파일이 필요한 것인까? 이는 빌드되는 어플리케이션이 실행될 때 찾아야 할 라이브러리의 이름이 공유 라이브러리 파일 안에 적혀있기 때문인데 이를 soname이라고 하고 이 soname은 라이브러리의 파일명하고 다를 수 있으므로 서로 구분하여야 한다. 공유 라이브러리를 컴파일할 때에는 아래와 같이 –fPIC (Position Independent Code)옵션이 필요하다.
gcc –fPIC –c MyLibrary.c
gcc –shared –Wl,-soname libMyLibrary.so.1 –o libMyLibrary.so.1.2.3 MyLibrary.o
동적 적재 라이브러리는 실행 중에 동적으로 로딩되는 라이브러리며, 프로그램 실행시가 아닌 해당 라이브러리에 포함된 함수가 사용되는 시점에 불려와 사용되게 된다. 따라서 동적 라이브러리를 사용하는 프로그램은 첫 실행이 정적 혹은 공유 라이브러리를 사용할 때 보다 빠르다는 장점이 있다. 또한 동적 적재 라이브러리는 실행 시 동적으로 라이브러리에서 직접 정보를 얻어와 실행한다. 따라서 동적 적재 라이브러리를 사용한 어플리케이션을 빌드하는 시점에서는 해당 라이브러리에 대한 헤더 파일이나 심지어 해당 라이브러리 자체도 필요 없다. 리눅스에서는 일반 공유 라이브러리를 동적으로 적재가 가능하므로 공유 라이브러리와 동적 적재 라이브러리가 같다고 할 수 있다. 다만 이를 사용하는 방법으로 구분될 뿐이다. 즉 so파일을 공유 라이브러리도 사용할 수도 동적 적재 라이브러리로 사용할 수도 있으며 동적 적재 라이브러리로 사용하려면 라이브러리를 사용하고자 하는 프로그램에서 dlopen, dlsym등을 사용하여 원하는 심볼을 불러오도록 처리하면 된다.
대부분의 unix/linux 시스템에서 LD_LIBRARY_PATH가 사용 가능하지만 모든 시스템에서 그런 것은 아니다. 리눅스에서는 LD_LIBRARY_PATH 사용이 가능하지만, /etc/ld.so.conf 파일과 ldconfig을 사용하여 /etc/ld.so.cache 파일을 생성하는 방법이 더 권장된다.
또다른 방법으로는 rpath를 지정하는 방법이 있다. rpath는 실행 파일을 compile할때 지정해 주면 된다.
gcc -Wall -o myexefile -Wl,rpath,. main.o -L. -lMyLibrary
위 예제에서는 rpath를 현재 디렉토리 [.]로 설정하였으며 이는 실행할 때 MyLibrary를 찾는 위치에 현재 디렉토리를 추가한 것이다. -L옵션은 링크시에 MyLibrary를 찾는 위치를 지정하는 옵션으로 역시 [.] 현재 디렉토리로 지정하였다. 굳이 -l 옵션으로 라이브러리를 지정할 필요 없이 경로를 포함한 파일 이름을 사용하여도 된다.
gcc -Wall -o myexefile -Wl,rpath,. main.o ./libMyLibrary.so.1.2.3
- 정적 라이브러리
- 공유 라이브러리
- 동적 적재 라이브러리
정적 라이브러리는 .a로 끝나는 파일로 빌드 시 실행 파일에 포함되게 된다. 정적 라이브러리를 사용하는 프로그램을 만들기 위해서는 해당 라이브러리가 extern하는 함수가 선언된 헤더 파일과 해당 라이브러리가 필요하다.
공유 라이브러리는 .so로 끝나는 파일로 빌드 시 실행 파일에 포함되지 않는다. 따라서 여러 프로그램이 공유하는 기능을 공유 라이브러리로 만들 경우 디스크 공간을 아낄수 있고 공유 라이브러리에 버그가 존재할 경우 공유 라이브러리만 재배포하여 문제를 수정할 수 있다. 또한 공유 라이브러리가 이를 사용하는 프로세스에 의해 메모리에 로드되면, 해당 공유 라이브러리를 사용하는 또다른 프로세스가 실행되었을 때 이전 프로세스가 로딩해 놓은 내용을 공유하여 사용하기 때문에 메모리 사용량 또한 절약된다.
공유 라이브러리를 사용하는 프로그램을 만들기 위해서는 해당 라이브러리가 extern하는 함수가 선언된 헤더 파일과 해당 공유 라이브러리가 필요하다. 공유 라이브러리는 실행 파일이 실제로 시스템에서 실행되는 시점에 프로그램 링커 로더가 /lib, /usr/lib, 그리고 LD_LIBRARY_PATH등에서 해당 파일의 존재유무를 확인하고 이를 사용되게 된다. 공유 라이브러리는 앞서 말한바와 같이 링크되는 시점(해당 공유 라이브러리를 사용하는 어플리케이션을 빌드하는 시점)에 공유 라이브러리의 헤더와 라이브러리( .so)파일이 필요하다. 왜 실행파일에 포함되지 않는 라이브러리 파일이 필요한 것인까? 이는 빌드되는 어플리케이션이 실행될 때 찾아야 할 라이브러리의 이름이 공유 라이브러리 파일 안에 적혀있기 때문인데 이를 soname이라고 하고 이 soname은 라이브러리의 파일명하고 다를 수 있으므로 서로 구분하여야 한다. 공유 라이브러리를 컴파일할 때에는 아래와 같이 –fPIC (Position Independent Code)옵션이 필요하다.
gcc –fPIC –c MyLibrary.c
gcc –shared –Wl,-soname libMyLibrary.so.1 –o libMyLibrary.so.1.2.3 MyLibrary.o
동적 적재 라이브러리는 실행 중에 동적으로 로딩되는 라이브러리며, 프로그램 실행시가 아닌 해당 라이브러리에 포함된 함수가 사용되는 시점에 불려와 사용되게 된다. 따라서 동적 라이브러리를 사용하는 프로그램은 첫 실행이 정적 혹은 공유 라이브러리를 사용할 때 보다 빠르다는 장점이 있다. 또한 동적 적재 라이브러리는 실행 시 동적으로 라이브러리에서 직접 정보를 얻어와 실행한다. 따라서 동적 적재 라이브러리를 사용한 어플리케이션을 빌드하는 시점에서는 해당 라이브러리에 대한 헤더 파일이나 심지어 해당 라이브러리 자체도 필요 없다. 리눅스에서는 일반 공유 라이브러리를 동적으로 적재가 가능하므로 공유 라이브러리와 동적 적재 라이브러리가 같다고 할 수 있다. 다만 이를 사용하는 방법으로 구분될 뿐이다. 즉 so파일을 공유 라이브러리도 사용할 수도 동적 적재 라이브러리로 사용할 수도 있으며 동적 적재 라이브러리로 사용하려면 라이브러리를 사용하고자 하는 프로그램에서 dlopen, dlsym등을 사용하여 원하는 심볼을 불러오도록 처리하면 된다.
대부분의 unix/linux 시스템에서 LD_LIBRARY_PATH가 사용 가능하지만 모든 시스템에서 그런 것은 아니다. 리눅스에서는 LD_LIBRARY_PATH 사용이 가능하지만, /etc/ld.so.conf 파일과 ldconfig을 사용하여 /etc/ld.so.cache 파일을 생성하는 방법이 더 권장된다.
또다른 방법으로는 rpath를 지정하는 방법이 있다. rpath는 실행 파일을 compile할때 지정해 주면 된다.
gcc -Wall -o myexefile -Wl,rpath,. main.o -L. -lMyLibrary
위 예제에서는 rpath를 현재 디렉토리 [.]로 설정하였으며 이는 실행할 때 MyLibrary를 찾는 위치에 현재 디렉토리를 추가한 것이다. -L옵션은 링크시에 MyLibrary를 찾는 위치를 지정하는 옵션으로 역시 [.] 현재 디렉토리로 지정하였다. 굳이 -l 옵션으로 라이브러리를 지정할 필요 없이 경로를 포함한 파일 이름을 사용하여도 된다.
gcc -Wall -o myexefile -Wl,rpath,. main.o ./libMyLibrary.so.1.2.3
2009년 1월 11일 일요일
MTD 이해하기...
MTD는 memory technology device의 약자로, char. device, block device와 같은 별도의 디바이스그룹이라고 간주하는것이 맞을 것 같다. 전통적으로 OS에서 장치를 char. device 와 block device 로 나누어왔기 때문에 flash memory용 디바이스 드라이버를 둘 중 어떤 것으로 분류 시켜야 하는지 고민이 생기게 된다, MTD는 분명 hdd를 대체하는 장치로 사용되고 있지만, 그 동작 특성이 block device와는 현저히 다르기 때문에 굳이 이를 block device 로 부르는 것은 옳지 않으므로 그냥 MTD 라고 부르는 것이 맞을 것 같다. MTD가 선보이기 시작하였을 때 개발자들은 hdd를 대신하여 MTD를 사용하기 위해서 MTD에 기존의 파일 시스템을 올리는 방법을 궁리하였고 가장 손쉬운 방법으로 고안한 것이 FTL 이다. FTL은 Flash devcie 를 block device처럼 보이도록 변환해주는 Layer로 이를 통해서 기존의 block device용으로 개발된 파일 시스템을 변경없이 flash memroy위에서 사용할 수 있게 되었다. 하지만, FTL 을 얼마나 잘 구현했는지에 따라서 성능이나 flash device의 수명이 큰 영향을 받게 된다. 실제로 가장 손쉬운 FTL구현을 생각해 보자, 특정 data를 overwrite하기 위해서 flash는 해당 sector를 모두 지우고 새 데이터를 포함한 sector전체를 다시 write해야 한다. 단지 한바이트의 데이터를 바꾸기 위해서 매번 해당 바이트가 포함된 sector전체를 지우고 다시쓰도록 FTL을 구현한다면, 손쉬운 구현이 되겠지만 flash device의 수명(10만~100만번 erase)이 아주 짧아질 수 밖에 없을 것이다. 따라서 다양한 방법을 사용해서 모든 flash sector가 균등하게 사용되도록 FTL을 구현해야 하며, 이를 Wear Leveling이라고 부른다.
USB drive같은 몇몇 flash devices는 FTL을 아예 H/W로 구현하여 제품에 내장하여 출시하고, 이런 장치는 MTD가 아닌 block device로 보는 것이 타당하다. 그렇지 않은 Flash device에서는 FTL을 소프트웨어로 구현하게 된다.
MTD는 때로는(오히려 더 자주, 일반적으로) device차체가 아닌, device driver를 지칭하는 말로 사용된다. 이 경우에는 MTD는 FTL이 해당 Device를 control 할 수 있도록 도와주는 Low Level Driver라고 생각하면 된다. 제조사 별로, device별로 device를 제어하는 방법이 조금씩 다르기 때문에 MTD Layer에서 이를 일반화하여 주는 것이다. 따라서 FTL은 하위 device의 제조사나 특정 제품에 상관 없이 구현 될 수 있는 것이다. 아래는 FTL 을 사용하는 시스템의 구조를 표현한 것이다.
File System for block device
-----------------------------
FTL
-----------------------------
MTD Layer
최근에는 flash device를 위해서도 몇몇 파일 시스템이 개발되었으며, 이 경우 FTL이 없이 file system이 MTD Layer위에 올리기도 한다.
File System for flash device
-----------------------------
MTD Layer
하지만 필요에 따라서 flash file system이 FTL을 포함하고 있는 경우도 있으며, 이 경우의 FTL은 위에서 설명한 FTL과는 약간 다른 일을 수행하게 된다. 즉 최초의 FTL은 flash device를 block device로 변환시켜주기 위해 사용한 library 나 h/w를 의미하였으나, 최근에는 좀더 광범위한 의미로 사용되고 있다.
읽어볼만한 자료들
http://www.linux-mtd.infradead.org/faq/general.html
http://blog.naver.com/idkiss?Redirect=Log&logNo=50018755390
http://lsea.tistory.com/160
http://www.hongikcom.com/entry/4FTLFlash-Translation-Layer
USB drive같은 몇몇 flash devices는 FTL을 아예 H/W로 구현하여 제품에 내장하여 출시하고, 이런 장치는 MTD가 아닌 block device로 보는 것이 타당하다. 그렇지 않은 Flash device에서는 FTL을 소프트웨어로 구현하게 된다.
MTD는 때로는(오히려 더 자주, 일반적으로) device차체가 아닌, device driver를 지칭하는 말로 사용된다. 이 경우에는 MTD는 FTL이 해당 Device를 control 할 수 있도록 도와주는 Low Level Driver라고 생각하면 된다. 제조사 별로, device별로 device를 제어하는 방법이 조금씩 다르기 때문에 MTD Layer에서 이를 일반화하여 주는 것이다. 따라서 FTL은 하위 device의 제조사나 특정 제품에 상관 없이 구현 될 수 있는 것이다. 아래는 FTL 을 사용하는 시스템의 구조를 표현한 것이다.
File System for block device
-----------------------------
FTL
-----------------------------
MTD Layer
최근에는 flash device를 위해서도 몇몇 파일 시스템이 개발되었으며, 이 경우 FTL이 없이 file system이 MTD Layer위에 올리기도 한다.
File System for flash device
-----------------------------
MTD Layer
하지만 필요에 따라서 flash file system이 FTL을 포함하고 있는 경우도 있으며, 이 경우의 FTL은 위에서 설명한 FTL과는 약간 다른 일을 수행하게 된다. 즉 최초의 FTL은 flash device를 block device로 변환시켜주기 위해 사용한 library 나 h/w를 의미하였으나, 최근에는 좀더 광범위한 의미로 사용되고 있다.
읽어볼만한 자료들
http://www.linux-mtd.infradead.org/faq/general.html
http://blog.naver.com/idkiss?Redirect=Log&logNo=50018755390
http://lsea.tistory.com/160
http://www.hongikcom.com/entry/4FTLFlash-Translation-Layer
2009년 1월 10일 토요일
#define에서 do{...}while(0) 을 사용하는 이유는?
#define을 사용한 매크로 함수에서 do{...}while(0) 문을 사용하는 경우를 볼 수 있다. 왜 굳이 의미없는 do{...}while(0)를 사용하는지 아래 예제를 보면서 살펴보자.
//do{...}while(0)문을 사용하지 않은 경우
#define Inc2Each(x,y) { x+=2;y+=2;}
//do{...}while(0)문을 사용한 경우
#define Inc2Each(x,y) do{ x+=2;y+=2}while(0)
do{...}while(0)을 사용하지 않은 첫번째 방식의 경우 어떤 문제가 사용하는지 살펴보자
if ( x > y )
Inc2Each(x,y);
else
x=y;
위 코드는 아래와 같이 확장될 것이다.
if ( x > y )
{ x+=2;y+=2;};
else
x=y;
즉 if 와 else 사이에 원치 않는 ; 가 포함됨으로써 예상치 못했던 오류를 만들어 내게 된다.
하지만 do{...}while(0)을 사용하면 아래와 같이 문제가 깨끗하게 해결된다.
if ( x > y )
do{ x+=2;y+=2;}while(0);
else
x=y;
혹시 아직도 뭐가 문제인지 모르겠다면...
if ( x > y )
{
x+=2;
y+=2;
} //if 문은 여기서 끝난다.
; //빈 라인이 되고...
else //이 else는 if문이 없는 else가 되므로 컴파일 에러 발생할 것임.
x=y;
자세한 내용은 http://kernelnewbies.org/FAQ/DoWhile0
//do{...}while(0)문을 사용하지 않은 경우
#define Inc2Each(x,y) { x+=2;y+=2;}
//do{...}while(0)문을 사용한 경우
#define Inc2Each(x,y) do{ x+=2;y+=2}while(0)
do{...}while(0)을 사용하지 않은 첫번째 방식의 경우 어떤 문제가 사용하는지 살펴보자
if ( x > y )
Inc2Each(x,y);
else
x=y;
위 코드는 아래와 같이 확장될 것이다.
if ( x > y )
{ x+=2;y+=2;};
else
x=y;
즉 if 와 else 사이에 원치 않는 ; 가 포함됨으로써 예상치 못했던 오류를 만들어 내게 된다.
하지만 do{...}while(0)을 사용하면 아래와 같이 문제가 깨끗하게 해결된다.
if ( x > y )
do{ x+=2;y+=2;}while(0);
else
x=y;
혹시 아직도 뭐가 문제인지 모르겠다면...
if ( x > y )
{
x+=2;
y+=2;
} //if 문은 여기서 끝난다.
; //빈 라인이 되고...
else //이 else는 if문이 없는 else가 되므로 컴파일 에러 발생할 것임.
x=y;
자세한 내용은 http://kernelnewbies.org/FAQ/DoWhile0
2008년 11월 18일 화요일
pthread와 condition variable
일단 pthread관련한 전반적인 내용은 ...
https://computing.llnl.gov/tutorials/pthreads/#Thread
pthread가 제공하는 조건 변수 관련 함수들(pthread_cond_wait, pthread_cond_signal etc.)을 제공하는 이유는 무엇일까 ?
이를 이해하기 위해서 반대로 조건 변수를 사용하지 않을 경우 어떤 불편이 있는지 살펴보자.
while(1)
{
pthread_mutex_lock(&mutex_test);
if(cond_var>=condition)
{
do_something();
cond_var--;
pthread_mutex_unlock(&mutex_test);
break;
}
pthread_mutex_unlock(&mutex_test);
};
위 코드는 조건 변수를 확인하기 위해서 계속적으로 CPU를 소모하며 polling을 해야 하므로 효율적이지 못하다. pthread_cond_xxx를 사용하면 아래와 같이 개선할 수 있다.
1: pthread_mutex_lock(&mutex_test);
2: while(cond_var < condition)
3: pthread_cond_wait(...);
4: do_something();
5: cond_var--;
6: pthread_mutex_unlock((&mutex_test);
- Line 1에서 mutex lock.
- Line 3에서는 아래와 같은 일들이 벌어진다.
- mutex unlock
- pthread_condition_signal이나 pthread_condition_broadcast가 호출될 때까지 block
- mutex lock
- Line 4,5 실행
- Line 6에서 mutex unlock.
Line2에서 if대신 while을 사용한 것에 주목할 필요가 있다. 이는 pthread_conditon_wait가 여러 thread에서 호출하였을 경우 먼저 스케쥴된 thread에서 cond_var--이 실행되기 때문에 나중에 스케쥴된 thread를 다시 재우기 위함이다.
https://computing.llnl.gov/tutorials/pthreads/#Thread
pthread가 제공하는 조건 변수 관련 함수들(pthread_cond_wait, pthread_cond_signal etc.)을 제공하는 이유는 무엇일까 ?
이를 이해하기 위해서 반대로 조건 변수를 사용하지 않을 경우 어떤 불편이 있는지 살펴보자.
while(1)
{
pthread_mutex_lock(&mutex_test);
if(cond_var>=condition)
{
do_something();
cond_var--;
pthread_mutex_unlock(&mutex_test);
break;
}
pthread_mutex_unlock(&mutex_test);
};
위 코드는 조건 변수를 확인하기 위해서 계속적으로 CPU를 소모하며 polling을 해야 하므로 효율적이지 못하다. pthread_cond_xxx를 사용하면 아래와 같이 개선할 수 있다.
1: pthread_mutex_lock(&mutex_test);
2: while(cond_var < condition)
3: pthread_cond_wait(...);
4: do_something();
5: cond_var--;
6: pthread_mutex_unlock((&mutex_test);
- Line 1에서 mutex lock.
- Line 3에서는 아래와 같은 일들이 벌어진다.
- mutex unlock
- pthread_condition_signal이나 pthread_condition_broadcast가 호출될 때까지 block
- mutex lock
- Line 4,5 실행
- Line 6에서 mutex unlock.
Line2에서 if대신 while을 사용한 것에 주목할 필요가 있다. 이는 pthread_conditon_wait가 여러 thread에서 호출하였을 경우 먼저 스케쥴된 thread에서 cond_var--이 실행되기 때문에 나중에 스케쥴된 thread를 다시 재우기 위함이다.
2008년 11월 5일 수요일
vim, ctags, cscope사용법 정리.
VI 설정.
Home directory아래 .vimrc 파일을 아래와 같이 설정
set number :
line number를 왼쪽에 보여줌.
set cindent :
c 스타일의 indent. 예를들어 여는괄호, 닫는 괄호 인덴트가 자동화됨
set smartindent :
정확히 어떤 똑똑한 인덴트 기능이 추가되는지 파악 못함
set ts=4 :
tab key눌렀을때 스페이스 4칸 만큼 떨어짐.
set sw=4 :
자동 인덴트시 스페이스 4칸 만큼 인덴트됨.
set ruler :
오른쪽 하단에 현재 커서 위치(좌표) 표시
syntax on:
syntax highlight(coloring)기능을 켬
set tags=tags :
태그 파일 설정. 오른쪽 tags는 vi를 실행시킨 디렉토리(current directory)의 tags란 파일을 의미함.
cs add cscope.out :
cscope파일 설정
source $HOME/cscope_maps.vim
cscope단축기 설정. cscope_map.vim은 "http://cscope.sourceforge.net/cscope_maps.vim"에서 다운 받을 수 있다.
vim에서 ctag및 cscope를 사용하기 위해서 해야 하는 일들.
find . -name *.[chsCHS] -print > cscope.files
cscope -b -i cscope.files
ctags -R .
> cscope의 -b 옵션을 주면 cscope.out파일만 생성되고 종료. -b 옵션이 없으면 cscope자체가 실행됨.
> cscope의 -i 옵션으로 입력파일 이름리스트가 있는 파일을 지정할 수 있음. -i옵션 없으면 default로 cscope.files를 사용하므로 위 예제에서는 -i 옵션이 굳이 필요 하지 않음.
vim에서 cs(cscope) 명령이 동작 하지 않는다면..
:ver 를 실행하여 +cscope인지 -cscope인지 확인할 것. -cscope면 +cscope로된 vim바이너리를 구하던지 vim을 직접 compile하여야 함. vim소스를 구했다면 일반적으로 --enable-cscope 옵션을 주고 컴파일하면 됨.
vim에서 cs 사용 법.
:cs find s symbol_name
cscope_maps.vim 설정을 사용하면 Cntl-'\' 단축기를 사용할 수 있다.
0 or s : (Cntl-'\' + s)
C 심볼중 검색
1 or g : (Cntl-'\' + g)
symbol_name의 정의를 점색
2 or d: (Cntl-'\' + d)
symbol_name에 해당하는 함수에 의해 호출되는 함수를 검색
3 or c: (Cntl-'\' + c)
symbol_name에 해당하는 함수를 호출하는 함수를 검색
4 or t: (Cntl-'\' + t)
symbol_name에 해당하는 text문자열을 검색
6 or e: (Cntl-'\' + e)
확장 정규식을 사용하여 symbol_name를 검색
7 or f: (Cntl-'\' + f)
파일이름중에서 symbol_name를 검색
8 or i: (Cntl-'\' + i)
symbol_name를 include하는 파일을 검색.
vim에서 아래와 같이 help를 불러올수 있다.
:help cscope
더 많은 내용이 필요할 경우에는...
http://wiki.kldp.org/wiki.php/VimCscopeTutorial
Home directory아래 .vimrc 파일을 아래와 같이 설정
set number :
line number를 왼쪽에 보여줌.
set cindent :
c 스타일의 indent. 예를들어 여는괄호, 닫는 괄호 인덴트가 자동화됨
set smartindent :
정확히 어떤 똑똑한 인덴트 기능이 추가되는지 파악 못함
set ts=4 :
tab key눌렀을때 스페이스 4칸 만큼 떨어짐.
set sw=4 :
자동 인덴트시 스페이스 4칸 만큼 인덴트됨.
set ruler :
오른쪽 하단에 현재 커서 위치(좌표) 표시
syntax on:
syntax highlight(coloring)기능을 켬
set tags=tags :
태그 파일 설정. 오른쪽 tags는 vi를 실행시킨 디렉토리(current directory)의 tags란 파일을 의미함.
cs add cscope.out :
cscope파일 설정
source $HOME/cscope_maps.vim
cscope단축기 설정. cscope_map.vim은 "http://cscope.sourceforge.net/cscope_maps.vim"에서 다운 받을 수 있다.
vim에서 ctag및 cscope를 사용하기 위해서 해야 하는 일들.
find . -name *.[chsCHS] -print > cscope.files
cscope -b -i cscope.files
ctags -R .
> cscope의 -b 옵션을 주면 cscope.out파일만 생성되고 종료. -b 옵션이 없으면 cscope자체가 실행됨.
> cscope의 -i 옵션으로 입력파일 이름리스트가 있는 파일을 지정할 수 있음. -i옵션 없으면 default로 cscope.files를 사용하므로 위 예제에서는 -i 옵션이 굳이 필요 하지 않음.
vim에서 cs(cscope) 명령이 동작 하지 않는다면..
:ver 를 실행하여 +cscope인지 -cscope인지 확인할 것. -cscope면 +cscope로된 vim바이너리를 구하던지 vim을 직접 compile하여야 함. vim소스를 구했다면 일반적으로 --enable-cscope 옵션을 주고 컴파일하면 됨.
vim에서 cs 사용 법.
:cs find s symbol_name
cscope_maps.vim 설정을 사용하면 Cntl-'\' 단축기를 사용할 수 있다.
0 or s : (Cntl-'\' + s)
C 심볼중 검색
1 or g : (Cntl-'\' + g)
symbol_name의 정의를 점색
2 or d: (Cntl-'\' + d)
symbol_name에 해당하는 함수에 의해 호출되는 함수를 검색
3 or c: (Cntl-'\' + c)
symbol_name에 해당하는 함수를 호출하는 함수를 검색
4 or t: (Cntl-'\' + t)
symbol_name에 해당하는 text문자열을 검색
6 or e: (Cntl-'\' + e)
확장 정규식을 사용하여 symbol_name를 검색
7 or f: (Cntl-'\' + f)
파일이름중에서 symbol_name를 검색
8 or i: (Cntl-'\' + i)
symbol_name를 include하는 파일을 검색.
vim에서 아래와 같이 help를 불러올수 있다.
:help cscope
더 많은 내용이 필요할 경우에는...
http://wiki.kldp.org/wiki.php/VimCscopeTutorial
2008년 10월 9일 목요일
fopen 옵션 정리
fopen의 파일 열기 옵션은 좀처럼 제대로 외우기가 쉽지 않다. 또한 모든 옵션별 차이점을 제대로 파악하기도 쉽지 않고... 일단 파악된 만큼 정리해 놓고... 추후 더 발견된 사항이 있으면 지속적으로 update해 나가는게 좋을 듯..
"r" : 읽기 전용 모드. 파일이 없으면 NULL return.
"w" :쓰기 전용 모드. 파일이 없으면 생성되고 있으면 내용이 없어진다.
"a" : append모드. 파일이 없으면 생성. 이미 존재하는 파일 끝부분에 file pointer가 위치하게 되며 이 위치부터 뒷쪽으로만 write가능. 읽기는 불가능. fseek등으로 이 부분보다 앞으로 file pointer를 이동시키면 어떻게 될까 ? 아래 내용으로 봐서는 fseek등으로 file pointer를 이동하여도 이와 상관없이 파일 끝부분에 write가 되는 것으로 생각됨.
Opening a file in append mode (a in the mode) causes all subsequent writes to the file to be forced to the current end-of-file, regardless of previous calls to the fseek() function.
"r+" : 읽고 쓰기 모드, 파일이 없으면 NULL return.
"w+" : 읽고 쓰기 모드 단, 파일이 없으면 만들고 있으면 기존 내용을 지움. write를 먼저 한 후 동일 파일 포인터로 읽기 수행이 필요한 경우 사용. 보통은 읽기 전용, 혹은 쓰기 전용으로 fopen하므로 w+가 필요한 일은 별로 없을 듯.
"a+" : append모드, 읽고 쓰기 가능. 파일이 이미 존재할 경우 그 파일의 끝부분에서부터 추가된 내용을 쓴다. 읽기는 fseek로 지정한 file pointer위치에서 가능하나 쓰기는 파일 끝부분에서만 가능.
When a file is opened with update mode (+ in the mode), both input and output may be performed on the associated stream
"r" : 읽기 전용 모드. 파일이 없으면 NULL return.
"w" :쓰기 전용 모드. 파일이 없으면 생성되고 있으면 내용이 없어진다.
"a" : append모드. 파일이 없으면 생성. 이미 존재하는 파일 끝부분에 file pointer가 위치하게 되며 이 위치부터 뒷쪽으로만 write가능. 읽기는 불가능. fseek등으로 이 부분보다 앞으로 file pointer를 이동시키면 어떻게 될까 ? 아래 내용으로 봐서는 fseek등으로 file pointer를 이동하여도 이와 상관없이 파일 끝부분에 write가 되는 것으로 생각됨.
Opening a file in append mode (a in the mode) causes all subsequent writes to the file to be forced to the current end-of-file, regardless of previous calls to the fseek() function.
"r+" : 읽고 쓰기 모드, 파일이 없으면 NULL return.
"w+" : 읽고 쓰기 모드 단, 파일이 없으면 만들고 있으면 기존 내용을 지움. write를 먼저 한 후 동일 파일 포인터로 읽기 수행이 필요한 경우 사용. 보통은 읽기 전용, 혹은 쓰기 전용으로 fopen하므로 w+가 필요한 일은 별로 없을 듯.
"a+" : append모드, 읽고 쓰기 가능. 파일이 이미 존재할 경우 그 파일의 끝부분에서부터 추가된 내용을 쓴다. 읽기는 fseek로 지정한 file pointer위치에서 가능하나 쓰기는 파일 끝부분에서만 가능.
When a file is opened with update mode (+ in the mode), both input and output may be performed on the associated stream
2008년 10월 6일 월요일
C에서 C++ 함수 호출하는 방법
많이 발생하는 경우는 아니지만, C에서 C++ 함수의 호출이 필요한 경우가 있다. 이런 경우에는 아래와 같은 방법으로 C++ 함수를 호출하여 사용할 수 있다.
먼저 호출하고자하는 함수를 extern "C"로 감싼다. 하지만 호출하고자 하는 함수가 클래스의 메소드일 경우에는 해당 메소드를 C 스타일의 전역 함수로 감싸야(wrapping) 한다.
//In the cpp file.
extern "C" void func_in_cpp(void);
...
extern "C"
{
void func_in_cpp(void)
{
class_type class_obj_name;
class_obj_name.class_method1();
}
}
/* In the C file */
int main(void)
{
...
func_in_cpp();
...
return 0;
}
위에서와 같이 extern "C"로 선언/정의된 함수에서도 class의 instance를 만들고 사용할 수 있다. 즉 위에서와 같이 glue I/F를 만들어 주고 이를 C 프로그램에서 호출함으로써 C++ 코드안의 함수를 호출할 수 있다. 단 여기서 실행파일 생성 시 주의할 점이 있다.
glue I/F (cpp파일)나, C++ 라이브러리는 compile시 당연히 C++컴파일러로 컴파일한다. (ex. g++사용, gcc를 사용해도 확장자를 인식해 g++로 컴파일 될 것임.)
g++ -c cppfile.cpp
C++ 함수를 호출하는 C 프로그램은 당연히 C 컴파일러로 컴파일 한다. (ex. gcc 사용)
gcc -c cfile.c
마지막으로 link시 C++ library가 link되도록 옵션을 설정하여야 한다.
gcc의 경우, gcc대신 g++을 사용하여 link하면 간단하게 해결된다.
g++ -o c_application cfile.o cppfile.o
하지만 빌드 tool chain에 따라서 이를 별도의 linker옵션으로 설정해야 하는 경우도 있다.
아래는 QNX tool chain에서 제공하는 링커의 옵션을 사용한 경우의 예이다.
qcc -lang-c++ -o c_application cfile.o cppfile.o
여게서 -lang-c++은 linker옵션으로 linker에게 c++ library를 사용해야 한다고 알려준다.
gcc대신 g++을 사용해서 c++ 링커 옵션을 대신할 수 있듯이 QNX 툴도 qcc대신 QCC를 사용하면 c++ 옵션을 대신할 수 있다. 하지만 대소문자 구분이 되지 않는 Windows환경을 host 컴퓨터로 사용하고 있다면 QCC를 제대로 인식하지 못할 것이다. 이런 경우 -lang-c++을 사용해야 한다.
먼저 호출하고자하는 함수를 extern "C"로 감싼다. 하지만 호출하고자 하는 함수가 클래스의 메소드일 경우에는 해당 메소드를 C 스타일의 전역 함수로 감싸야(wrapping) 한다.
//In the cpp file.
extern "C" void func_in_cpp(void);
...
extern "C"
{
void func_in_cpp(void)
{
class_type class_obj_name;
class_obj_name.class_method1();
}
}
/* In the C file */
int main(void)
{
...
func_in_cpp();
...
return 0;
}
위에서와 같이 extern "C"로 선언/정의된 함수에서도 class의 instance를 만들고 사용할 수 있다. 즉 위에서와 같이 glue I/F를 만들어 주고 이를 C 프로그램에서 호출함으로써 C++ 코드안의 함수를 호출할 수 있다. 단 여기서 실행파일 생성 시 주의할 점이 있다.
glue I/F (cpp파일)나, C++ 라이브러리는 compile시 당연히 C++컴파일러로 컴파일한다. (ex. g++사용, gcc를 사용해도 확장자를 인식해 g++로 컴파일 될 것임.)
g++ -c cppfile.cpp
C++ 함수를 호출하는 C 프로그램은 당연히 C 컴파일러로 컴파일 한다. (ex. gcc 사용)
gcc -c cfile.c
마지막으로 link시 C++ library가 link되도록 옵션을 설정하여야 한다.
gcc의 경우, gcc대신 g++을 사용하여 link하면 간단하게 해결된다.
g++ -o c_application cfile.o cppfile.o
하지만 빌드 tool chain에 따라서 이를 별도의 linker옵션으로 설정해야 하는 경우도 있다.
아래는 QNX tool chain에서 제공하는 링커의 옵션을 사용한 경우의 예이다.
qcc -lang-c++ -o c_application cfile.o cppfile.o
여게서 -lang-c++은 linker옵션으로 linker에게 c++ library를 사용해야 한다고 알려준다.
gcc대신 g++을 사용해서 c++ 링커 옵션을 대신할 수 있듯이 QNX 툴도 qcc대신 QCC를 사용하면 c++ 옵션을 대신할 수 있다. 하지만 대소문자 구분이 되지 않는 Windows환경을 host 컴퓨터로 사용하고 있다면 QCC를 제대로 인식하지 못할 것이다. 이런 경우 -lang-c++을 사용해야 한다.
C에서 C++함수를 호출할 때는 몇가지 주의 사항이 있다. 이에 대한 자세한 내용은 "BINARY HACKS 해커가 전수하는 테크닉 100선, 타카바야시 사토루외 4인, O'REILLY ****" 에서 찾을 수 있다.
2008년 3월 14일 금요일
awk 사용하기 - 1
awk는 개발자의 삶을 편안하게 해주는 툴이다. 사용자가 얼마나 창의적으로 사용하는가에 따라서 여러가지 목적으로 사용될 수 있기 때문에, 사용법보다 응용법이 더 중요하다. 우선 사용법이 잘 정리된 사이트를 소개한다.
http://www.4ellene.net/tt/1087
아래는 개인적으로 개발시 많이 사용하는 사용예이다.
make | awk '{for(i=1;i<=NF; i++) print $i }'
큰 규모의 프로그램을 컴파일할 때는 컴파일 및 링크 옵션이 매우 복잡하고 파일 경로가 길 경우 화면에 출력되는 로그의 양이 엄청나다. 따라서 자칫 makefile에 옵션을 잘못 넣어도 이를 찾아내기도 어렵고, 컴파일 에러가 발생해도 바로 문제 위치를 찾기 어렵다. 이럴 때 위와 같이 사용하면 출력되는 로그가 공백을 기준으로 줄바꿈되며 정렬된다.
아래는 위 옵션 이 없이 make했을때의 결과이다.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
gcc -c -DSPIDER_AP -DVER1 -DCOBRA_AP -DAR
5315 -DFREEDOM_AP -ID:/io-pkt/core_networ
king/stage/usr/include -IC:/QNX632/target
/qnx6/usr/include -I../common/include -Ii
nclude -I../../../../src/dk/mdk/client/co
bra/soc_linux/include -c -V3.3.5,gcc_ntop
pcbe -O2 -I. -I../../../include -I../mdk
-I../devmld -DMAUI -I../../../../src/dk/m
dk/client/soc_linux_driver/include -D:/Av
inashWork/AP61_LinuxART_v53b59/releases/l
inuxsrc/src/802_11/madwifi/madwifi/ath -I
D:/AvinashWork/AP61_LinuxART_v53b59/relea
ses/linuxsrc/src/802_11/madwifi/madwifi -
ID:/io-pkt/core_networking/trunk/sys/dev_
nda/ath_hal -DSOC_LINUX -DLinux -DQNX -DM
DK_AP -DARCH_BIG_ENDIAN -DSOC_AP -DENDIAN
_SWAP -I../devlib -I../devlib/ar5210 -I..
/devlib/ar5211 -I../devlib/ar5212 -I../de
vlib/ar2413 -I../devlib/ar6000 -I../devli
b/ar5513 -I../devlib/inis ../../../../src
/dk/mdk/common/linux_hw.c -o ../../../../
src/dk/mdk/client/cobra/soc_linux/obj/lin
ux_hw.o
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
awk를 사용하여 정리하면 다음 처럼 정렬되어 보인다.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
gcc
-c
-DSPIDER_AP
-DVER1
-DCOBRA_AP
-DAR5315
-DFREEDOM_AP
-ID:/io-pkt/core_networking/stage/usr/inc
lude
-IC:/QNX632/target/qnx6/usr/include
-I../common/include-Iinclude
-I../../../../src/dk/mdk/client/cobra/soc
_linux/include
(중략)
-I../devlib/ar5513
-I../devlib/inis ../../../../src/dk/mdk/c
ommon/linux_hw.c
-o
../../../../src/dk/mdk/client/cobra/soc_l
inux/obj/linux_hw.o
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
http://www.4ellene.net/tt/1087
아래는 개인적으로 개발시 많이 사용하는 사용예이다.
make | awk '{for(i=1;i<=NF; i++) print $i }'
큰 규모의 프로그램을 컴파일할 때는 컴파일 및 링크 옵션이 매우 복잡하고 파일 경로가 길 경우 화면에 출력되는 로그의 양이 엄청나다. 따라서 자칫 makefile에 옵션을 잘못 넣어도 이를 찾아내기도 어렵고, 컴파일 에러가 발생해도 바로 문제 위치를 찾기 어렵다. 이럴 때 위와 같이 사용하면 출력되는 로그가 공백을 기준으로 줄바꿈되며 정렬된다.
아래는 위 옵션 이 없이 make했을때의 결과이다.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
gcc -c -DSPIDER_AP -DVER1 -DCOBRA_AP -DAR
5315 -DFREEDOM_AP -ID:/io-pkt/core_networ
king/stage/usr/include -IC:/QNX632/target
/qnx6/usr/include -I../common/include -Ii
nclude -I../../../../src/dk/mdk/client/co
bra/soc_linux/include -c -V3.3.5,gcc_ntop
pcbe -O2 -I. -I../../../include -I../mdk
-I../devmld -DMAUI -I../../../../src/dk/m
dk/client/soc_linux_driver/include -D:/Av
inashWork/AP61_LinuxART_v53b59/releases/l
inuxsrc/src/802_11/madwifi/madwifi/ath -I
D:/AvinashWork/AP61_LinuxART_v53b59/relea
ses/linuxsrc/src/802_11/madwifi/madwifi -
ID:/io-pkt/core_networking/trunk/sys/dev_
nda/ath_hal -DSOC_LINUX -DLinux -DQNX -DM
DK_AP -DARCH_BIG_ENDIAN -DSOC_AP -DENDIAN
_SWAP -I../devlib -I../devlib/ar5210 -I..
/devlib/ar5211 -I../devlib/ar5212 -I../de
vlib/ar2413 -I../devlib/ar6000 -I../devli
b/ar5513 -I../devlib/inis ../../../../src
/dk/mdk/common/linux_hw.c -o ../../../../
src/dk/mdk/client/cobra/soc_linux/obj/lin
ux_hw.o
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
awk를 사용하여 정리하면 다음 처럼 정렬되어 보인다.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
gcc
-c
-DSPIDER_AP
-DVER1
-DCOBRA_AP
-DAR5315
-DFREEDOM_AP
-ID:/io-pkt/core_networking/stage/usr/inc
lude
-IC:/QNX632/target/qnx6/usr/include
-I../common/include-Iinclude
-I../../../../src/dk/mdk/client/cobra/soc
_linux/include
(중략)
-I../devlib/ar5513
-I../devlib/inis ../../../../src/dk/mdk/c
ommon/linux_hw.c
-o
../../../../src/dk/mdk/client/cobra/soc_l
inux/obj/linux_hw.o
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
2008년 3월 9일 일요일
종료상태 넘기기 실수담
며칠 전 디바이스의 레지스터값을 읽고 쓰는 작은 프로그램을 하나 포팅하였다. 타겟 환경은 QNX라는 유닉스 계열의 임베디드 OS였는데, 포팅이 끝난 후 프로그램의 동작 방식을 조금 수정해야 할 일이 생겼다. 원래 프로그램은 읽은 레지스터 값을 화면에 출력하고 종료하는 형태로 실행되는데, 다른 프로그램으로 읽은 레지스터 값을 넘겨주고 종료하도록 동작 방식을 바꾸어야 했다. 부모 프로그램에서 spawn( fork + exec를 합쳐놓은....)을 사용하여 레지스터 읽는 프로그램을 실행하고... 읽은 레지스터 값을 어떻게 받아올까를 잠시 고민했다. 4바이트 레지스터값을 넘기기 위해서 IPC를 사용하기가 귀찮아 간편한 방법을 고민하던 중 exit를 사용하여 프로세스 종료상태를 넘기는 대신 읽은 레지스터값을 넘기는 방법을 생각하기에 이르렀다. QNX에서 제공하는 spawn이라는 API는 P_WAIT옵션을 주면 실행된 자식프로세스가 끝날 때까지 block되어 있다가 호출한 프로세스에게 종료 상태값을 return하는 기능이 있었다. exit() API 문서에도 exit의 인자로 int형으로 명시되어 있어 spawn과 exit를 사용히면 4바이트 레지스터 값을 넘기는데 별 문제가 없어보였지만, 실제로는 잘 동작하지 않았다. 레지스터의 값이 255 이상일 경우에 읽은 레지스터 값이 종료 상태값(echo $?로 확인 가능)으로 넘어오지 않는 것이었다. exit가 int형 파라메터를 가지지만 실제로 unix계열의 OS에서 프로세스 종료 상태는 0~255사이의 값만 가진다는 것을 나중에야 알았다. (혹시 특정 계열 OS에서 255이상을 종료 상태값으로 가지는 환경이 있으면 알려주시기 바랍니다.)
피드 구독하기:
글 (Atom)