1. 程式人生 > >Linux-核心模組開發

Linux-核心模組開發

前言:最近在做入職公司的作業系統培訓作業,其中第三個作業是關於Linux核心模組開發的,於是乎又重新拿起了Linux裝置驅動程式這本書,看起了申嵌視訊,在CSDN上寫寫學習筆記,在校的最後一個月蛋疼中。。。。。。

首先來看兩段最基礎的程式碼,一個可以動態載入進核心的程式碼和一個Makefile:

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

#define DRIVER_AUTHOR "sty"
#define DRIVER_DESC "HELLO DRIVER"

MODULE_LICENSE("GPL");
MODULE_AUTHOR(DRIVER_AUTHOR);
MODULE_DESCRIPTION(DRIVER_DESC);

int __init hello_init(void)
{
	printk(KERN_INFO "HELLO WORLD\n");
	return 0;
}

void __exit hello_exit(void)
{
	printk(KERN_INFO "goodbye world\n");
}

module_init(hello_init);
module_exit(hello_exit);

ifeq ($(KERNELRELEASE),)#假如變數$(KERNELRELEASE)不等於空,執行下面的語句,否則執行else下面的語句

KERNELDIR ?=/lib/modules/$(shell uname -r)/build#核心原始碼的路徑,build這個其實是個連線檔案,會連線到原始碼目錄(需要變)

PWD := $(shell pwd)#表示核心模組在當前目錄下,modules表示編譯的是核心

modules:
	$(MAKE) -C $(KERNELDIR) M=$(PWD) modules#進入到$(KDIR)目錄下使用它自己的makefile

modules_install:
	$(MAKE) -C $(KERNELDIR) M=$(PWD) modules_install

clean:
	rm -rf *.o *~ core .depend .*.cmd *.ko *.mod.c .tmp_versions

.PHONY: modules modules_install clean

else
    obj-m := hello.o
endif

三個常用命令:載入 insmod (insmod hello.ko),裝載時會呼叫module_init指定函式;解除安裝rmmod (rmmod hello),解除安裝也會呼叫指定函式;檢視 lsmod載入 modprobe (modprobe hello)。

modprobe如同insmod,也是載入一個模組到核心。它的不同之處在於它會根據檔案/lib/modules/<$version>/modules.dep檢視要載入的模組,看它是否還依賴於其他模組,如果是,modprobe會首先找到這些模組,把它們載入到核心。

而printk()是除錯核心的常用手段,列印資訊會在dmesg,而不是在終端上,除錯前最好用

dmesg -c清除掉以前歷史資訊。


與應用程式對比。核心模組有以下不同:

應用程式從頭(main)到尾執行任務,執行結束後從記憶體中消失。核心模組則是先在核心中註冊自己以便於服務將來的某個請求,然後它的初始化函式結束,此時模組仍然存在於核心中,直到解除安裝函式被呼叫,模組才從核心中消失。模組是具有獨立功能的程式,它可以被單獨編譯,但不能獨立執行。它在執行時被連結到核心作為核心的一部分在核心空間執行,這與執行在使用者空間的程序是不同的。模組通常由一組函式和資料結構組成,用來實現一種檔案系統、一個驅動程式或其他核心上層的功能。

     

                                                                                

                                          

核心執行緒模組程式:

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/errno.h>
#include <linux/completion.h>       // for DECLARE_COMPLETION()
#include <linux/jiffies.h>
#include <linux/param.h>
#include <linux/slab.h>
#include <linux/input.h>
#include <linux/init.h>
#include <linux/serio.h>
#include <linux/delay.h>
#include <linux/clk.h>
#include <linux/miscdevice.h>
#include <linux/sched.h>            
#include <linux/delay.h>            // mdelay()
#include <linux/kthread.h> 
#include <linux/cdev.h>

static int kthread_major = 0;
static struct cdev kThreadDevs;//表示的是字元裝置的核心的內部結構

#define DRIVER_AUTHOR "sty"
#define DRIVER_DESC "KTHREAD DRIVER"
#define BEEP_MAGIC 'k'

MODULE_LICENSE("GPL");
MODULE_AUTHOR(DRIVER_AUTHOR);
MODULE_DESCRIPTION(DRIVER_DESC);

static struct task_struct *task;
int flag = 0;

/*
 * Open the device; in fact, there's nothing to do here.
 */
int k_thread_open (struct inode *inode, struct file *filp)
{
	printk(KERN_INFO "k_thread_open!!!\n");
	return 0;
}
ssize_t k_thread_read(struct file *file, char __user *buff, size_t count, loff_t *offp)
{
	printk(KERN_INFO "k_thread_read!!!\n");
	return 0;
}
ssize_t k_thread_write(struct file *file, const char __user *buff, size_t count, loff_t *offp)
{
	printk(KERN_INFO "k_thread_write!!!\n");
	return 0;
}
static long k_thread_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
	printk(KERN_INFO "k_thread_unlock_ioctl!!!\n");
	return 0;
}
static int k_thread_release(struct inode *node, struct file *file)
{
	printk(KERN_INFO "k_thread_release!!!\n");
	return 0;
}

/*
 * Our various sub-devices.
 */
