操作系统字符设备驱动---实现聊天应用程序

在B站录了一个简单的视频讲解,有需要的同学可以去康康。
链接: 操作系统实验—字符设备驱动实现聊天程序.

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
此实验的最重要的思路:
聊天程序通过循环读取字符设备数据来达到接收消息的功能,而使用非阻塞键盘读取输入来达到发送消息的功能。主要是学会如何使用字符设备,熟悉字符设备的一些操作函数。

文件夹结构图如下:
在这里插入图片描述

其中只有chardev.c和chat.c和do.sh和del.sh和Makefile是自行编写程序.

chardev.c 字符驱动程序

//chardev.c   字符驱动程序

#include <linux/init.h>
#include <linux/module.h>
#include <linux/cdev.h>
#include <linux/fs.h>
#include <linux/uaccess.h>
#include <linux/vmalloc.h>
#include <linux/slab.h>

#define MAX_DEVICES 10
#define MAJOR_NUM  101
#define MINOR_NUM 0
#define BUFFER_SIZE  1048576

struct _device_data{
    struct cdev  chardev;
    unsigned char *buffer;
    int npos;
}*mydata[MAX_DEVICES];

static ssize_t device_read(struct file *filp, char * buff, size_t len, loff_t* offset)
{
    int nlen=len;
    struct _device_data* pdb=filp->private_data;
    if(nlen>pdb->npos-*offset)
        nlen=pdb->npos-*offset;
    if(copy_to_user(buff, (pdb->buffer)+*offset, nlen))
        return -EFAULT;
    *offset += nlen;
    return nlen;
}

static ssize_t device_write(struct file *filp, const char * buff, size_t len, loff_t* offset)
{
    int nlen=len;
    struct _device_data* pdb=filp->private_data;
    if(nlen>BUFFER_SIZE-pdb->npos)
        nlen=BUFFER_SIZE-pdb->npos;
    if(nlen==0)
        return -ENOMEM;
    if(copy_from_user(&pdb->buffer[pdb->npos], buff, nlen))
        return -EFAULT;
    pdb->npos += nlen;
    return nlen;
}

static int device_open(struct inode *inode, struct file * filp)
{
    int nminor=iminor(inode);
    if(!mydata[nminor]->buffer)
        mydata[nminor]->buffer=(unsigned char *)vmalloc(BUFFER_SIZE);
    if(!mydata[nminor]->buffer)
        return -ENOMEM;
    filp->private_data=mydata[nminor];
    if((filp->f_flags&O_ACCMODE)==O_WRONLY)
        mydata[nminor]->npos=0;
    return 0;
}

static int device_release(struct inode* inode, struct file* filp)
{
/*
    int i;
    for(i=0; i<MAX_DEVICES; ++i){
        cdev_del(&mydata[i]->chardev);
        if(mydata[i]->buffer)
            vfree(mydata[i]->buffer);
        kfree(mydata[i]);
    }
*/  
  return 0;
}

struct file_operations fops = {
    .owner=THIS_MODULE,
    .read=device_read,
    .write=device_write,
    .open=device_open,
    .release=device_release,
};

static int device_init(void)
{
    int i, ndev, ret;
    printk(KERN_INFO "Loading " KBUILD_MODNAME "...\n");
    for(i=0; i<MAX_DEVICES; ++i) {
        mydata[i] =(struct _device_data*)kmalloc(sizeof(*mydata[0]), GFP_KERNEL);
        if(!mydata[i]){
            printk(KERN_EMERG "Can't allocate memory to mydata\n");
            return -ENOMEM;
        }
        mydata[i]->buffer=NULL;
        mydata[i]->npos=0;
        cdev_init(&mydata[i]->chardev, &fops);
        mydata[i]->chardev.owner=THIS_MODULE;
        ndev=MKDEV(MAJOR_NUM, MINOR_NUM+i);
        ret=cdev_add(&mydata[i]->chardev, ndev,1);
        if(ret){
            printk(KERN_EMERG "Can't register device[%d]!\n", i);
            return -1;
        }
    }
    return 0;
}

static void device_exit(void)
{
    int i;
    printk(KERN_INFO "Unloading " KBUILD_MODNAME "...\n");
    for(i=0; i<MAX_DEVICES; ++i){
        cdev_del(&mydata[i]->chardev);
        if(mydata[i]->buffer)
            vfree(mydata[i]->buffer);
        kfree(mydata[i]);
    }
}

