안녕하세요. 요전에 Linked list의 Delete에 관해 이야기 드렸는데, 이번엔 이의 문제점과 보안점을 이야기 해볼까 합니다.
https://dojang.io/mod/page/view.php?id=647
curr = head->next; // 연결 리스트 순회용 포인터에 첫 번째 노드의 주소 저장
while (curr != NULL) // 포인터가 NULL이 아닐 때 계속 반복
{
struct NODE *next = curr->next; // 현재 노드의 다음 노드 주소를 임시로 저장
free(curr); // 현재 노드 메모리 해제
curr = next; // 포인터에 다음 노드의 주소 저장
}
free(head); // 머리 노드 메모리 해제
여기서 소개된 Linked list algorithm의 크나큰 문제점을 먼저 이야기 해보겠습니다. 가정을 해보겠습니다. 해당 Algorithm을 실사용 했을 시 엄청난 Data를 Linked 하게 됩니다. (물론 실사용 code는 조금 더 견고하게 만들어지겠지만요..)
그리고 list를 모두 Delete 할 경우에 짧지만 체감할 수 있는 시간이 발생합니다. 이 과정을 수십번에서 수백번 반복합니다. 여기서 외부요인으로 인해 Delete과정이 모두 이루어지지 않고, 중간에 작업이 종료되었습니다. (이 과정이 계속 반복됩니다.)
여기서 위의 Algorithm은 미처 제거하지 못한 list를 제거할 수 없습니다. (물론 직접 할당된 List를 찾아 제거하는 방법이 있지만 이는 논외로 하겠습니다.) 때문에 이를 보안하기 위해 효율이 다소 떨어질 수도 있지만 제거시에도 Linked를 유지하면서 제거를 해줘야 이를 보안할 수 있습니다. 아래 Code처럼 말이죠.
curr = head->next; // 연결 리스트 순회용 포인터에 첫 번째 노드의 주소 저장
while (curr != NULL) // 포인터가 NULL이 아닐 때 계속 반복
{
struct NODE *next = curr->next; // 현재 노드의 다음 노드 주소를 임시로 저장
head->next = next; // 추가!
free(curr); // 현재 노드 메모리 해제
curr = next; // 포인터에 다음 노드의 주소 저장
}
free(head); // 머리 노드 메모리 해제
이 한줄이 추가됨으로 인해서 삭제 시 위의 문제점을 보안할 수 있습니다. 이상입니다.
제가 여기서 본 Linked list의 대한 고찰을 적어봤습니다. 한줄 추가해보는건 어떨까요^^?
해당 과정은 고려하지 않아도 됩니다.
리스트 해제 도중 중단되는 상황은 프로그램이 중단된 것에 해당합니다.
즉, program crashed -> aborted 상태이면 프로그램은 종료된 것이고,
해제되지 못했던 메모리를 포함해서 프로그램이 사용한 모든 메모리에는 운영체제에 의해 반환됩니다.
문의한 사항은 프로그래밍에서 고려 대상이 되지 않습니다.
가장 널리 쓰이는 현대 운영체제 중에 하나인 리눅스 커널도 간단히 리스트를 제거할 뿐입니다.
https://github.com/torvalds/linux/blob/master/include/linux/list.h
static inline void __list_del(struct list_head * prev, struct list_head * next) |
{ |
next->prev = prev; |
WRITE_ONCE(prev->next, next); |
리눅스 커널이 리스트를 해제하는 과정 중에 중단되었다면 커널 실행에 문제가 발생한 것이니 운영체제가 중단되고, 시스템은 먹통이 되는 상황이니 재부팅을 하면 됩니다.
head->next = next;를 추가해서 이익을 얻을 수 있으려면 해제하는 과정에서 프로그램이 중단되는 상황이 발생했을 때 그 중단을 막고 리커버리할 수 있어야 하는데, 시스템 프로그램에서 그런 상황은 대체로 없습니다. 해제 과정에서 프로그램이 죽었는데 head->next = next;가 어떤 의미가 있을까요?
버그 없이 제대로 동작하는 프로그램을 작성하는 게 더 낫습니다.
static inline void list_splice_tail(struct list_head *list, |
struct list_head *head) |
{ |
if (!list_empty(list)) |
__list_splice(list, head->prev, head); |