/* Device 0 uses remap_pfn_range */
static struct file_operations kThread_remap_ops = {  //這種結構將裝置節點與驅動程式關聯起來,實現了系統呼叫
	.owner   = THIS_MODULE,
	.open    = k_thread_open,
	.release = k_thread_release,
	.read    = k_thread_read,
	.write   = k_thread_write,
	.unlocked_ioctl   = k_thread_ioctl,	
};

int kthread_sendmsg(void *arg)
{
	printk(" in %s()\n", __FUNCTION__);
	allow_signal(SIGKILL); //使得執行緒可以接收SIGKILL訊號
	mdelay(2000);
	printk("should stop: %d\n",kthread_should_stop());
	while (!signal_pending(current) && !kthread_should_stop()) 
	{
		//使得執行緒可以可以被殺死,也可以再rmmod的時候結束
		printk(" jiffies is %lu\n", jiffies);
		set_current_state(TASK_INTERRUPTIBLE);
		schedule_timeout(HZ * 5);   
		printk("should stop: %d\n",kthread_should_stop());
	}
	printk("Leaving kthread_function\n");
	flag = 1; //flag很關鍵!
	return 0;
}


/*
 * Set up the cdev structure for a device.
 */
static void kthread_setup_cdev(struct cdev *dev, int minor, struct file_operations *fops)
{
	int err, devno = MKDEV(kthread_major, minor);
    
	cdev_init(dev, fops);
	dev->owner = THIS_MODULE;
	dev->ops = fops;
	err = cdev_add (dev, devno, 1);
	/* Fail gracefully if need be */
	if (err)
		printk (KERN_NOTICE "Error %d adding beep%d", err, minor);
}
int __init kthread_init(void)
{
	int result;
	dev_t dev = MKDEV(kthread_major, 0);//dev_t是一個32位型別,前12位表示主號,後20位表示次號
	char dev_name[]="kThread";
	printk(KERN_INFO "CREATE KTHREAD!!!\n");
	/* Figure out our device number. */
	if (kthread_major)
		result = register_chrdev_region(dev, 1, dev_name);
	else {
		result = alloc_chrdev_region(&dev, 0, 1, dev_name);//動態分配裝置號
		kthread_major = MAJOR(dev);
	}
	if (result < 0) {
		printk(KERN_WARNING "beep: unable to get major %d\n", kthread_major);//列印主裝置號
		return result;
	}
	if (kthread_major == 0)
		kthread_major = result;	
	/* Now set up cdev. */
	kthread_setup_cdev(&kThreadDevs, 0, &kThread_remap_ops);
	printk("kthread device installed, with major %d\n", kthread_major);
	printk("The device name is: %s\n", dev_name);

	task = kthread_run(kthread_sendmsg,NULL,"kthread_sendmsg");
	return 0;
}

void __exit kthread_exit(void)
{
	/* 解除安裝驅動程式 */
	cdev_del(&kThreadDevs);
	unregister_chrdev_region(MKDEV(kthread_major, 0), 1);
	printk(KERN_INFO "RELEASE KTHREAD!!!\n");
        if(!flag)
        {
                 if (!IS_ERR(task))
                 {  
                      int ret = kthread_stop(task);  
                      printk(KERN_INFO "First thread function has stopped ,return %d\n", ret);  
                 }                  
       }
       printk("task_struct: 0x%x",task);
       printk(" Goodbye\n");
}

module_init(kthread_init);
module_exit(kthread_exit);


使用者空間程式:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <pthread.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <string.h>
#include <asm/types.h>
#include <linux/netlink.h>
#include <linux/socket.h>

#define BEEP_MAGIC 'k'
#define MAX_PAYLOAD 1024 /* maximum payload size*/

struct sockaddr_nl src_addr, dest_addr;
struct nlmsghdr *nlh = NULL;
struct iovec iov;
int sock_fd;
struct msghdr msg;

void *recvMsg(void *args);
void service();

int main()
{
	int i = 0;
	int dev_fd;
	dev_fd = open("/dev/kThread",O_RDWR | O_NONBLOCK);
	if ( dev_fd == -1 ) 
	{
		printf("Cann't open file /dev/kThread\n");
		exit(1);
	}
	ioctl(dev_fd,1,1); 
	service();
	printf("This is main Thread!\n");
	sleep(3);
	close(dev_fd);
	return 0;
}

void *recvMsg(void *args)
{
   	int t_id;
	t_id = (int) args;
	printf("pthread %d create!!!\n",t_id);
	while(1)
	{
		printf("pthread!!!\n");
		sleep(1);
	}		
}

void service()
{
	int i, ret;
	pthread_t  tid;
	for(i = 0; i < 2; i++) 
	{	
        	ret = pthread_create(&tid,NULL,(void *) recvMsg,(void *)i);
		if(ret != 0)//error 
		{
			perror("fail to created consumer thread\n");
			return;
		}
	   	if(pthread_detach(tid) != 0) //將子程序的狀態設定為分離的,這樣該執行緒執行結束後會自動釋放所有資源
		{
			perror("fail to detach consumer pthread \n");
			return;
		}
	}
}



Linux核心執行緒之深入淺出:http://blog.163.com/jiams_wang/blog/static/303391492012103010374038/

Linux核心多執行緒(一):http://www.cnblogs.com/zhuyp1015/archive/2012/06/11/2545624.html