커널 모듈을 작성 중입니다. 사용자 공간에서 바이트를 읽고 다시 씁니다.
static ssize_t dev_read(struct file *filep, char *buffer, size_t len, loff_t *offset) {
Node *msg;
int error_count = 0;
// Entering critical section
down(&sem); //wait state
msg = pop(&l, 0);
// No message? No wait!
if(!msg) {
up(&sem);
return -EAGAIN;
}
len = msg->length;
error_count = copy_to_user(buffer, msg->string, msg->length);
if (error_count == 0) {
current_size -= msg->length;
remove_element(&l, 0);
up(&sem);
return 0;
} else {
up(&sem);
printk(KERN_INFO "opsysmem: Failed to send %d characters to the user\n", error_count);
return -EFAULT; // Failed -- return a bad address message (i.e. -14)
}
}
static ssize_t dev_write(struct file *filep, const char *buffer, size_t len, loff_t *offset) {
Node *n;
// buffer larger than 2 * 1024 bytes
if(len > MAX_MESSAGE_SIZE || len == 0) {
return -EINVAL;
}
n = kmalloc(sizeof(Node), GFP_KERNEL);
if(!n) {
return -EAGAIN;
}
n->string = (char*) kmalloc(len, GFP_KERNEL);
n->length = len;
copy_from_user(n->string, buffer, len);
// Enter critical section
down(&sem); //wait state
// buffer is larger than the total list memory (2MiB)
if(current_size + len > MAX_LIST_SIZE) {
up(&sem);
return -EAGAIN;
}
current_size += len;
push(&l, n);
up(&sem);
// Exit critical section
return len;
}
연결된 목록을 해제해야 하는 함수를 삭제합니다.
static void __exit opsysmem_exit(void) {
// Deallocate the list of messages
down(&sem);
destroy(&l);
up(&sem);
device_destroy(opsysmemClass, MKDEV(majorNumber, 0)); // remove the device
class_unregister(opsysmemClass); // unregister the device class
class_destroy(opsysmemClass); // remove the device class
unregister_chrdev(majorNumber, DEVICE_NAME); // unregister the major number
printk(KERN_INFO "charDeviceDriver: Goodbye from the LKM!\n");
}
내 연결 목록과 파괴 기능은 다음과 같습니다.
static void destroyNode(Node *n) {
if(n) {
destroyNode(n->next);
kfree(n->string);
n->string = NULL;
kfree(n);
n = NULL;
}
}
static void destroy(list *l){
if(l) {
destroyNode(l->node);
}
}
typedef struct Node {
unsigned int length;
char* string;
struct Node *next;
} Node;
typedef struct list{
struct Node *node;
} list;
질문은 다음과 같습니다.
rmmod
나는 장치 드라이버를 작성하고 드라이버와 opsysmem_exit
kfree()가 모든 메모리에서 호출되기를 원합니다 .
이는 노드 수가 적을 때 작동합니다.
많은 수의 노드(1000개 이상)를 실행하고 rmmode를 사용하려고 하면 VM이 정지됩니다.
이 문제를 진단하기 위해 왜 해야 하는지 그리고 또 무엇을 해야 하는지 아십니까?
내 함수가 너무 많은 수준의 재귀를 생성합니까?
2000000개 노드에 썼다가 다시 읽어보면 문제 없을 것 같습니다. rmmod를 수행할 때 목록이 비어 있으면 모든 것이 잘 작동합니다.
편집 1:메모리를 해제하지 않고 rmmod를 실행하면 커널이 충돌하지 않는 것으로 나타났습니다. 그러나 아래와 같이 할당된 메모리가 모두 누수됩니다.케델
답변1
방금 해결했습니다. 머레이 존슨이 옳습니다. 내 핵심을 죽인 것은 재귀였습니다.
이것을 배우는 데 왜 7시간이 걸렸는지 설명할 수 있는 사람이 있습니까? 실제로 C의 최대 재귀 깊이는 얼마입니까? 오늘 아침에 523756이라는 기사를 읽었습니다.여기에서 읽었습니다. C까지 아래로 스크롤하세요..
이것이 내 할당 해제기입니다. 아시다시피 누출이 전혀 없습니다.
static void destroy2(list *l) {
Node *_current = l->node;
Node *_next;
while(_current) {
_next = _current->next;
kfree(_current->string);
kfree(_current);
_current = _next;
}
}
내가 메인 포스트에서 사용한 재귀 접근 방식의 또 다른 나쁜 점은 kfreeing 2~4개 노드를 무작위로 건너뛴다는 것입니다.
내 누출 검사 보고서에 관심이 있는 모든 사람을 위해: 저는 github에서 찾은 오픈 소스 도구를 사용하고 있습니다.https://github.com/euspecter/kedr. 보장할 수는 없지만 매우 도움이 됩니다. 커널을 다시 컴파일할 필요는 없습니다.