[토론?] 핸들 관리의 딜레마????

kkb110의 이미지

흠 제가 핸들을 배포하고 관리해야하는 라이브러리를 짜면서

핸들 생성/삭제/접근에 상수시간이 걸리고 용량도 효율적인

자료구조가 없을까 고민해보다가

Devpia에다가 고민의 글을 올렸었거든요

지금은 생성/삭제/접근이 모두 상수시간이 걸리고

용량도 효율적인 구조를 고안한상태인데

KLDP분들의 의견도 듣고싶어서 한번 Devpia에 올렸던 글을 여기다 다시 올려봅니다 ^^;

있다가 제가 고안한방법도 한번 올려볼게요

------------------------------------------------------------------------------------
라이브러리 하나를 짜고 있는데
핸들 관리에 딜레마에 빠졌습니다.. -_-;;

이유인즉슨 -_-;

제 DLL 라이브러리에서는

MyHandle CreateSomething();
void DeleteSomething(MyHandle);
void SetData(MyHandle,Data);
Data GetData(MyHandle);
(요것들이 구현됨)

이런식으로 CreateSomething() 을 이용해 객체 생성하고 MyHandle을 얻고
SetData나 GetData로 MyHandle에 해당하는 데이터를 조작,관리 할수있게 해야합니다.

그런데 문제가 뭐냐~

제 DLL은 여러 핸들을 관리하게 될텐데 그 많은 핸들을 과연 어떤 자료구조를 저장해둬야 하냐 이거죠 -_-;

지금까지 생각해본 핸들 저장 자료구조 형식입니다.

1.

리스트를 쓴다.

노드당 데이터는 MyHandle,Data 가 들어있고

CreateSomthing() 요청마다 노드를 추가한다

SetData(),GetData() 할때마다 노드를 모두 순차검색해서 맞는 MyHandle을 찾아서 그 노드의 Data를 조작한다.

실격사유 : SetData,GetData할때 리스트에서 해당되는 MyHandle을 가지고있는 노드를 찾아야되는데 순차검색을 해야한다.

2.

위의 리스트를 업그레이드해서 정렬 리스트 컨테이너를 쓴다.(아니면 이진 트리나 해쉬컨테이너계열등.)

나아진점 : GetData,SetData시 맞는 MyHandle을 찾는 시간은 로그적으로 증가한다.

실격사유 : 순차검색보다 나아진 검색시간을 갖지만 아직 임의접근이 불가능하다 -_-;;

3.

임의접근이 불가능해서 리스트를 버렸다 -_-;;

CreateSomthing() 요청마다 힙에 Data가 저장될 공간을 선언하고

그 포인터를 MyHandle로 쓴다. 그렇다면 GetData(MyHandle); 메서드에서

넘어온 MyHandle이 가리키는 메모리주소가 Data가 되기때문에 GetData(),SetData()의 임의접근이 실현된다(앗싸)

실격사유 : GetData,SetData 시에 넘어온 MyHandle이 유효한지 알수가 없다 ㅡㅡ;;;;;

(물론 MyHandle을 생성할때마다 따로 전부 저장해뒀다가 SetData시,GetData시에 유효판정을 고려하는방법을 생각할수도있겠으나

결국 위에 1,2번과 같아짐으로 무효다.)

4.

이제 백터를 쓰기로 했다(크기가 가변적인 배열)

MyHandle은 0,1,2,3,4,.... 이렇게 쭉 나가는 인덱스로 할당이 된다.

Data GetData(MyHandle mh)
{
return dataArray[mh];
}

간단히 써보면 대충 이렇게 될것이다. 드디어 임의접근도 실현되었고

mh의 범위를 체크함으로써 핸들이 유효한지 여부도 알수있다.

실격사유 : 대단한 취약점을 가지고 있다 1000000000000000개의 Handle이 할당된 후 앞쪽에 999999999999999 의 핸들이 Delete 되었을경우

맨뒤에 핸들 하나때문에 엄청난 공간이 낭비된다.

정녕 완벽한 핸들 관리는 불가능한것인가요 -_-;;;;;;;;;;;
-----------------------------------------------------------------------------------

음 ㅡㅡ;;;;

moonzoo의 이미지

Quote:
이제 백터를 쓰기로 했다(크기가 가변적인 배열)

MyHandle은 0,1,2,3,4,.... 이렇게 쭉 나가는 인덱스로 할당이 된다.

Data GetData(MyHandle mh)
{
return dataArray[mh];
}

간단히 써보면 대충 이렇게 될것이다. 드디어 임의접근도 실현되었고

mh의 범위를 체크함으로써 핸들이 유효한지 여부도 알수있다.

실격사유 : 대단한 취약점을 가지고 있다 1000000000000000개의 Handle이 할당된 후 앞쪽에 999999999999999 의 핸들이 Delete 되었을경우

맨뒤에 핸들 하나때문에 엄청난 공간이 낭비된다.

Indexing Table을 하나 만들어 사용하시면 어떨런지..
(index + 포인터 )

예를 들면 File Discriptor Table 처럼요.. ( 스펠링 맞나 --;)

비행소년의 이미지

