Linux内核模块聊天程序
一对一聊天程序
设计一个信号量,当有人写入时唤醒读出程序,从内核缓冲区读出数据
内核的新建,实例与卸载相关代码可以自行查询,一对一只需设置一个信号量即可实现
内核驱动
#include <linux/module.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/uaccess.h>
#include <linux/semaphore.h>
#include <linux/sched.h>
#include <linux/wait.h>
#include <linux/cdev.h>
#include <linux/kdev_t.h>
#include <linux/device.h>
#include <linux/types.h>
#define MAXNUM 100
#define MAJOR_NUM 290
struct dev{
struct cdev devm; //字符设备
struct semaphore sem; //PV操作
wait_queue_head_t outq;
int flag; //唤醒标志
char buffer[MAXNUM + 1];
char *rd, *wr, *end;
}globalvar;
static struct class *my_class;
int major = MAJOR_NUM;
static ssize_t globalvar_read(struct file *filp, char __user *buf, size_t len, loff_t *ppos)
{
if(wait_event_interruptible(globalvar.outq, globalvar.flag!=0)) //不可读时 阻塞读进程
{
return -ERESTARTSYS;
}
if(down_interruptible(&globalvar.sem)) //P 操作
{
return -ERESTARTSYS;
}
globalvar.flag = 0;
if(globalvar.rd < globalvar.wr)
len = min(len,(size_t)(globalvar.wr - globalvar.rd)); //更新读写长度
else
len = min(len,(size_t)(globalvar.end - globalvar.rd));
if(copy_to_user(buf,globalvar.rd,len))
{
printk(KERN_ALERT"copy failed\n");
up(&globalvar.sem);
return -EFAULT;
}
globalvar.rd = globalvar.rd + len;
if(globalvar.rd == globalvar.end)
globalvar.rd = globalvar.buffer; //字符缓冲区循环
up(&globalvar.sem); //V 操作
return len;
}
static ssize_t globalvar_write(struct file *filp, const char __user *buf, size_t len, loff_t *ppos)
{
if(down_interruptible(&globalvar.sem)) //P 操作
{
return -ERESTARTSYS;
}
if(globalvar.rd <= globalvar.wr)
len = min(len,(size_t)(globalvar.end - globalvar.wr));
else
len = min(len,(size_t)(globalvar.rd - globalvar.wr-1));
if(copy_from_user(globalvar.wr,buf,len))
{
up(&globalvar.sem); //V 操作
return -EFAULT;
}
globalvar.wr = globalvar.wr + len;
if(globalvar.wr == globalvar.end)
globalvar.wr = globalvar.buffer; //循环
up(&globalvar.sem);//V 操作
globalvar.flag = 1; //条件成立,可以唤醒读进程
wake_up_interruptible(&globalvar.outq); //唤醒读进程
return len;
}
static int globalvar_open(struct inode *inode,struct file *filp)
{
try_module_get(THIS_MODULE); //模块计数加一
printk("This chrdev is in open\n");
return(0);
}
static int globalvar_release(struct inode *inode,struct file *filp)
{
module_put(THIS_MODULE); //模块计数减一
printk("This chrdev is in release\n");
return(0);
}
struct file_operations globalvar_fops =
{
.read = globalvar_read,
.write = globalvar_write,
.open = globalvar_open,
.release = globalvar_release,
};
int globalvar_init(void)
{
dev_t dev = MKDEV(major, 0);
int result;
if(major)
{
//静态申请设备编号
result = register_chrdev_region(dev, 1, "charmem");
}
else
{
//动态分配设备号
result = alloc_chrdev_region(&dev, 0, 1, "charmem");
major = MAJOR(dev);
}
if(result < 0)
return result;
cdev_init(&globalvar.devm, &globalvar_fops);
globalvar.devm.owner = THIS_MODULE;
cdev_add(&globalvar.devm, dev, 1);
sema_init(&globalvar.sem, 1);
init_waitqueue_head(&globalvar.outq);
globalvar.rd = globalvar.buffer;
globalvar.wr = globalvar.buffer;
globalvar.end = globalvar.buffer + MAXNUM;
globalvar.flag = 0;
my_class = class_create(THIS_MODULE, "chardev0");
device_create(my_class, NULL, dev, NULL, "chardev0");
return 0;
}
static void globalvar_exit(void)
{
device_destroy(my_class, MKDEV(major, 0));
class_destroy(my_class);
cdev_del(&globalvar.devm);
unregister_chrdev_region(MKDEV(major, 0), 1);
}
module_init(globalvar_init);
module_exit(globalvar_exit);
MODULE_LICENSE("GPL");
读者模块
#include<sys/types.h> //read
#include<unistd.h>
#include<sys/stat.h>
#include<stdio.h>
#include<fcntl.h>
#include<string.h>
int main()
{
int fd,i;
char msg[101];
fd= open("/dev/chardev0",O_RDWR,S_IRUSR|S_IWUSR);
if(fd!=-1)
{
while(1)
{
for(i=0;i<101;i++) //初始化
msg[i]='\0';
read(fd,msg,100);
printf("%s\n",msg);
if(strcmp(msg,"quit()")==0)
{
close(fd);
break;
}
}
}
else
{
printf("device open failure,%d\n",fd);
}
return 0;
}
写者模块
#include<sys/types.h> //writer
#include<unistd.h>
#include<sys/stat.h>
#include<stdio.h>
#include<fcntl.h>
#include<string.h>
int main()
{
int fd;
char msg[100];
fd= open("/dev/chardev0",O_RDWR,S_IRUSR|S_IWUSR);
if(fd!=-1)
{
while(1)
{
printf("Please input the globar:\n");
scanf("%s",msg);
write(fd,msg,strlen(msg));
if(strcmp(msg,"quit()")==0)
{
close(fd);
break;
}
}
}
else
{
printf("device open failure\n");
}
return 0;
}
Makefile文件
CONFIG_MODULE_SIG=n
ifneq ($(KERNELRELEASE),)
obj-m :=dev.o
else
KERNELDIR := /lib/modules/$(shell uname -r)/build
PWD := $(shell pwd)
modules:
$(MAKE) -C $(KERNELDIR) M=$(PWD) modules
clean:
$(MAKE) -C $(KERNELDIR) M=$(PWD) clean
endif
多对多群聊
这里实现的是多写者多读者
这里需要设计一下,因为一对一是只要有写者写了,读者就会被唤醒,但是当有多个读者时,如何唤醒多个读者?如何确保每个读者只读一次?
这里修改了原本的唤醒信号量flag,又增加了一个等待队列,用来存放已经读了一次数据的读者。
Flag值为拥有的读进程个数,有几个读进程就有几个flag,因此读一次可以唤醒n个写进程
还需要另外一个互斥量,就是当多个读者一起读时,每个进程只能读一次,因此:
当读者读了一次时,并且还有其他读者没读(flag!=0),需要把读进程阻塞,当且仅当最后一个读进程读完时,读进程被唤醒。
#include <linux/module.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/uaccess.h>
#include <linux/semaphore.h>
#include <linux/sched.h>
#include <linux/wait.h>
#include <linux/cdev.h>
#include <linux/kdev_t.h>
//.................
#include <linux/device.h>
//...................
#include <linux/types.h>
#define MAXNUM 100
#define MAJOR_NUM 290
struct dev{
struct cdev devm; //zi fu she bei
struct semaphore sem; //PV
wait_queue_head_t outq,que;
int flag; //huan xing
char buffer[MAXNUM + 1];
char *rd, *wr, *end;
}globalvar;
//................
static struct class *my_class;
//................
int major = MAJOR_NUM;
int num = -1;
int copy_num;
static ssize_t globalvar_read(struct file *filp, char __user *buf, size_t len, loff_t *ppos)
{
if(wait_event_interruptible(globalvar.outq, globalvar.flag!=0)) //不可读时 阻塞读进程,0 sleep 1wake up
{
return -ERESTARTSYS;
}
// bu bei zu se
if(down_interruptible(&globalvar.sem)) //P 操作
{
return -ERESTARTSYS;
}
//yao rang bie ren du
globalvar.flag--;
if(copy_to_user(buf,globalvar.rd,len))
{
printk(KERN_ALERT"copy failed\n");
up(&globalvar.sem);
return -EFAULT;
}
copy_num--;
up(&globalvar.sem); //V 操作
if(wait_event_interruptible(globalvar.que, copy_num == 0)) //不可读时 阻塞读进程,0 sleep 1wake up
{
return -ERESTARTSYS;
}
if(copy_num==0) wake_up_interruptible(&globalvar.que);
return len;
}
static ssize_t globalvar_write(struct file *filp, const char __user *buf, size_t len, loff_t *ppos)
{
if(down_interruptible(&globalvar.sem)) //P 操作
{
return -ERESTARTSYS;
}
if(copy_from_user(globalvar.wr,buf,100))
{
up(&globalvar.sem); //V 操作
return -EFAULT;
}
up(&globalvar.sem);//V 操作
globalvar.flag = num; //条件成立,可以唤醒读进程
copy_num = num;
wake_up_interruptible(&globalvar.outq); //唤醒读进程
return len;
}
static int globalvar_open(struct inode *inode,struct file *filp)
{
num++;
try_module_get(THIS_MODULE); //模块计数加一
printk("This chrdev is in open\n");
return(0);
}
static int globalvar_release(struct inode *inode,struct file *filp)
{
num--;
module_put(THIS_MODULE); //模块计数减一
printk("This chrdev is in release\n");
return(0);
}
struct file_operations globalvar_fops =
{
.read = globalvar_read,
.write = globalvar_write,
.open = globalvar_open,
.release = globalvar_release,
};
int globalvar_init(void)
{
//major and mijor to the place in table
dev_t dev = MKDEV(major, 0);
int err = 0;
int result = 0;
//register
if(major)
{
//静态申请设备编号
result = register_chrdev_region(dev, 1, "charmem");
}
else
{
//动态分配设备号
result = alloc_chrdev_region(&dev, 0, 1, "charmem");
major = MAJOR(dev);
}
//failure
if(result < 0)
return result;
cdev_init(&globalvar.devm, &globalvar_fops);
globalvar.devm.owner = THIS_MODULE;
err = cdev_add(&globalvar.devm, dev, 1);
if(err)
printk(KERN_INFO"Error %d adding char_mem device",err);
else{
//sema init
sema_init(&globalvar.sem, 1);
//waitquere init
init_waitqueue_head(&globalvar.outq);
init_waitqueue_head(&globalvar.que);
globalvar.rd = globalvar.buffer;
globalvar.wr = globalvar.buffer;
globalvar.end = globalvar.buffer + MAXNUM;
globalvar.flag = 0;
}
//.............................
my_class = class_create(THIS_MODULE, "chardev0");
device_create(my_class, NULL, dev, NULL, "chardev0");
//..............................
return 0;
}
static void globalvar_exit(void)
{
//...........................................
device_destroy(my_class, MKDEV(major, 0));
class_destroy(my_class);
//.............................................
cdev_del(&globalvar.devm);
unregister_chrdev_region(MKDEV(major, 0), 1);
}
module_init(globalvar_init);
module_exit(globalvar_exit);
MODULE_LICENSE("GPL");
读写程序和Makefile文件相同