Linux下编写内核模块实现1对1聊天以及多发多收实现

这篇博客介绍了如何使用Linux内核模块创建一对一和多对多聊天程序。通过PV操作和信号量,实现了内核缓冲区的数据读写同步。在一对一聊天程序中,使用一个信号量来协调读写进程。而在多对多聊天程序中,引入了等待队列和额外的计数变量,使得多个读者可以并发读取,但每个读者只能读取一次数据。
摘要由CSDN通过智能技术生成

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文件相同

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

volcanical

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值