moonzoo wrote:
예를 들면 File Discriptor Table 처럼요.. ( 스펠링 맞나 --

네이버 사전에서 찾았습니다. 지엽적인 걸로 딴지걸어서 죄송 :wink:

Quote:
descriptor [diskrípt] n. 【컴퓨터】 기술어(記述語), 기술자(記述子) ((정보의 분류색인에 쓰는 어구))

높이 날다 떨어지면.
아푸다 ㅡ,.ㅡ

hey의 이미지

음. 핸들이라는 용어가 낯설군요.

3번 4번을 합치시면 되겠네요.
문주님 말씀대로 디스크립터 색인표를 만듭니다.

그리고 핸들을 생성할 때 3번처럼 데이터 공간을 만든 다음 그 포인터를 디스크립터 색인표의 빈 공간에 넣으시고 그 빈 공간의 번호를 반환하세요. 이게 핸들이자 색인표의 색인번호입니다.
(빈공간을 찾는 방법은 여러 가지가 있는데, 좋은 방법을 생각해 보시구요)
이제 GetData나 SetData를 할 때는 4번처럼 하면 되겠죠.
mh가 들어오면 범위 검사를 하시구요, 디스크립터 색인표에 가서 mh번 배열에 들어있는 포인터를 반환합니다. 이 포인터는 3번에서 말씀하신 데이터 공간의 시작 부분이죠.
그럼 임의 접근도 가능하네요. 만약에 Data가 0이라면 아직 생성되지 않은 핸들이겠죠.


----------------------------
May the F/OSS be with you..


moonzoo의 이미지

비행소년 wrote:
moonzoo wrote:
예를 들면 File Discriptor Table 처럼요.. ( 스펠링 맞나 --

네이버 사전에서 찾았습니다. 지엽적인 걸로 딴지걸어서 죄송 :wink:

Quote:
descriptor [diskrípt] n. 【컴퓨터】 기술어(記述語), 기술자(記述子) ((정보의 분류색인에 쓰는 어구))

앗 그러네여 ㅋ

어쩐지 쓰면서 좀 이상했어요.

확인 감사~

codebank의 이미지

토론의 가치가 있어보여서(원래는 프로그램 QnA쪽으로 생각했는데... :)) 게시물을
옮겼음을 알려드립니다.

P.S. : 그렇게 많은 핸들이 필요할까 생각이 되어서 마지막(4번)을 보는 순간에
환형큐가 생각나 버렸습니다. :twisted:

------------------------------
좋은 하루 되세요.

kkb110의 이미지

역시 데브피아하고는 수준이 다르네요

근데 Data의 포인터를 배열에 인댁싱해두는것은... 결국
1000000개 Handle 생성후 앞에 999999개 Handle 삭제 할때
999999개의 Handle이 삭제되었을때 4*999999 byte가 낭비되는 깔끔하지 못한면이 남아있죠

뭐 이거때문에 지장가는일은 상상하기는 힘들겠지만 -_-;

kkb110의 이미지

제가 고안한 방법은
moonzoo님과 hey님이 말씀해주신것과 원리는 같습니다.

말로하면 약간 복잡합니다.

CreateSomething() 을 할때마다
힙에 ( sizeof(int) + sizeof(Data) ) 만큼 크기를 malloc 합니다.
그리고 그 포인터를 Handle 로 돌려주고요.
( sizeof(int) + sizeof(Data) )의 포인터인 Handle을 vector나 deque에
push_back(Handle) 해둡니다.

그리고 push_back(Handle)된 인덱스(container.size()-1이 되겠죠) 를
malloc 한 메모리에 가장 앞에 int사이즈만큼 더 할당해둔 공간에 저장합니다.

Data는 그 뒤부터 들어가구요.

그렇다면 Handle은 int+Data의 포인터가 되겠죠? Handle+4byte는 Data의 포인터가 되구요.
SetData(Handle)나 GetData(Handle)가 들어오면 *Handle 이 인덱스가 되니까

if (container[*Handle] == Handle)

구문으로 유효성 점검을 할수있고 Handle+4byte 는 Data의 포인터가 되겠죠.

1000000생성후 999999삭제시 용량 낭비 문제는
삭제가 Handle을 담아둔 container 앞이나 중간에서 일어날 경우겠죠?

그럼 DeleteSomething(Handle) 시 만약 Handle이 가르키는
인덱스가 container 앞이나 중간에 있는것이라면
container 맨 마지막에 있는 Handle을 삭제할 자리로 끌어와주면 됩니다.

어떻게하냐면

*container[ container.size()-1] == container.size()-1;

이겠죠? 컨테이너의 마지막 Handle이 가르키는것은 컨테이너에 마지막 인덱스일테니까요.
그 인덱스를 삭제할 인덱스로 바꿔주고 container 마지막에 담긴 Handle을
삭제할 요소에다가 옴겨놓아주면 되는겁니다.

간단한건데 말로하니까 되게 복잡하네요 --;;;;

익명 사용자의 이미지

근데 DLL에서 핸들이 유효한지 꼭 체크를 해야 하나요?

커널같은 경우는 다른 프로세스의 데이터를 접근을 못하도록

체크를 해야하겠지만

DLL의 경우에는 그 DLL을 사용하는 프로세스의 데이타만

가지고 있을 테고 또 잘못된 핸들을 넘겨서 프로그램이 죽는다 해도

그건 사용자가 잘못 사용한 것이기 때문이라고 책임을 넘겨도(-_-)

될 것 같은데요...

혹시 제가 잘못 생각한 구석이 있는걸까요? :roll: