# Linux 核心專題: 虛擬攝影機裝置驅動程式 > 執行人: hankTaro > [專題解說錄影](https://youtu.be/Ze4QXG0Ov9I) :::success :question: 提問清單 * ? ::: ## 任務簡述 [vcam](https://github.com/sysprog21/vcam) 是個針對 Linux 核心開發的虛擬攝影機裝置,全部程式碼僅 1 千 5 百行,從而可理解 V4L2 (video fro Linux APIs, version 2) 的使用和 Linux 多媒體架構。 ## TODO: 整理過往報告 閱讀以下報告,並針對 Linux v5.15+ 的 [V4L2 介面](https://www.kernel.org/doc/html/latest/driver-api/media/) (及異動),予以更新,應包含 V4L2 和 [vcam](https://github.com/sysprog21/vcam) 運作原理,可善用 [HackMD 書本模式](https://hackmd.io/s/how-to-create-book-tw)展現。 * [2020 年開發紀錄](https://hackmd.io/@eecheng/B16rQ3GjU) * [2021 年開發紀錄](https://hackmd.io/@WayneLin1992/HkDBmLUDO) * [2022 年開發紀錄](https://hackmd.io/@Masamaloka/linux2022-vcam) 可沿用過去的內容,但應該在整理的材料開頭提及貢獻者。紀錄你的認知和疑惑。 ### V4L2 架構與簡介 V4L2 全名為 video for linux two ,前身為 V4L ,故 V4L2 尾端的 2 是2代的概念 V4L2 是 linux 中的一個驅動程式框架,其作為 API 支援實時(realtime)影音捕捉,他提供不同硬體一個統一的框架,像是你電腦的攝影機與螢幕之間,sensor 獲取了資料,要如何處理這些資料、怎麼傳輸、傳輸到哪,都是在 V4L2 的框架下統一管理。 為何需要一個這樣的框架去架在裝置與裝置之間,因為在過去不同裝置之間會需要有個別的驅動程序來供其運作,這是一件耗時且困難的工作,也容易使得裝置不正確運作,並且許多通用的代碼由於沒有共同框架,在[代碼重構](https://en.wikipedia.org/wiki/Code_refactoring)上會十分困難,故 V4L2 提供一個驅動框架收錄了各類驅動所需的基礎架構塊(basic building blocks),可以依照使用者所需選用。 在Linux 中,所有裝置都被看成一種特殊的文件,可以像存取一般文件一樣對其進行讀寫。像是筆電的攝影機就是在 `/dev/videoX` (其中`X`是數字,取決於有幾個註冊的攝影裝置),V4L2 藉由對其記憶體映射(mmap)或直接讀取(read)來採集影像。 V4L2規範中不僅定義了通用API元素(Common API Elements),圖像的格式(Image Formats),輸入/輸出方法(Input/Output),還定義了Linux內核驅動處理視頻信息的一系列介面(Interfaces),這些介面主要有: * 視頻採集介面——Video Capture Interface; * 視頻輸出介面—— Video Output Interface; * 視頻覆蓋/預覽介面——Video Overlay Interface; * 視頻輸出覆蓋介面——Video Output Overlay Interface; * 編解碼介面——Codec Interface。 <!-- 雖然說其名稱是 video for linux 2 ,但其應用面相十分廣泛,影片傳輸只是其中一個部份,還有包括解碼、格式轉換等等各種 --> 對於使用者來說,V4L2 的大體操作流程如下: * Opening the device * Changing device properties, selecting a video and audio input, video standard, picture brightness a. o. * Negotiating a data format * Negotiating an input/output method * The actual input/output loop * Closing the device <!-- 接下來會依照 [2.4 Video device' s internal representation](https://www.kernel.org/doc/html/latest/driver-api/media/v4l2-dev.html) 來介紹如何將一新裝置藉由 V4L2 註冊進電腦 --> <!-- #### 裝置的宣告 The actual device nodes in the /dev directory are created using the video_device struct (v4l2-dev.h). This struct can either be allocated dynamically or embedded in a larger struct. To allocate it dynamically use video_device_alloc(): ```c struct video_device *vdev = video_device_alloc(); if (vdev == NULL) return -ENOMEM; vdev->release = video_device_release; ``` --> ### v4l2 framework <!-- ### Vcam 其作用 --> ## 確認通過 v4l2-compliance 依據 [2022 年開發紀錄](https://hackmd.io/@Masamaloka/linux2022-vcam),確保 vcam 可通過所有 [v4l2-compliance](https://manpages.ubuntu.com/manpages/bionic/man1/v4l2-compliance.1.html) 的測試項目,若有無法通過,則逐一排除。 使用 `sudo v4l2-compliance -d /dev/video2 -f` 測試 ### 5.19.0 測試結果 ```shell $ sudo v4l2-compliance -d /dev/video2 -f v4l2-compliance 1.22.1, 64 bits, 64-bit time_t Compliance test for vcam device /dev/video2: Driver Info: Driver name : vcam Card type : vcam Bus info : platform: virtual Driver version : 5.19.17 Capabilities : 0x85200001 Video Capture Read/Write Streaming Extended Pix Format Device Capabilities Device Caps : 0x05200001 Video Capture Read/Write Streaming Extended Pix Format Required ioctls: test VIDIOC_QUERYCAP: OK test invalid ioctls: OK Allow for multiple opens: test second /dev/video2 open: OK test VIDIOC_QUERYCAP: OK test VIDIOC_G/S_PRIORITY: OK test for unlimited opens: OK Debug ioctls: test VIDIOC_DBG_G/S_REGISTER: OK (Not Supported) test VIDIOC_LOG_STATUS: OK (Not Supported) Input ioctls: test VIDIOC_G/S_TUNER/ENUM_FREQ_BANDS: OK (Not Supported) test VIDIOC_G/S_FREQUENCY: OK (Not Supported) test VIDIOC_S_HW_FREQ_SEEK: OK (Not Supported) test VIDIOC_ENUMAUDIO: OK (Not Supported) test VIDIOC_G/S/ENUMINPUT: OK test VIDIOC_G/S_AUDIO: OK (Not Supported) Inputs: 1 Audio Inputs: 0 Tuners: 0 Output ioctls: test VIDIOC_G/S_MODULATOR: OK (Not Supported) test VIDIOC_G/S_FREQUENCY: OK (Not Supported) test VIDIOC_ENUMAUDOUT: OK (Not Supported) test VIDIOC_G/S/ENUMOUTPUT: OK (Not Supported) test VIDIOC_G/S_AUDOUT: OK (Not Supported) Outputs: 0 Audio Outputs: 0 Modulators: 0 Input/Output configuration ioctls: test VIDIOC_ENUM/G/S/QUERY_STD: OK (Not Supported) test VIDIOC_ENUM/G/S/QUERY_DV_TIMINGS: OK (Not Supported) test VIDIOC_DV_TIMINGS_CAP: OK (Not Supported) test VIDIOC_G/S_EDID: OK (Not Supported) Control ioctls (Input 0): test VIDIOC_QUERY_EXT_CTRL/QUERYMENU: OK (Not Supported) test VIDIOC_QUERYCTRL: OK (Not Supported) test VIDIOC_G/S_CTRL: OK (Not Supported) test VIDIOC_G/S/TRY_EXT_CTRLS: OK (Not Supported) test VIDIOC_(UN)SUBSCRIBE_EVENT/DQEVENT: OK (Not Supported) test VIDIOC_G/S_JPEGCOMP: OK (Not Supported) Standard Controls: 0 Private Controls: 0 Format ioctls (Input 0): test VIDIOC_ENUM_FMT/FRAMESIZES/FRAMEINTERVALS: OK test VIDIOC_G/S_PARM: OK test VIDIOC_G_FBUF: OK (Not Supported) test VIDIOC_G_FMT: OK test VIDIOC_TRY_FMT: OK test VIDIOC_S_FMT: OK test VIDIOC_G_SLICED_VBI_CAP: OK (Not Supported) test Cropping: OK (Not Supported) test Composing: OK (Not Supported) test Scaling: OK (Not Supported) Codec ioctls (Input 0): test VIDIOC_(TRY_)ENCODER_CMD: OK (Not Supported) test VIDIOC_G_ENC_INDEX: OK (Not Supported) test VIDIOC_(TRY_)DECODER_CMD: OK (Not Supported) Buffer ioctls (Input 0): test VIDIOC_REQBUFS/CREATE_BUFS/QUERYBUF: OK test VIDIOC_EXPBUF: OK test Requests: OK (Not Supported) Test input 0: Stream using all formats: test MMAP for Format RGB3, Frame Size 640x480@59.94 Hz: Stride 1920, Field None: OK test MMAP for Format RGB3, Frame Size 640x480@1.00 Hz: Stride 1920, Field None: OK Total for vcam device /dev/video2: 47, Succeeded: 47, Failed: 0, Warnings: 0 ``` ### 6.1.32 虛擬環境測試結果 ```shell root@(none):/tmp/vcam# v4l2-compliance -d /dev/video0 -f v4l2-compliance 1.22.1, 64 bits, 64-bit time_t Compliance test for vcam device /dev/video0: Driver Info: Driver name : vcam Card type : vcam Bus info : platform: virtual Driver version : 6.1.35 Capabilities : 0x85200001 Video Capture Read/Write Streaming Extended Pix Format Device Capabilities Device Caps : 0x05200001 Video Capture Read/Write Streaming Extended Pix Format Required ioctls: test VIDIOC_QUERYCAP: OK test invalid ioctls: OK Allow for multiple opens: test second /dev/video0 open: OK test VIDIOC_QUERYCAP: OK test VIDIOC_G/S_PRIORITY: OK test for unlimited opens: OK Debug ioctls: test VIDIOC_DBG_G/S_REGISTER: OK (Not Supported) test VIDIOC_LOG_STATUS: OK (Not Supported) Input ioctls: test VIDIOC_G/S_TUNER/ENUM_FREQ_BANDS: OK (Not Supported) test VIDIOC_G/S_FREQUENCY: OK (Not Supported) test VIDIOC_S_HW_FREQ_SEEK: OK (Not Supported) test VIDIOC_ENUMAUDIO: OK (Not Supported) test VIDIOC_G/S/ENUMINPUT: OK test VIDIOC_G/S_AUDIO: OK (Not Supported) Inputs: 1 Audio Inputs: 0 Tuners: 0 Output ioctls: test VIDIOC_G/S_MODULATOR: OK (Not Supported) test VIDIOC_G/S_FREQUENCY: OK (Not Supported) test VIDIOC_ENUMAUDOUT: OK (Not Supported) test VIDIOC_G/S/ENUMOUTPUT: OK (Not Supported) test VIDIOC_G/S_AUDOUT: OK (Not Supported) Outputs: 0 Audio Outputs: 0 Modulators: 0 Input/Output configuration ioctls: test VIDIOC_ENUM/G/S/QUERY_STD: OK (Not Supported) test VIDIOC_ENUM/G/S/QUERY_DV_TIMINGS: OK (Not Supported) test VIDIOC_DV_TIMINGS_CAP: OK (Not Supported) test VIDIOC_G/S_EDID: OK (Not Supported) Control ioctls (Input 0): test VIDIOC_QUERY_EXT_CTRL/QUERYMENU: OK (Not Supported) test VIDIOC_QUERYCTRL: OK (Not Supported) test VIDIOC_G/S_CTRL: OK (Not Supported) test VIDIOC_G/S/TRY_EXT_CTRLS: OK (Not Supported) test VIDIOC_(UN)SUBSCRIBE_EVENT/DQEVENT: OK (Not Supported) test VIDIOC_G/S_JPEGCOMP: OK (Not Supported) Standard Controls: 0 Private Controls: 0 Format ioctls (Input 0): test VIDIOC_ENUM_FMT/FRAMESIZES/FRAMEINTERVALS: OK test VIDIOC_G/S_PARM: OK test VIDIOC_G_FBUF: OK (Not Supported) test VIDIOC_G_FMT: OK test VIDIOC_TRY_FMT: OK test VIDIOC_S_FMT: OK test VIDIOC_G_SLICED_VBI_CAP: OK (Not Supported) test Cropping: OK (Not Supported) test Composing: OK (Not Supported) test Scaling: OK (Not Supported) Codec ioctls (Input 0): test VIDIOC_(TRY_)ENCODER_CMD: OK (Not Supported) test VIDIOC_G_ENC_INDEX: OK (Not Supported) test VIDIOC_(TRY_)DECODER_CMD: OK (Not Supported) Buffer ioctls (Input 0): test VIDIOC_REQBUFS/CREATE_BUFS/QUERYBUF: OK test VIDIOC_EXPBUF: OK test Requests: OK (Not Supported) Test input 0: Stream using all formats: test MMAP for Format RGB3, Frame Size 640x480@59.94 Hz: Stride 1920, Field None: OK test MMAP for Format RGB3, Frame Size 640x480@1.00 Hz: Stride 1920, Field None: OK Total for vcam device /dev/video0: 47, Succeeded: 47, Failed: 0, Warnings: 0 ``` ## TODO: 排除記憶體錯誤 運用 [kmemleak](https://www.kernel.org/doc/html/latest/dev-tools/kmemleak.html) 和 [kasan](https://www.kernel.org/doc/html/latest/dev-tools/kasan.html) 排除記憶體錯誤 但是在使用 [kmemleak](https://www.kernel.org/doc/html/latest/dev-tools/kmemleak.html) 前,需要先編譯核心,使其可以運作,因為這個工具並非是正常使用者會使用到的,故開發者沒必要讓其在所有環境下都執行(另一方面也不是所有環境下都能讓此工具運做),故其只能在特定狀況下運行,而要如何打造這個環境就是首要的目標 ### 打造虛擬環境 根據 [測試 Linux 核心的虛擬化環境](https://hackmd.io/@sysprog/linux-virtme) 來設定環境 在使用 virtme 選取預設核心組態並編譯階段,需要確保 CONFIG_DEBUG_INFO 在組態中有被開啟,以利後續實驗使用,並且在[kmemleak](https://www.kernel.org/doc/html/latest/dev-tools/kmemleak.html) 文件中寫到,需要先將 CONFIG_DEBUG_KMEMLEAK enable。 執行 `$ grep CONFIG_DEBUG_INFO .config` 若輸出沒有出現 `CONFIG_DEBUG_INFO=y` ,如下 ``` CONFIG_DEBUG_INFO_NONE=y # CONFIG_DEBUG_INFO_DWARF_TOOLCHAIN_DEFAULT is not set # CONFIG_DEBUG_INFO_DWARF4 is not set # CONFIG_DEBUG_INFO_DWARF5 is not set ``` 會需要執行 `make menuconfig` 進行設定 6.1.x 的[CONFIG_DEBUG_INFO](https://www.kernelconfig.io/config_debug_info?q=&kernelversion=6.1.32&arch=x86)和[CONFIG_DEBUG_KMEMLEAK](https://www.kernelconfig.io/config_debug_kmemleak?arch=x86&kernelversion=6.1.32)是依照此位置 ![](https://hackmd.io/_uploads/SkFNwpaU3.png) 在你的抵達位置時不一定會找到 Compile the kernel with debug info 這個選項,你需要將 Debug information 設定成任一顯示方法,個人是設定成用 DWARF 4代格式顯示 ![](https://hackmd.io/_uploads/HkhyOa6U3.png) 隨後就會出現其選項了 ![](https://hackmd.io/_uploads/r1_V_66L2.png) 隨後就出現了 `CONFIG_DEBUG_INFO=y` ``` $ grep CONFIG_DEBUG_INFO .config CONFIG_DEBUG_INFO=y # CONFIG_DEBUG_INFO_NONE is not set # CONFIG_DEBUG_INFO_DWARF_TOOLCHAIN_DEFAULT is not set CONFIG_DEBUG_INFO_DWARF4=y # CONFIG_DEBUG_INFO_DWARF5 is not set # CONFIG_DEBUG_INFO_REDUCED is not set CONFIG_DEBUG_INFO_COMPRESSED=y # CONFIG_DEBUG_INFO_SPLIT is not set ``` ![](https://hackmd.io/_uploads/HyN8qmx_n.png) 隨後等待其 make 結束(會需要一段時間),在編譯結束後,預期可見以下訊息: ```shell Kernel: arch/x86/boot/bzImage is ready ``` 隨後便可透過以下命令,在 QEMU 虛擬機器中啟動 Linux 核心: ```shell $ virtme-run --kdir . --mods=auto ``` :::warning 要注意一點,在 virtme 中,除了/tmp 外的路徑都是唯獨的,若要寫入需要切換到 host 端 ::: ![](https://hackmd.io/_uploads/ryspXjkP3.png) > 確保 Debug Filesystem 是開啟的 ### 編譯和測試核心模組 照著[測試 Linux 核心的虛擬化環境](https://hackmd.io/@sysprog/linux-virtme#%E5%88%A9%E7%94%A8-virtme-%E5%BB%BA%E7%AB%8B-Linux-%E6%A0%B8%E5%BF%83%E6%B8%AC%E8%A9%A6%E7%92%B0%E5%A2%83)中,編譯和測試核心模組的步驟,撰寫 hello.c 以及Makefile ```c #include <linux/init.h> #include <linux/module.h> MODULE_LICENSE("GPL"); static int hello_init(void) { printk(KERN_ALERT "Hello World! - init\n"); return 0; } static void hello_exit(void) { printk(KERN_ALERT "Hello World! - exit\n"); } module_init(hello_init); module_exit(hello_exit); ``` ``` KDIR=/home/ubuntu/linux obj-m += hello.o PWD := $(shell pwd) all: $(MAKE) -C $(KDIR) M=$(PWD) modules clean: $(MAKE) -C $(KDIR) M=$(PWD) clean ``` :::warning 在[測試 Linux 核心的虛擬化環境](https://hackmd.io/@sysprog/linux-virtme#%E5%88%A9%E7%94%A8-virtme-%E5%BB%BA%E7%AB%8B-Linux-%E6%A0%B8%E5%BF%83%E6%B8%AC%E8%A9%A6%E7%92%B0%E5%A2%83)中, Makefile 的代碼有誤 `KDIR := $(PWD)/..` 這行會導致 KDIR 導引到錯誤位置,故將其刪除 以及要將 `KDIR=/home/ubuntu/linux` 設定為自己存放虛擬環境的目錄 ::: ```shell # make make -C /home/hank/linux2023/virtme/linux M=/tmp/hello modules make[1]: Entering directory '/home/hank/linux2023/virtme/linux' warning: the compiler differs from the one used to build the kernel The kernel was built by: x86_64-linux-gnu-gcc (Ubuntu 11.3.0-1ubuntu1~22.04.1) 11.3.0 You are using: gcc (Ubuntu 11.3.0-1ubuntu1~22.04.1) 11.3.0 CC [M] /tmp/hello/hello.o MODPOST /tmp/hello/Module.symvers CC [M] /tmp/hello/hello.mod.o LD [M] /tmp/hello/hello.ko make[1]: Leaving directory '/home/hank/linux2023/virtme/linux' ``` 隨後便可測試載入以及移除模組 ```shell # insmod hello.ko [ 902.474833] Hello World! - init # rmmod hello [ 934.470132] Hello World! - exit ``` ### 嘗試建立 ko 檔 因為 vcam 需要 videobuf2-vmalloc videobuf2-v4l2 兩個核心模組作為前製, ``` $ sudo make modules_install modules=videobuf2-vmalloc [sudo] hank 的密碼: make[1]: *** 沒有規則可製作目標「videobuf2-vmalloc」,由「__modinst」 需求。 停止。 make: *** [Makefile:1941:modules_install] 錯誤 2 ``` 發現 make modules 也只有少少幾個 ko 檔被編譯出來 ``` $ make modules UPD include/generated/compile.h CC scripts/mod/empty.o MKELF scripts/mod/elfconfig.h HOSTCC scripts/mod/modpost.o CC scripts/mod/devicetable-offsets.s HOSTCC scripts/mod/file2alias.o HOSTCC scripts/mod/sumversion.o HOSTLD scripts/mod/modpost CC kernel/bounds.s CC arch/x86/kernel/asm-offsets.s CALL scripts/checksyscalls.sh DESCEND objtool CC [M] fs/efivarfs/inode.o CC [M] fs/efivarfs/file.o CC [M] fs/efivarfs/super.o CC [M] fs/efivarfs/vars.o LD [M] fs/efivarfs/efivarfs.o CC [M] drivers/thermal/intel/x86_pkg_temp_thermal.o CC [M] net/netfilter/nf_log_syslog.o CC [M] net/netfilter/xt_mark.o CC [M] net/netfilter/xt_nat.o CC [M] net/netfilter/xt_LOG.o CC [M] net/netfilter/xt_MASQUERADE.o CC [M] net/netfilter/xt_addrtype.o CC [M] net/ipv4/netfilter/iptable_nat.o MODPOST Module.symvers LDS scripts/module.lds CC [M] drivers/thermal/intel/x86_pkg_temp_thermal.mod.o LD [M] drivers/thermal/intel/x86_pkg_temp_thermal.ko CC [M] fs/efivarfs/efivarfs.mod.o LD [M] fs/efivarfs/efivarfs.ko CC [M] net/ipv4/netfilter/iptable_nat.mod.o LD [M] net/ipv4/netfilter/iptable_nat.ko CC [M] net/netfilter/nf_log_syslog.mod.o LD [M] net/netfilter/nf_log_syslog.ko CC [M] net/netfilter/xt_LOG.mod.o LD [M] net/netfilter/xt_LOG.ko CC [M] net/netfilter/xt_MASQUERADE.mod.o LD [M] net/netfilter/xt_MASQUERADE.ko CC [M] net/netfilter/xt_addrtype.mod.o LD [M] net/netfilter/xt_addrtype.ko CC [M] net/netfilter/xt_mark.mod.o LD [M] net/netfilter/xt_mark.ko CC [M] net/netfilter/xt_nat.mod.o LD [M] net/netfilter/xt_nat.ko ``` <!-- 在 virtme 中無法裝上 v4l2 模組,因為 `/lib/modules/6.3.12/kernel/driver` 中並沒有 media 的目錄 ```shell /lib/modules/6.1.32/kernel/drivers$ ls -a . .. thermal ``` --> <!-- 使用 `modinfo` 確認在 host 端所載入的模組位置,確認其是在 `/lib/modules/5.19.0-45-generic/kernel/drivers/media/common/videobuf2/videobuf2-v4l2.ko` 中,這是在 6.3.12 中沒有的路徑,接來就是要想辦法去建構出 6.3.12 版的 videobuf2-v4l2.ko 來 ```shell $ modinfo videobuf2-v4l2 filename: /lib/modules/5.19.0-45-generic/kernel/drivers/media/common/videobuf2/videobuf2-v4l2.ko ``` https://www.kernel.org/doc/Documentation/kbuild/kbuild.txt --> <!-- 由於原先使用的版本是5.19.0,而虛擬環境是6.1.32,所以在 `lib/modules` 中並沒有 6.1.32 的目錄與相關文件,故在 makefile 中的 `modules_install` target 會變成建立一個 6.1.32 的目錄,並且將 `modules.oreder` 的 ko 檔加入原本的目錄(也就是於只有`modules.oreder` 中的 ko 檔會被建立) ``` modules_install: $(modinst_pre) PHONY += __modinst_pre __modinst_pre: @rm -rf $(MODLIB)/kernel @rm -f $(MODLIB)/source @mkdir -p $(MODLIB)/kernel @ln -s $(abspath $(srctree)) $(MODLIB)/source @if [ ! $(objtree) -ef $(MODLIB)/build ]; then \ rm -f $(MODLIB)/build ; \ ln -s $(CURDIR) $(MODLIB)/build ; \ fi @sed 's:^:kernel/:' modules.order > $(MODLIB)/modules.order @cp -f modules.builtin $(MODLIB)/ @cp -f $(objtree)/modules.builtin.modinfo $(MODLIB)/ endif # CONFIG_MODULES ``` --> 在與教授討論並重新詳閱 kernelconfig 加上反覆驗證後,得知需要在編譯核心時設定 [`CONFIG_MEDIA_SUPPORT = y`](https://www.kernelconfig.io/CONFIG_MEDIA_SUPPORT?kernelversion=6.1.35) 才會編譯出相關的 ko 檔 ![](https://hackmd.io/_uploads/B1KJvTMd3.png) > [videobuf2-vmalloc.ko](https://www.kernelconfig.io/CONFIG_VIDEOBUF_VMALLOC?kernelversion=6.1.35) 與 [videobuf2-v4l2.ko](https://www.kernelconfig.io/CONFIG_VIDEOBUF2_V4L2?q=videobuf2-v4l2.ko&kernelversion=6.1.35&arch=x86) 都依賴於此 <!-- ![](https://hackmd.io/_uploads/Bya3kAzuh.png) 一開始裡面的東西很少,要將 Media device type 內的選項全選才會多出許多選項 ![](https://hackmd.io/_uploads/Sy0-gAzO3.png) --> 使用 `/` 搜尋 VIDEOBUF2_V4L2,找出使其 enable 的設定 ![](https://hackmd.io/_uploads/r1BxZ0zd3.png) ![](https://hackmd.io/_uploads/ByDOI0fu3.png) > 需要將自動選擇附加功能的這個選項關掉 ![](https://hackmd.io/_uploads/BkYVLRzdn.png) 隨後 make 完使用 make modules_install ,便可見到需要的那兩個 ko 檔 ``` root@(none):/# modprobe videobuf2_v4l2 videobuf2_vmalloc root@(none):/# lsmod Module Size Used by videobuf2_vmalloc 16384 0 videobuf2_memops 16384 1 videobuf2_vmalloc videobuf2_v4l2 36864 0 videobuf2_common 65536 3 videobuf2_vmalloc,videobuf2_v4l2,videobuf2_memops ``` 架設成功 ### 嘗試在虛擬環境中 make vcam ``` root@(none):/tmp/vcam# make make -C /lib/modules/6.1.35/build M=/tmp/vcam modules make[1]: Entering directory '/home/hank/linux2023/virtme/linux' warning: the compiler differs from the one used to build the kernel The kernel was built by: x86_64-linux-gnu-gcc (Ubuntu 11.3.0-1ubuntu1~22.04.1) 11.3.0 You are using: gcc (Ubuntu 11.3.0-1ubuntu1~22.04.1) 11.3.0 CC [M] /tmp/vcam/module.o CC [M] /tmp/vcam/control.o ... [ 190.819560] Tasks state (memory values in pages): [ 190.819844] [ pid ] uid tgid total_vm rss pgtables_bytes swapents oom_score_adj name [ 190.820366] [ 98] 0 98 5780 528 61440 0 0 systemd-udevd [ 190.820890] [ 163] 0 163 1193 151 45056 0 0 bash [ 190.821362] [ 169] 0 169 795 77 45056 0 0 make [ 190.821871] [ 171] 0 171 879 165 45056 0 0 make [ 190.822344] [ 386] 0 386 897 169 45056 0 0 make [ 190.822821] [ 409] 0 409 724 38 49152 0 0 sh [ 190.823284] [ 410] 0 410 939 43 40960 0 0 gcc [ 190.823756] [ 411] 0 411 18841 9025 188416 0 0 cc1 [ 190.824284] oom-kill:constraint=CONSTRAINT_NONE,nodemask=(null),cpuset=/,mems_allowed=0,task=cc1,pid=411,uid=0 [ 190.824992] Out of memory: Killed process 411 (cc1) total-vm:75364kB, anon-rss:35828kB, file-rss:272kB, shmem-rss:0kB, UID:0 pgtables:184kB oom_score_adj:0 gcc: fatal error: Killed signal terminated program cc1 compilation terminated. make[2]: *** [scripts/Makefile.build:250: /tmp/vcam/control.o] Error 1 make[1]: *** [Makefile:2012: /tmp/vcam] Error 2 make[1]: Leaving directory '/home/hank/linux2023/virtme/linux' make: *** [Makefile:14: kmod] Error 2 root@(none):/tmp/vcam# ``` 發生了 oom (out of memory),由於執行 kmemleak 需要較大的記憶體,故退出虛擬環境利用`virtme-run --kdir . --mods=auto --qemu-opts -m 2048` 配置 2048 的記憶體大小 再度執行 make ``` root@(none):/tmp/vcam# make make -C /lib/modules/6.1.35/build M=/tmp/vcam modules make[1]: Entering directory '/home/hank/linux2023/virtme/linux' warning: the compiler differs from the one used to build the kernel The kernel was built by: x86_64-linux-gnu-gcc (Ubuntu 11.3.0-1ubuntu1~22.04.1) 11.3.0 You are using: gcc (Ubuntu 11.3.0-1ubuntu1~22.04.1) 11.3.0 CC [M] /tmp/vcam/module.o CC [M] /tmp/vcam/control.o CC [M] /tmp/vcam/device.o CC [M] /tmp/vcam/videobuf.o CC [M] /tmp/vcam/fb.o LD [M] /tmp/vcam/vcam.o MODPOST /tmp/vcam/Module.symvers CC [M] /tmp/vcam/vcam.mod.o LD [M] /tmp/vcam/vcam.ko make[1]: Leaving directory '/home/hank/linux2023/virtme/linux' cc -O2 -Wall -Wextra -pedantic -std=c99 -o vcam-util vcam-util.c ``` 成功通過 隨後將 vcam.ko 架設上,並開始測試 ```shell root@(none):/tmp/vcam# insmod vcam.ko [65600.023786] vcam: loading out-of-tree module taints kernel. [65600.026288] Console: switching to colour frame buffer device 80x30 root@(none):/tmp/vcam# lsmod Module Size Used by vcam 28672 1 videobuf2_vmalloc 16384 1 vcam videobuf2_memops 16384 1 videobuf2_vmalloc videobuf2_v4l2 36864 1 vcam videobuf2_common 65536 4 videobuf2_vmalloc,vcam,videobuf2_v4l2,videobuf2_memops root@(none):/tmp/vcam# ./vcam-util -l Available virtual V4L2 compatible devices: 1. fb0(640,480,1/1,rgb24) -> /dev/video0 ``` ### kmemleak 的使用與架設 執行 `# mount -t debugfs nodev /sys/kernel/debug/` 後,利用 `mount | grep "debug"` 確認 ```shell root@(none):/# mount | grep "debug" debugfs on /sys/kernel/debug type debugfs (rw,relatime) ``` 確認已成功裝上 <!-- ### 過往被砍掉的東西 --> <!-- <s> 一開始先在 [The Linux Kernel Archives](https://www.kernel.org/) 下載 linux-6.3.5,並且解壓縮在/usr/src,依照[鳥哥的教學](https://linux.vbird.org/linux_basic/mandrake9/0540kernel-2.4.18.php)似乎是要放在/usr/src/linux,但我的目錄結構中並沒有 /usr/src/linux,而且所有的核心目錄都是在 /usr/src 中,故解壓縮到 /usr/src </s> --> <!-- :::warning 不需要特別解壓縮到 `/usr/src` 目錄,在一般使用者的工作目錄編譯核心即可。鳥哥的教學關於 Linux 核心編譯的描述已過時。 :notes: jserv ::: --> <!-- ![](https://hackmd.io/_uploads/S1B3fQILh.png) ![](https://hackmd.io/_uploads/Hk9Km78Ln.png) ![](https://hackmd.io/_uploads/HkDxVmLLn.png) --> <!-- :::warning 不要裝太新的 Linux 核心 (v6.3+),參照 [測試 Linux 核心的虛擬化環境](https://hackmd.io/@sysprog/linux-virtme) 來設定和測試。 :notes: jserv ::: -->