함수 포인터 사용의 장단점이 뭘까요?
글쓴이: gurugio / 작성시간: 수, 2009/04/22 - 11:19오전
리눅스 커널도 요즘들어 구조체를 객체처럼 쓰는것 같습니다.
struct adress_space_operations 도 그렇고
함수 포인터들을 모아놓고 클래스 메소드처럼 사용하는것 같습니다.
저도 개인 프로젝트에서 이렇게 사용해볼까 했다가
한가지 고민이 생겼습니다.
예를 들어 세마포어를 만드는데
struct sema {
void (*up)(void);
void (*down)(void);
atomic_t count;
}
up = sema_up;
down = sema_down;
...
sema->up();
이렇게 하면요 한가지 문제가 소스가 길어질 경우
함수 포인터 up에 실제로 어떤 함수의 포인터가 저장되어있는지
알아내기가 힘든것 같습니다.
몇가지 정적 소스 분석 툴들이 함수 포인터를 관리하는것 같은데
혹시 이런 툴을 써보신 분들이 계신지요.
아니면 함수 포인터를 추적하는 방법이 있으면 소개 부탁드립니다.
커널 코드 분석할때도 함수 포인터를 만나면 좀 골치아플때가 많아서 갑갑합니다.
Forums:
근본적으로, 그런걸
근본적으로, 그런걸 몰라도 되니까 객체 지향입니다.
객체의 함수포인터에 무엇이 대입되는지는 객체를 생성하고 초기화하는 과정에서 이루어지고, 그 구체적인 내용은 몰라도 되고 몰라야 하니까요.
함수 포인터의 장단점이라면...
역시 뭐니뭐니해도 코드의 유연성 VS 가독성 or 디버깅성(-_-?)이 아닐까요??
함수포인터의 포인터의 포인터를 만들어서 기능들을 관리했던 적이 있는데, 주석을 잘 안달아놓아서인지... 2달인가 후에 보니 뭐가 뭔지 도통 모르겠더군요... -.-;;;
句日新, 日新 日新 又日新.
句日新, 日新 日新 又日新.
함수 포인터들을
함수 포인터들을 모아놓고 클래스 메소드처럼 사용하는 건,
전 그냥 자바의 인터페이스와 같은 개념이라고 이해했습니다.
커널 쪽에서 VFS나 드라이버 등등 쪽에서 보면..
커널 쪽에서는 파일 시스템이나 드라이버 구현의 상세한 내용을 알지 못해도,
구조체에 있는 멤버의 이름으로 호출을 할 수 있는 거죠.
자바에서 주로 쓰는 말로 하면 Loose Coupling 을 위한 게 아닐까요?
---
Emerging the World!
Emerging the World!
환경이...
함수 포인터를 분석해야 하는 상황이 궁금하네요.
커널 소스 분석을 위해?
자신의 프로그램에서 함수포인터로 할당되는 함수를 찾기 위해?
어쨌든 어떤 함수가 할당되는지 추적을 하고 싶다면,
함수 포인터의 메모리 영역을 감시하고,
메모리의 데이터(함수 포인터)가 변경되면 그 주소를 캐치하면 어떤 함수인지 알 수 있겠네요.
_________________________________________________________
nineye's blog
개인적인
개인적인 이유는
리눅스 커널 소스를 보고 공부하는 경우도 있구요
제 소스를 분석하기 위해서이기도 하구요
꼭 그렇게 만드는게 좋지만 한건가 호기심이 들어서입니다.
간편하게 분석할 수 있는 방법이 있다면
설계에도 좋고 분석에도 좋지 않을까 생각됩니다만
설계에 좋은지는 알겠는데
분석에는 좋은건지 모르겠어서요.
주소값을 확인하는 것은 온라인으로만 확인하는 방법이라
좀더 다른 방법은 없을까 궁금합니다.
----
섬기며 사랑하면 더 행복해집니다.
개인 홈페이지가 생겼습니다 http://caoskernel.org
어셈러브를 개편중입니다 http://www.asmlove.co.kr
struct를 위처럼 쓰는
struct를 위처럼 쓰는 것으로 봐서는 c일 것 같은데요,
위의 어느 분이 말씀하신 것처럼 c에서 객체지향을 표현한 것과 비슷하다고 할 수 있겠네요.
저런 설계가 꼭 좋다고 할 수는 없습니다.
설계는 전체적인 모습으로 판단하는 것이지, 코드 몇줄가지고 판단할 수 있는 것이 아니니까요.
위의 경우를 보니, 세마포어에 대한 데이터와 세마포어를 관리하는 동작을 한 곳에 놓고
사용하기 위한 것인것 같은데요, 저 경우에 함수포인터인 up과 down에 할당될 수 있는
함수는 한정되어 있는 것 같습니다. 즉, 세마포어 데이터를 증가시키거나 감소시키는 함수밖에
올 수 없는 것이죠. 그 이외의 다른 함수가 할당된다면 그건 설계가 잘못된 것 같네요.
그리고 설계는 진행되는 프로젝트에 대해 여러 가지 상태에 따른 장단점을 보고,
선택하는 것이기 때문에, 모듈의 융통성이 좋으면 분석에도 좋을 것이다.. 라고 장담하지 못합니다.
그것은 프로젝트의 성격에 따라 달라지기 때문이죠.
어쨌든 만약 위의 모습으로 만들어 놓고, 함수 포인터에 어떤 함수가 할당되는지 모른다면,
그 부분부터 좀 더 명확한 구조로 만들 필요가 있겠네요.
_________________________________________________________
nineye's blog
_________________________________________________________
nineye's blog
인용:어쨌든 만약
그 반대가 아닌가요? 함수 포인터가 실제로 어떤 함수와 연결되어있는지 알아야 제대로 쓸수 있다면, 그거야 말로 제대로 캡슐화가 안된 잘못된 설계가 아닌가 생각합니다...
제대로된 객체지향 설계라면, 알아야 할것은 up/down이 무슨 역할을 하고 언제 호출해야 하는지이지, 그것들이 실제로 어떻게구현되어있는지는 숨겨져야 할 부분이지요.
제 말의 의도는...
제 말의 의도는 함수를 호출하기 위해서는 함수의 내용을 알아야 한다는 것이 아니라,
처음 글 쓰신 분이 소스 분석중에 up/down의 함수포인터에 어떤 함수가 할당되는지
모르겠다고 하셔서, up/down에 할당될 함수는 적어도 개발 당사자는 알 수 있을 정도로
명확해야 하지 않냐는 말이었습니다. 그리고 함수포인터로 함수를 할당한다는 것은
함수의 내용과는 무관합니다.
_________________________________________________________
nineye's blog
_________________________________________________________
nineye's blog
개발 당사자도
개발 당사자도 개발때는 알텐데
좀 지나면 다시 확인해야하자나요.
그때 좀더 쉽게 추적해서 알아내면 좋겠는데 방법을 잘 모르겠습니다.
----
섬기며 사랑하면 더 행복해집니다.
개인 홈페이지가 생겼습니다 http://caoskernel.org
어셈러브를 개편중입니다 http://www.asmlove.co.kr
원질문의 의도에는 맞지 않는 것 같지만...
Application program의 경우에는 callgrind를 붙여서 한참 돌리고 결과물을 kcachegrind로 뽑아보면 아주 예쁘고 탁월한 GUI로 각각의 함수 주위의 call graph와 불린 횟수, 각 함수에서 보낸 시간 (simulated) 등등을 보여줍니다. 좀 무겁긴 합니다만 강추.
* 마지막으로 써본 게 2년쯤 전이라 요즘에는 어떻게 바뀌었는지 잘 모르겠네요. 더 좋아졌을라나?
개발 당사자라면,
개발 당사자라면, 자신이 객체 설계할 때 정한 원칙이 있겠지요.
예를 들면, 이름이 xx인 클래스는 xx_initialize함수에서 모든 멤버가 초기화된다'와 같이요.
원칙도 없이 xx 클래스는 xx_inistalize, yy는 initialize_yy, zz는 zzInit 이와같이 중구난방으로 하는 경우는 처음부터 유지보수를 고려하지 않는거나 마찬가지일테구요.
음......
리눅스 커널에서 파일 시스템 관련부분을 보면...
VFS 를 쓰죠.. 리눅스에서 동일한 파일시스템 함수들을 정해놓고, 정작 그 구현은 파일 시스템에 따라 달라야 하니까요.
이렇듯, 동일한 인터페이스를 써야 하는데 그 구현은 각기 달라야 하는 모듈들을 구현한다던가 할 때, 자바 같으면 인터페이스를 쓰지만.
C에서는 함수포인터를 쓰는게 맞겠죠..
구조체에 func_name
구조체에
func_name 멤버를 추가하시면됩니다.
물론 함수포인터 할당하실때 명칭도 함께 넣어주면되겠죠..
유레카~ 그러면
유레카~ 그러면 분석도 쉽겠네요.
어짜피 양이 많은것도 아니니까요.
감사합니다.
아... 그럼 아예 함수 포인터를 위한 구조체를 하나 만들면 좋겠네요.
#DEFINE FUNC_POINTER(f_type, name)
struct func_pointer
{
f_type name;
char fname[16];
}
정확한 문법은 잘 모르겠습니다만 대략 이런식으로 해보면 어떨까요?
----
섬기며 사랑하면 더 행복해집니다.
개인 홈페이지가 생겼습니다 http://caoskernel.org
어셈러브를 개편중입니다 http://www.asmlove.co.kr
portability를 위해서는
portability를 위해서는 위 방법이 좋겠지만..
만약 gnu extension을 사용할 수 있는 환경이라면,
backtrace()를 이용하시는 것도 한 방법이겠네요.
(메모리도 절약하고, 매번 이름을 넣을 필요 없다는 장점이...)
gcc로 컴파일 할 때 -rdynamic 옵션을 주셔야 합니다.
노파심에서
노파심에서 말씀드립니다만..
이름 넣으실때 일일이 넣을게 아니라.
메크로 이용하여 자동으로 들어가게하시면됩니다.
하고자 하는 목적과는 다른것 같은데요.
정적 분석을 하는데, 그렇게 한다고 달라지는 것이 있는지 궁금합니다.
함수 포인터를
함수 포인터를 초기화하는 공통 함수를 만들면
소스 분석때도 그 함수들만 찾거나
아예 그 함수를 특정 파일포맷 섹션에 집어넣고
섹션안에 함수 이름들이 저장되게 하는 등의 방법을 쓰면
좀더 분석이 쉽지 않을까 생각이 됩니다.
저도 아직 생각뿐이니 주말에 쉬면서 좀 맹글어보고 확인해봐야겠습니다.
다른 분들께서 말씀해주신 툴들도 써봐야지요.
----
섬기며 사랑하면 더 행복해집니다.
개인 홈페이지가 생겼습니다 http://caoskernel.org
어셈러브를 개편중입니다 http://www.asmlove.co.kr
님들의 깊은 내공에 자극받아..,
늦었지만, 저도 한수 올립니다.
//------------ C언어 코딩 -------------------
struct sema
{
char this_up[100];
char this_down[100];
void (*up)(void);
void (*down)(void);
atomic_t count;
}
void sema_up(void)
{
//역할
}
void sema_down(void)
{
//역할
}
sema->up = sema_up;
sema.this_up = "sema_up";
sema->down = sema_down;
sema.this_down = "sema_down";
//------------ C# 코딩 (자바도 비슷할듯) -------
interface Isema
{
void up(void);
void down(void);
}
class Csema : Isema
{
atomic_t count;
public void up(void)
{
//역할
}
public void down(void)
{
//역할
}
}
//------------- 비교 결과 ------------
0. C의 함수포인터와 C#의 interface는 비슷한 개념으로, 틀을 하나 만들어 놓고 그것을 재사용함.
(보통, 고수들이 즐겨 사용)
1. 객체지향언어(C#, 자바..)는 포인터라는 개념을 안에 숨기고 있다.
2. C언어는 포인터를 잘 이해하고 활용해야 한다.
3. 객체지향언어는 class 라는 붕어빵틀로 붕어빵을 팡팡 찍어내면 된다.
4. C언어는 붕어빵틀과 붕어빵을 모두 만들어야 한다.
5. 객체지향언어는 붕어빵틀을 사용하는 방법을 익혀야 하고, 초기 비용이 든다.
6. C언어는 수작업을 많이 해야 하지만, 내가 일일히 제어해 갈 수 있다.
(수제품을 만들어 가는 장인정신이 필요함)
... 결론은 자기에게 익숙한 것을 사용하면 된다는...
From:
*알지비 (메신저: rgbi3307(at)nate.com)
*학창시절 마이크로마우스를 만들었고, 10년동안 IT관련 개발자로 일하고 있음.
*틈틈히 커널연구회(http://www.kernel.kr/) 내용물들을 만들고 있음.
*((공부해서 남을 주려면 남보다 더많이 연구해야함.))
From:
*알지비 (메일: rgbi3307(at)nate.com)
*커널연구회(http://www.kernel.bz/) 내용물들을 만들고 있음.
*((공부해서 남을 주려면 남보다 더많이 연구해야함.))
글 쓰신 분은 실행
글 쓰신 분은 실행 시간에 어떤 함수가 할당되는지 모른다고 하셨는데..
위 코드는 함수포인터에 어떤 함수가 할당되는지 아는 젠제하에 만드는 것 같은데요?
또는, 함수포인터에 들어갈 함수는 정해져 있는데, 실행 시간에 자주 교체되어서
체크하는 시점에는 어떤 함수가 들어가는지 모르는 것이라면,
struct sema를 키울 필요없이,
초기화 시점에, 함수포인터에 들어갈 수 있는 함수의 포인터에 번호나, 이름을 할당하고
그 이름을 특정 리스트로 관리하면서,
체크하는 시점에, 함수포인터에 할당된 포인터의 값만으로도 어떤 함수가 들어가 있는 지
확인 가능할 것 같습니다.
들어갈 함수가 정해져 있다는 것은 그 수가 적을거라 예상되므로, 리스트의 사이즈에 대한
압박은 그리 크지 않을거라 생각되네요. 그것도 분석용도라...
어쨌든 저는 분석이나 디버깅 용도의 코드가 실제 적용할 코드의 내부에 들어가면 안된다고 생각합니다.
그러면 시간 체크나, 부하 체크나, 정확성 체크 등에서, 분석용 코드를 뺀 코드에서도 동일하다는 장담을 할 수가 없으니까요..
_________________________________________________________
nineye's blog
_________________________________________________________
nineye's blog
nineye님께서 말씀하신것들은,
C#의 interface에서 하고 있는 것들입니다.
interface에서 함수형(붕어빵틀)을 만들어 두고,
class에서 실제함수(붕어빵)를 만들어 넣는 거죠.
"어떤 함수가 할당되는지 모른다" 이 말의 의미는,
함수형(붕어빵틀)에 A, B, C, D... 함수원형(붕어빵)을 넣어 재사용 하다보면,
현재 함수형(붕어빵틀)이 A를 실행하고(굽고) 있는지, D를 실행하고(굽고) 있는지 헷갈린다는 것이고,
이것을 손쉽게 디버깅 하는 방법을 고민한듯 합니다.(처음 글쓴분이...)
C#에서는 this 나 base 연산자를 통하여 자신과 부모정보를 확인하면서 디버깅할 수 있지만,
C에서는 이런 역할을 하는 것을 직접 코딩해 넣어야 한다는 것입니다.
즉, C에서도 함수포인터와 구조체를 잘 활용하면 객체지향 코딩을 할 수 있다는 것이구요.
제가 위에서 예로 든 코드는 sema(형틀)에 대해서 A 함수(up,down)만 예시한 것입니다.
From:
*알지비 (메신저: rgbi3307(at)nate.com)
*학창시절 마이크로마우스를 만들었고, 10년동안 IT관련 개발자로 일하고 있음.
*틈틈히 커널연구회(http://www.kernel.kr/) 내용물들을 만들고 있음.
*((공부해서 남을 주려면 남보다 더많이 연구해야함.))
From:
*알지비 (메일: rgbi3307(at)nate.com)
*커널연구회(http://www.kernel.bz/) 내용물들을 만들고 있음.
*((공부해서 남을 주려면 남보다 더많이 연구해야함.))
// DECLARE_FPTYPE typedef
// DECLARE_FPTYPE
typedef int (*ftype)(int);
#define DECLARE_FPOINTER(type)\
struct fpointer_t {\
__typeof__(type) fp;\
char *fname;\
} fpointer_t;
#define SET_FPOINTER(func)\
struct fpointer_t p##func = {\
.fp = (func),\
.fname = (#func)\
};
#define GET_FPOINTER(func) (&p##func)
DECLARE_FPOINTER(ftype);
SET_FPOINTER(foo);
struct fpointer_t *fp = GET_FPOINTER(foo);
gcc에서 이렇게 함 만들어봤구요
CaOS에 넣을때는 함수 포인터들을 따로 하나의 전용 섹션에 넣어볼까 합니다.
구조체 선언과 정의를 하나로 합치려면 어떻게 하는게 좋을까요?
합치면 좀더 간단해질것 같은데요.
----
섬기며 사랑하면 더 행복해집니다.
개인 홈페이지가 생겼습니다 http://caoskernel.org
어셈러브를 개편중입니다 http://www.asmlove.co.kr
헉.. 단순한 분석
헉.. 단순한 분석 용도 치고는 너무 정형적으로 만드셨네요...ㅋㅋ
macro 문법에 익숙하지 않아서 보기가 힘들다는... - -;;
어쨌든 꼭 구조체 선언과 정의를 하나로 합칠 필요는 없어 보입니다.
각각의 매크로가 하나씩의 의미를 가지고 있어서
분석 용도 이외의 함수포인터에 대한 관리까지도 용이한 구조로 만들 수 있을 것 같네요...
_________________________________________________________
nineye's blog
_________________________________________________________
nineye's blog