C/C++
제목에 ioctl() 만 적었습니다만, ioctl() 외에도 void 포인터 인수를 사용하는 함수에 대해 말씀 드리려 합니다.
변수 타입에 얽매이지 않고 인수를 받을 수 있는 void 포인터 인수는 분명 편리한 방법입니다.
void 포인터가 없다면 각 변수 타입에 맞추어 함수를 만들어야 할 테니 말이죠.
그러나 편한 만큼 조심해야 하는 것이 void 포인터라고 생각합니다.
왜냐하면 자칫 게으름을 폈다가는 매우 난처한 상황을 경험할 수 있기 때문이죠.
자 어떤 경우가 있을까요?
오늘 제가 그런 경험을 하면서, 넘겨 집고 프로그래밍을 한 저의 잘못을 다시 반성하게 되었습니다.
문제는 사운드 카드를 이용하는 프로그램을 작성하면서였습니다.
사운드 카드에 사운드를 출력하기 위해서는 사운드 장치의 Fragment size를 먼저 확인해야 합니다.
그 크기만큼 장치에 쓰기를 해야 하는데, 이미 강좌 글에 올렸기 때문에 아래와 같이 작성했습니다.
int get_fragment_size() { int size; ioctl( fd_soundcard, SNDCTL_DSP_GETBLKSIZE, &size); return size; }
언제든지 사운드 카드의 fragment 크기를 알려면 get_fragment_size()를 이용하면 됩니다.
그런데, 이번에 사운드 카드 장치를 다루는 디스크립터, fd_soundcard를 O_NONBLOCK을 사용하려 했습니다.
당연히 사운드카드의 버퍼를 확인하지 않고 쓰기를 하면 버퍼 오버로 문제가 발생합니다.
그래서 버퍼의 비어 있는 양을 알아야 했는데, 버퍼의 비어있는 양을 아는 것은 아직 찾지를 못했고,
대신에 얼만큼 쓰기를 해도 블록되지 않을 수 있는 크기를 구하는 방법을 알게 되었습니다.
바로 SNDCTL_DSP_GETOSPACE 입니다.
그래서 위의 함수를 아래와 같이 수정해서 추가했습니다.
int get_writable_size() { int size; ioctl( fd_soundcard, SNDCTL_DSP_GETOSPACE, &size); return size; }
이제 사운드 카드로 출력하는 디스크립터에 쓰기를 할 때 미리 get_writable_size()를 이용하여
사이즈를 구한 후 사운드 카드에 출력하면 됩니다.
위 함수가 옳을까요? 당연히(?) 틀립니다. ^^; 그러나 잘못된 것은 모르고
한참동안 엉뚱하게 행동하는 프로그램 때문에 고생했습니다.
컴파일을 해도 Warning 없이 깨끗합니다.
그러나 아래와 같이 코드를 실행하면 에러가 없습니다.
void test( void) { while( 1) { printf( "%d\n", get_writable_size()); } }
그러나 아래와 같이 while() 안에 있는 printf()를 밖에 빼면 에러가 발생합니다.
그것도 그 무서운 세그멘트 에러.
void test( void) { printf( "%d\n", get_writable_size()); <--- 이 행에서 에러 while( 1) { printf( "%d\n", get_writable_size()); } }
이상하죠. 똑 같은 구문을 while() 위에 올렸을 뿐인데, 왜 에러가 날까요?
이 글은 문제 부분만 뽑아셔 말씀 드리는 것이라 일단 보기라도 부담이 없습니다만,
실제 코드는 벌써 복잡한 코드이기 때문에 당황할 수 밖에 없습니다.
세그멘트 에러라면 메모리 문제라는 것을 알 수 있습니다.
알면서도 왜 발생하는지 자기체면에 걸려서 도대체 알 수 없었습니다.
그러나 나중에 새로 추가한 SNDCTL_DSP_GETOSPACE는 인수로 int 형이 아닌 audio_buf_info 변수를 사용한다는 것을
알게 되었습니다. 아~ 정말. 이러니 당연히 에러가 나지요.
SNDCTL_DSP_GETOSPACE를 알게 된것만 가지고, 더 확인을 하지 않은 저의 잘못입니다.
왜 SNDCTL_DSP_GETBLKSIZE와 같을 것이라고 생각했을 까요?
여기서 ioctl() 함수의 선언된 모습을 보겠습니다.
int ioctl(int d, int request, ...)
request에 따라 다양한 인수를 받을 수 있습니다.
여러 개가 아니더라도 (void *) 인수를 사용하는 함수는 사용이 편해도 사용에 주의 해야 겠습니다.
request 가 같은 형식을 취한다고 하더라도 어떤 인수를 사용하는지 꼭 확인해야 겠네요.
그래서 get_writable_size() 를 올바르게 수정하면 아래와 같습니다.
int get_writable_size() { audio_buf_info bi; ioctl( mmx->fd_soundcard, SNDCTL_DSP_GETOSPACE, &bi); return bi.bytes; }