# [2020q1](http://wiki.csie.ncku.edu.tw/linux/schedule) 第 10 週測驗題
###### tags: `linux2020`
:::info
目的: 檢驗學員對 [Linux 記憶體管理](https://hackmd.io/s/rJBXOchtE)的認知
:::
==[作答表單](https://docs.google.com/forms/d/e/1FAIpQLSfISbuPVFT28YiEn0qMtDvhiJyy_4bWQs74BBYu7AL6XrKSDA/viewform)==
---
### 測驗 `1`
以下 Linux 核心模組程式碼嘗試模仿 [Android Logging System](https://elinux.org/Android_Logging_System),提供一個特製的裝置驅動程式,允許 userspace 透過 VFS 寫入日誌和紀錄追蹤。已知可在 Linux v4.15 運作。
- [ ] `Makefile`
```shell
ccflags-y += -std=gnu99
obj-m := log.o
KERNELDIR ?= /lib/modules/`uname -r`/build
default:
$(MAKE) -C $(KERNELDIR) M=$(PWD) module
```
- [ ] `log.c`
```cpp
#include <linux/fs.h>
#include <linux/miscdevice.h>
#include <linux/mm.h>
#include <linux/module.h>
#include <linux/slab.h>
#define LOG_LINE_BUF_SIZE 256
#define LOG_BUF_OUTPUT_BUF_SIZE (LOG_LINE_BUF_SIZE * 4 * 4 * 32)
struct mylog {
void *buff;
ssize_t buf_write_size;
};
static struct mylog *mylog = NULL;
static int mylog_open(struct inode *inode, struct file *file) {
file->private_data = mylog;
printk("client: %s (%d)\n", current->comm, current->pid);
return 0;
}
static int mylog_release(struct inode *inode, struct file *file) {
struct mylog *mylog = file->private_data;
ClearPageReserved(virt_to_page(mylog->buff));
kfree(mylog->buff);
kfree(mylog);
return 0;
}
static int mylog_mmap(struct file *file, struct vm_area_struct *vma) {
struct mylog *mylog = AAA;
unsigned long pfn_start =
(virt_to_phys(mylog->buff) >> BBB) + CCC;
unsigned long size = vma->vm_end - vma->vm_start;
return remap_pfn_range(vma, vma->vm_start, pfn_start, size,
vma->vm_page_prot);
}
static const struct file_operations mylog_fops = {
.owner = THIS_MODULE,
.open = mylog_open,
.release = mylog_release,
.mmap = mylog_mmap,
};
static struct miscdevice mylog_misc = {
.minor = MISC_DYNAMIC_MINOR,
.name = "mylog",
.fops = &mylog_fops,
};
static int __init mylog_init(void) {
int ret = 0;
mylog = kzalloc(sizeof(struct mylog), GFP_KERNEL);
if (!mylog) {
ret = -ENOMEM;
goto err_mylog;
}
mylog->buff = kzalloc(LOG_BUF_OUTPUT_BUF_SIZE, GFP_KERNEL);
if (!mylog->buff) {
ret = -ENOMEM;
goto err_buff;
}
SetPageReserved(virt_to_page(mylog->buff));
ret = misc_register(&mylog_misc);
if (unlikely(ret)) {
pr_err("failed to register misc device!\n");
goto err_register;
}
return 0;
err_register:
kfree(mylog->buff);
err_buff:
kfree(mylog);
err_mylog:
return ret;
}
static void __exit mylog_exit(void) {
misc_deregister(&mylog_misc);
}
module_init(mylog_init);
module_exit(mylog_exit);
MODULE_LICENSE("GPL");
```
參考資訊:
* [include/linux/mm_types.h](https://github.com/torvalds/linux/blob/v4.15/include/linux/mm_types.h)
* [The file Structure](https://www.oreilly.com/library/view/linux-device-drivers/0596000081/ch03s04.html)
請補完程式碼。
==作答區==
AAA = ?
* `(a)` NULL
* `(b)` `file->f_security`
* `(c)` `file->f_mapping`
* `(d)` `file->private_data`
BBB = ?
* `(a)` `PAGE_SHIFT`
* `(b)` 1
* `(c)` 2
* `(d)` `PAGE_SIZE`
CCC = ?
* `(a)` 1
* `(b)` `vma->vm_start`
* `(c)` `vma->vm_end`
* `(d)` `vma->vm_raend`
* `(e)` `vma->m_private_data`
* `(f)` `vma->vm_pgoff`
:::success
延伸問題:
1. 搭配 [Linux Device Drivers](https://www.oreilly.com/library/view/linux-device-drivers/0596000081/) 一書的描述,解釋上述程式碼的運作;
2. 撰寫使用者層級的測試程式碼,VFS 寫入日誌和紀錄,應引入 ring buffer;
:::