前言:最近在做入职公司的操作系统培训作业,其中第三个作业是关于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