문자 장치 드라이버를 만들기 위해 LKM을 작성 중입니다.
Linux 커널: VirtualBox의 4.4.0-93-일반, 2GB RAM, SWAP은 300Kb
질문 1
처리할 C 프로그램을 작성하면FDdev_write에서는 모든 것이 정상입니다. 그래야 하는 것처럼 읽히지만, 사용하려고 하면헤더 -n 1 < /dev/opsysmem아무것도 출력하지 않습니다.
장치에서 코드 읽기:
int main()
{
int ret, fd;
char stringToSend[BUFFER_LENGTH];
printf("Starting device test code example...\n");
fd = open("/dev/opsysmem", O_RDWR); // Open the device with read/write access
if (fd < 0)
{
perror("Failed to open the device...");
return errno;
}
printf("Press ENTER to read back from the device...\n");
getchar();
printf("Reading from the device...\n");
ret = read(fd, receive, BUFFER_LENGTH); // Read the response from the LKM
if (ret < 0)
{
perror("Failed to read the message from the device.");
return errno;
}
printf("The received message is: [%s]\n", receive);
return 0;
}
질문 2
충분히 큰 메시지를 반복해서 보내면 모든 것이 정상이며 2MiB 버퍼가 가득 차고 다음 메시지가 삭제됩니다. 그러나 메시지가 더 작은 경우(예: 메시지당 1자) 약 10000 노드 후에 중지됩니다. 이것은 내 연결 목록 구현의 문제입니까, 알려진 Linux 문제입니까, 아니면 내 코드에 내가 관찰하지 못하는 뭔가가 있는 것입니까?
문제 2가 발생하면 vCPU가 정현파 방식으로 조절됩니다.
읽기 및 쓰기 기능은 다음과 같습니다.
static ssize_t dev_read(struct file *filep, char *buffer, size_t len, loff_t *offset) {
Node *msg;
int ret_val;
char* message;
unsigned int message_length;
// Entering critical section
down(&sem); //wait state
msg = pop(&l, 0);
// No message? No wait!
if(!msg) {
up(&sem);
return -EAGAIN;
}
if(len < msg->length) {
up(&sem);
return -EINVAL;
}
// Since we have a message, let's send it!
current_size -= message_length;
// copy_to_user has the format ( * to, *from, size) and returns 0 on success
ret_val = copy_to_user(buffer, msg->string, message_length);
if (!ret_val) {
remove_element(&l, 0);
up(&sem);
return ret_val;
} else {
up(&sem);
return -EFAULT; // Failed
}
}
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
if(SLEEP) msleep(100);
// 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;
}
이것은 내 연결 목록과 그 기능입니다.
typedef struct Node {
unsigned int length;
char* string;
struct Node *next;
} Node;
typedef struct list{
struct Node *node;
} list;
static void init(list * l){
l->node = NULL;
}
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);
}
}
static Node* pop(list *l, unsigned int index) {
struct Node *_current = l->node;
// Cut down index until reaching the desired position
while(_current) {
if(index) {
_current = _current->next;
index--;
} else { return _current; }
}
// If you are here, the node does not exist
return NULL;
}
static int push(list * l, Node *n) {
if(!n) { return -1; }
// Initialize the string
// Do we have a node in the list?
if (l->node) {
// Virtually add it as a head
n->next = l->node;
} else {
n->next = NULL;
} // Otherwise prepare the list to have no tail
// Now make the list point to the head
l->node = n;
return 0;
}
static int remove_element(list * l, unsigned int index){
// Get the reference for head
struct Node *previous;
struct Node *_current;
previous = NULL;
_current = (Node*) l->node;
// Swap _current until index
while(_current) {
// Is the index !0 and we have more nodes?
if(index) {
previous = _current;
_current = _current->next;
index--;
} else {
if(previous) {
previous->next = _current->next;
} else {
l->node = _current->next;
}
// Free memory, assign NULL pointer
kfree(_current->string);
_current-> string = NULL;
kfree(_current);
_current = NULL;
// Return success
return 0;
}
}
// No _current? No problem!
return -1;
}
__ 질문 2에 대한 업데이트 __ 다양한 크기의 입력 문자열을 시도한 결과 장치 드라이버(크기 3.3k)에 대해 약 650번 호출한 후 메시지 목록 크기가 4MiB(최대값)가 되었습니다. 장치를 몇 번 더 호출하면 커널이 정지됩니다.
편집 1:댓글을 기반으로 te dev_write를 업데이트하고 디버그 코드를 제거했습니다. 편집 2:더 많은 기능 추가: 푸시/팝/파괴 편집 3:버퍼 길이와 메시지 길이를 확인했습니다.
답변1
제 생각에는질문 1head
줄 끝 문자(예: 개행 문자 '\n'
)가 표시되지 않거나 검색 시스템 호출을 사용하고 및 함수의 인수를 무시하기 때문일 수 있습니다 offset
. 즉, 올바르게 이해하면 검색이 작동하지 않습니다. ).. . 확인하다dev_read()
dev_write()
이것out - head는 검색을 사용하여 항목을 최적화하려고 시도하지만 귀하의 경우에는 작동할지 확실하지 않습니다.
당신의 대답도 확실하지 않습니다질문 2동기화되지 않은 시간으로 인해 발생하는 문제는 정확합니다(관련이 없는 한 msleep()
)... 제 추측으로는 메모리 할당 문제나 경쟁 조건이지만 소스 코드를 표시하지 않으셨기 push()
때문에 pop()
알 수 없습니다.
buffer
및 len
인수를 저장한 dev_write()
다음 이를 사용하여 ... dev_read()
에 전달하는 것 같습니다. copy_to_user()
해당 버퍼의 데이터는 여전히 사용자 공간에 있으므로 아마도 사용자 공간에서 사용자 공간으로 복사하려고 할 것입니다. 읽다이것도움이 될 수도 있습니다.
push()
and...의 코드로 질문을 업데이트해야 합니다. pop()
하지만 최소한 push()
연결된 목록 요소가 목록에 삽입될 메모리와 기록된 데이터를 저장할 버퍼를 할당해야 합니다. 이 버퍼는 copy_from_user()
데이터를 가져오는 데 사용됩니다. 사용자 공간과 커널 버퍼 영역... 그런 다음 msg
in 작업이 끝나면 및 그 자체 dev_read()
에 포함된 커널 버퍼를 해제해야 합니다 .msg
msg
여기에서 많은 복사가 일어나고 있다는 것을 알고 있지만 이를 방지하려면 가상 메모리 시스템과 코드 설계에 매우 열심히 노력해야 합니다(예:제로 카피구현하다).
또 하나의 사소하지만 매우 중요한 점은 dev_read()
버퍼에 메시지를 위한 충분한 공간이 있는지 확인하지 않는다는 것입니다 message_length
. <= len
예를 들어, 코드에 따라 드라이버가 사용 가능한 공간보다 큰 메시지를 복사하려고 할 수 있습니다. copy_to_user()
그걸 잡아야 하는데, 그러다가 다시 말하지만, 어쩌면 그게 당신이 온 곳일 수도 있습니다질문 2.
답변2
나는 그것을 해결했다질문 2. 이는 virtualbox가 호스트 시스템과 동기화되지 않았기 때문에 발생합니다.
여기서 해결책을 찾았습니다 https://stackoverflow.com/questions/5308492/virtualbox-synchronization-problems
이것은:
VBoxManage 게스트 속성 설정 "/VirtualBox/GuestAdd/VBoxService/--timesync-set-threshold" 1000
답변3
질문 1
내 커밋 기록을 검색한 결과 "Working test.sh"라는 커밋 메시지를 발견했습니다. 이는 다음을 통해 쓰고 echo
읽고 있음을 의미합니다.head -n 1
이 커밋에서 내 dev_read 함수는 메시지 길이를 반환합니다.
이것은 위의 C 프로그램에서 버퍼링된 읽기에 작동하는 것 같습니다 head -n 1
.cat
나는 여기서 답을 찾았습니다. https://www.tldp.org/LDP/lkmpg/2.4/html/c577.htm