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,而不是在終端上,除錯前最好用
與應用程式對比。核心模組有以下不同:
應用程式從頭(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