小工具知识网

当前位置: 首页 > 行业知识 > linux内核驱动——字符设备实现两个终端单向收发

行业知识

linux内核驱动——字符设备实现两个终端单向收发

2026-11-16 03:42:14 来源:互联网转载

linux内核驱动——字符设备实现两个终端单向收发

目录

  • linux内核驱动——字符设备实现两个终端单向收发
    • 参考
    • 1 简单轮询收发
    • 2 ���待队列实现读取阻塞

      参考

      • Ubuntu18.04添加内核模块(字符设备)

        ()
      • Linux驱动—等待队列

        1 简单轮询收发

        • 创建内核驱动文件chat_dev.c:
          #include      //定义了module_init#include    //最基本的头文件,其中定义了MODULE_LICENSE这一类宏#include        // file_operations结构体定义在该头文件中#include     //class、device结构体的定义位置#include #include     // copy_to_user & copy_from_user #include static const char* device_name = "chat_driver";  //  定义设备名static const char* device_path = "chat";  //  定义设备文件名  /dev/chatstatic struct class *chat_class;    //定义class结构体static struct device *chat_dev;    //定义device结构体//定义了open函数static int chat_drv_open (struct inode *inode, struct file *file){  printk("open\n");  return 0;}static char words[1000];  //聊天内容缓存static int words_cnt = 0;  //聊天内容缓存字节数static struct mutex words_mutex; //互斥锁//read函数static ssize_t chat_drv_read(struct file *file, char __user *buf, size_t size, loff_t *fpos) {  printk("read begin\n");  ssize_t cnt = 0;  //返回值,表示读取字节长度  mutex_lock(&words_mutex);  //上锁    if(words_cnt > 0){    copy_to_user(buf, words, words_cnt); //数据拷贝到用户态    printk("words_cnt=%d\n", words_cnt);    cnt = words_cnt;    words_cnt = 0;  //读取后缓存清空  }  mutex_unlock(&words_mutex); //解锁  printk("read finish\n");  return cnt;}//定义了write函数static ssize_t chat_drv_write (struct file *file, const char __user *buf, size_t size, loff_t * ppos){  printk("write begin\n");  mutex_lock(&words_mutex); //上锁  copy_from_user(words, buf, size);  //从用户态拷贝数据  words_cnt = size;       //更新缓存字节长度    printk("words_cnt=%d\n", words_cnt);  printk("words=%s\n", words);  mutex_unlock(&words_mutex); //解锁  printk("write finish\n");  return 0;}//在file_operations中注册open和write函数static struct file_operations chat_drv_fo ={  .owner  =  THIS_MODULE,  //将对应的函数关联在file_operations的结构体中  .open   =  chat_drv_open,      .read    = chat_drv_read,    .write  =  chat_drv_write,};static int dev_id = 0;     //初始化的设备号0//init驱动的入口函数static int __init chat_drv_init(void){        //注册设备,实际是将file_operations结构体放到内核的制定数组中,以便管理  //在register_chrdev中制定dev_id作为主设备号,若dev_id为0则自动分配一个主设备号  dev_id = register_chrdev(dev_id, device_name , &chat_drv_fo);  chat_class = class_create(THIS_MODULE, device_path);    //初始化class结构体,指定设备文件名  chat_dev = device_create(chat_class, NULL, MKDEV(dev_id, 0), NULL, device_path);// 根据class来初始化device,会创建出对应的设备文件    mutex_init(&words_mutex);    printk("init\n");  return 0;}//驱动的出口函数static void __exit chat_drv_exit(void){  printk("exit\n");  unregister_chrdev(dev_id, device_name);  //卸载设备,实际是将file_operations结构体从内核维护的相关数组中以主设备号作为索引删除  device_unregister(chat_dev); // 后创建的先卸载  class_destroy(chat_class);}//内核将通过这个宏,来直到这个驱动的入口和出口函数module_init(chat_drv_init);  module_exit(chat_drv_exit);MODULE_LICENSE("GPL");  //指定协议
          • 编译并加载内核驱动,详细可以看参考

            ()
          • 创建发送端测试程序write.c:

            #include #include #include #include #include int main(int argc, char **argv){    int fd;    char words[1000];    fd = open("/dev/chat",  O_RDWR);  //根据设备描述符打开设备    if(fd  
            • 创建接收端测试程序read.c:
              #include #include #include #include #include int main(int argc, char **argv){    int fd;    char words[1000];    fd = open("/dev/chat",  O_RDWR);  //根据设备描述符打开设备    if(fd  0)printf("%s\n", words);             sleep(1);     }    return 0;}
              • 编译两个测试程序,并分别在两个终端运行:
                gcc write.c -o writegcc read.c -o read
                • 结果如下:
                  $ sudo ./writenihaoqpwoeiruty12222222222222rrrrrrrrrrrrrrrrrrrrrrrrrrrr
                  $ sudo ./readnihaoqpwoeiruty12222222222222rrrrrrrrrrrrrrrrrrrrrrrrrrrr

                  2 等待队列实现读取阻塞

                  改成read阻塞方式,可以及时收到消息,并且减少cpu占用。

                  等待队列参考:Linux驱动—等待队列

                  • 创建内核驱动文件chat_dev.c:
                    #include      //定义了module_init#include    //最基本的头文件,其中定义了MODULE_LICENSE这一类宏#include        // file_operations结构体定义在该头文件中#include     //class、device结构体的定义位置#include #include     // copy_to_user & copy_from_user #include #include static const char* device_name = "chat_driver";  //  定义设备名static const char* device_path = "chat";  //  定义设备文件名  /dev/chatstatic struct class *chat_class;    //定义class结构体static struct device *chat_dev;    //定义device结构体//定义了open函数static int chat_drv_open (struct inode *inode, struct file *file){  printk("open\n");  return 0;}static char words[1000];  //聊天内容缓存static int words_cnt = 0;  //聊天内容缓存字节数static struct mutex words_mutex; //互斥锁static wait_queue_head_t wq; //等待队列//read函数static ssize_t chat_drv_read(struct file *file, char __user *buf, size_t size, loff_t *fpos) {  printk("read begin\n");  ssize_t cnt = 0;  //返回值,表示读取字节长度  wait_event(wq, words_cnt > 0);  //阻塞直至唤醒,且满足后面条件  mutex_lock(&words_mutex);  //上锁      copy_to_user(buf, words, words_cnt); //数据拷贝到用户态  printk("words_cnt=%d\n", words_cnt);  cnt = words_cnt;  words_cnt = 0;  //读取后缓存清空  mutex_unlock(&words_mutex); //解锁  printk("read finish\n");  return cnt;}//定义了write函数static ssize_t chat_drv_write (struct file *file, const char __user *buf, size_t size, loff_t * ppos){  printk("write begin\n");  mutex_lock(&words_mutex); //上锁  copy_from_user(words, buf, size);  //从用户态拷贝数据  words_cnt = size;       //更新缓存字节长度    printk("words_cnt=%d\n", words_cnt);  printk("words=%s\n", words);  mutex_unlock(&words_mutex); //解锁  wake_up(&wq); //唤醒  printk("write finish\n");  return 0;}//在file_operations中注册open和write函数static struct file_operations chat_drv_fo ={  .owner  =  THIS_MODULE,  //将对应的函数关联在file_operations的结构体中  .open   =  chat_drv_open,      .read    = chat_drv_read,    .write  =  chat_drv_write,};static int dev_id = 0;     //初始化的设备号0//init驱动的入口函数static int __init chat_drv_init(void){        //注册设备,实际是将file_operations结构体放到内核的制定数组中,以便管理  //在register_chrdev中制定dev_id作为主设备号,若dev_id为0则自动分配一个主设备号  dev_id = register_chrdev(dev_id, device_name , &chat_drv_fo);  chat_class = class_create(THIS_MODULE, device_path);    //初始化class结构体,指定设备文件名  chat_dev = device_create(chat_class, NULL, MKDEV(dev_id, 0), NULL, device_path);// 根据class来初始化device,会创建出对应的设备文件  mutex_init(&words_mutex);  init_waitqueue_head(&wq);    printk("init\n");  return 0;}//驱动的出口函数static void __exit chat_drv_exit(void){  printk("exit\n");  unregister_chrdev(dev_id, device_name);  //卸载设备,实际是将file_operations结构体从内核维护的相关数组中以主设备号作为索引删除  device_unregister(chat_dev); // 后创建的先卸载  class_destroy(chat_class);}//内核将通过这个宏,来直到这个驱动的入口和出口函数module_init(chat_drv_init);  module_exit(chat_drv_exit);MODULE_LICENSE("GPL");  //指定协议
                    • 编译并加载内核驱动,详细可以看参考

                    • 创建发送端测试程序write.c:

                      #include #include #include #include #include int main(int argc, char **argv){    int fd;    char words[1000];    fd = open("/dev/chat",  O_RDWR);  //根据设备描述符打开设备    if(fd  
                      • 创建接收端测试程序read.c:
                        #include #include #include #include #include int main(int argc, char **argv){    int fd;    char words[1000];    fd = open("/dev/chat",  O_RDWR);  //根据设备描述符打开设备    if(fd  0){            printf("B reads from A:\n");            printf("%s\n\n", words);         }    }    return 0;}
                        • 编译两个测试程序,并分别在两个终端运行:
                          gcc write.c -o writegcc read.c -o read
                          • 结果如下:
                            $ sudo ./writeA writes to B:ni haoA writes to B:helloA writes to B:Oh my God, I'm so handsome!A writes to B:
                            $ sudo ./readB reads from A:ni haoB reads from A:helloB reads from A:Oh my God, I'm so handsome!

上一篇:服务器iis日志怎么看?

下一篇:国外虚拟主机详解,优势、选择与使用全攻略