module_exit(device_exit);
module_init(device_init);
MODULE_LICENSE("GPL");


Makefile

# Makefile5.3
obj-m := chardev.o
PWD  := $(shell pwd)
KVER ?= $(shell uname -r)
KDIR := /lib/modules/$(KVER)/build
all:
	$(MAKE) -C $(KDIR) M=$(PWD) #注意这里不是空格,是tab
clean:
	rm -rf .*.cmd *.o *.mod.c *.ko .tmp_versions *.mod *.symvers *.order

do.sh shell程序(安装字符设备)

#do.sh   shell程序(安装字符设备)

sudo insmod ./chardev.ko  #安装设备驱动模块
lsmod | grep chardev  #显示安装的设备驱动模块
sudo mknod /dev/chardev0 c 101 0  #创建设备文件,含以下共10个
sudo mknod /dev/chardev1 c 101 1
sudo mknod /dev/chardev2 c 101 2
sudo mknod /dev/chardev3 c 101 3
sudo mknod /dev/chardev4 c 101 4
sudo mknod /dev/chardev5 c 101 5
sudo mknod /dev/chardev6 c 101 6
sudo mknod /dev/chardev7 c 101 7
sudo mknod /dev/chardev8 c 101 8
sudo mknod /dev/chardev9 c 101 9
sudo chmod 666 /dev/chardev*

del.sh shell程序(卸载字符设备)

#del.sh   shell程序(卸载字符设备)

sudo rm /dev/chardev0
sudo rm /dev/chardev1
sudo rm /dev/chardev2
sudo rm /dev/chardev3
sudo rm /dev/chardev5
sudo rm /dev/chardev6
sudo rm /dev/chardev7
sudo rm /dev/chardev8
sudo rm /dev/chardev9

sudo rmmod chardev
dmesg  | tail -1   #倒数1条信息


chat.c 聊天程序

//chat.c  聊天程序

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <memory.h>
#include <unistd.h>
#include <termios.h>

int kbhit(void);

int main()
{
    int fd,i;
    char msg[101];
    
    fd=open("/dev/chardev0", O_RDWR|O_APPEND);
    if(fd==-1){
        fprintf(stderr, "can't openfile chardev0\n");
        exit(0);
    }

    

    while(1){
       
	if( kbhit() ) 
	{
	   char k[100]; //用来存储键盘输入的数据
	   char head[150]; //存放消息头信息和数据,表明是哪个进程写入的消息
	   int pid = getpid();

	   fgets(k,100,stdin);
           //printf("有按键按下,开始读取键盘输入信息,并写入字符设备\n");
           //printf("我是进程%d,我写入的数据为:%s\n",pid,k);
           sprintf(head,"我是进程%d:%s",pid,k);
           write(fd,head,strlen(head));//写入字符设备
        }

         for(i=0;i<101;i++)
             msg[i]='\0';
          
          read(fd,msg,100);
          if(strlen(msg) != 0 ) { //要有新数据才会显示在屏幕上
            //printf("读出的数据为:\n%s\n",msg);
            printf("%s\n",msg);         
          }
         
          sleep(2);
    }
    close(fd);
    return 0;
}

//非阻塞检测按键函数
int kbhit(void)
{ 
	struct termios oldt, newt;
	int ch,oldf; 
	tcgetattr(STDIN_FILENO, &oldt);
	newt=oldt;
	newt.c_lflag &= ~(ICANON | ECHO);
	tcsetattr(STDIN_FILENO, TCSANOW, &newt);
	oldf=fcntl(STDIN_FILENO, F_GETFL, 0);
	fcntl(STDIN_FILENO, F_SETFL, oldf | O_NONBLOCK);

	  ch=getchar();
	  tcsetattr(STDIN_FILENO, TCSANOW, &oldt);
	  fcntl(STDIN_FILENO, F_SETFL, oldf);
	  if(ch != EOF) { 
	     ungetc(ch, stdin);
	     return 1;
	  } 
    return 0;
}


实验进行步骤:
1.把上述代码全部编辑好。
2.在终端输入“make”命令,编译字符驱动。
3.给do.sh和del.sh特权。命令是:chmod +x do.sh 和 chmod +x del.sh
4.运行do.sh安装字符驱动 方法:./do.sh
5.编译chat.c文件,方法:gcc -o chat chat.c
6.运行两次chat程序。方法:./chat

7.开始键盘输入聊天内容。

程序运行结果显示:
在这里插入图片描述

评论 14
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值