內核必須懂(四): 撰寫內核驅動

前言

以前的文章裏面說了簡單的.ko文件編譯. 這裏繼續深刻下去. 固然, 仍是從驅動的Hello, world!開始.node


驅動模塊裏的Hello, world!

首先是源碼部分, 這裏因爲是內核, 因此c庫的函數就不能用了, 好比printf這樣的, 要用printk替代, 這裏的k就是指kernel. 而後**__init__exit意味着只有初始化和卸載纔會執行函數, 也就是都只執行一次. module_initmodule_exit**理解爲註冊函數就好了.linux

#include<linux/kernel.h>
#include<linux/init.h>
#include<linux/module.h>

MODULE_LICENSE("Dual BSD/GPL");
MODULE_AUTHOR("Sean Depp");

static int __init hello_init(void)
{
        printk("Hello, sean!\n")  ;
        return 0;
}

static void __exit hello_exit(void)
{
        printk("Exit, sean!\n");
}

module_init(hello_init);
module_exit(hello_exit);
複製代碼

Makefile常規寫法就好, 沒什麼特別要說的. 固然, 你能夠寫的更有效一些, 好比編譯完成以後刪除除了**.ko**文件以外的其它生成文件. 下面給出常規寫法和改進寫法:ios

obj-m:=helloKo.o

PWD:=$(shell pwd)
KER_DIR=/lib/modules/$(shell uname -r)/build

all :
        make -C $(KER_DIR) M=$(PWD) modules
clean :
        make -C $(KER_DIR) M=$(PWD) clean
複製代碼
ifneq ($(KERNELRELEASE),)
        obj-m := helloKo.o
else
        PWD := $(shell pwd)
        KER_DIR ?= /lib/modules/$(shell uname -r)/build
default:
        $(MAKE) -C $(KER_DIR) M=$(PWD) modules
        rm *.order *.symvers *.mod.c *.o .*.o.cmd .*.cmd .tmp_versions -rf
endif
複製代碼

來編譯生成模塊, 以後安裝和卸載.shell

sudo make
sudo insmod helloKo.ko
sudo rmmod helloKo
複製代碼

安裝與卸載

我想你看到了一個提示Makefile:934: "Cannot use CONFIG_STACK_VALIDATION=y, please install libelf-dev, libelf-devel or elfutils-libelf-devel", 很明顯這是一個內核編譯的參數沒生效, 可是編譯成功了. 因而我好奇就裝了一下libelf-dev, 反而就沒法編譯成功了. 這裏若是有大佬能夠告知我爲何, 評論區見, 提早筆芯. 因此這裏暫時無論這個參數了.bash

固然, 能夠用改進的Makefile再操做一次, 此次用lsmod查看一下安裝的模塊, 用dmesg查看信息是否打印出來.函數

安裝與卸載

成功看到模塊和打印的消息:ui

lsmod

dmesg


自定義設備驅動

接下來更進一步, 寫一下驅動代碼, 這裏能夠自定義驅動的open, ioctl等等函數. 這裏的MAJOR_NUMDEVICE_NAME宏要記一下, 一個是設備節點號, 一個是設備名稱.spa

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/delay.h>

#define MAJOR_NUM 231
#define DEVICE_NAME "hellodr"

int DriverOpen( struct inode *pslINode, struct file *pslFileStruct )
{
    printk( KERN_ALERT DEVICE_NAME " hello open.\n" );
    return(0);
}


ssize_t DriverWrite( struct file *pslFileStruct, const char __user *pBuffer, size_t nCount, loff_t *pOffset )
{
    printk( KERN_ALERT DEVICE_NAME " hello write.\n" );
    return(0);
}


long DriverIOControl( struct file *pslFileStruct, unsigned int uiCmd, unsigned long ulArg )
{
    printk( KERN_ALERT DEVICE_NAME " hello ioctl.\n" );
    return(0);
}


struct file_operations hello_flops = {
    .owner      = THIS_MODULE,
    .open       = DriverOpen,
    .write      = DriverWrite,
    .unlocked_ioctl = DriverIOControl
};

static int __init hello_init( void )
{
    int ret;

    ret = register_chrdev( MAJOR_NUM, DEVICE_NAME, &hello_flops );
    if ( ret < 0 )
    {
        printk( KERN_ALERT DEVICE_NAME " can't register major number.\n" );
        return(ret);
    }
    printk( KERN_ALERT DEVICE_NAME " initialized.\n" );
    return(0);
}


static void __exit hello_exit( void )
{
    printk( KERN_ALERT DEVICE_NAME " removed.\n" );
    unregister_chrdev( MAJOR_NUM, DEVICE_NAME );
}

module_init( hello_init );
module_exit( hello_exit );
MODULE_LICENSE( "GPL" );
MODULE_AUTHOR( "Sean Depp" );
複製代碼

用戶態方面, 寫個調用open和ioctl函數的.3d

#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <iostream>
#include <sys/types.h>
/*提供類型pid_t,size_t的定義*/
#include <sys/stat.h>
#include <sys/ioctl.h>
/* BSD and Linux */
#include <stropts.h>
/* XSI STREAMS */
#include <string.h>
using namespace std;

int main( void )
{
	int fd;
	if ( (fd = open( "/dev/hellodr", O_RDWR ) ) < 0 )
	{
		cerr << strerror( errno ) << endl;
		return(-1);
	}

	ioctl( fd, 1, 0 );
	close( fd );

	return(0);
}
複製代碼

Makefile文件也是類似的.調試

ifneq ($(KERNELRELEASE),)
	obj-m := helloDr.o
else
	PWD := $(shell pwd)
	KER_DIR ?= /lib/modules/$(shell uname -r)/build
default:
	$(MAKE) -C $(KER_DIR) M=$(PWD) modules
	rm *.order *.symvers *.mod.c *.o .*.o.cmd .*.cmd .tmp_versions -rf
endif
複製代碼

用g++和make編譯一下文件, 來跑下. 若是你直接跑是不行的, 須要連接節點. 從lsmod打印的信息來看, 已經成功安裝模塊了. 而後你能夠查看/proc/devices中, 也出現了設備名和設備號:

lsmod

設備信息

因此須要連接它們, 以後就能夠成功運行了. 而後dmesg看下打印的信息:

運行

dmesg


最後

目前來看, 內核驅動模塊好像比用戶態程序難不了多少, 可是當程序複雜下去, 調試就會愈加困難了, 不比用戶態. 不少時候, 一個錯誤會很致命, 不少時候, 一個錯誤錯得徹底看不懂. 喜歡記得點贊, 有意見或者建議評論區見哦.

相關文章
相關標籤/搜索