Linux源码阅读笔记13-进程通信组件中

架构图

代码分析

c 复制代码
loff_t lnchannel_llseek(struct file *filp, loff_t offset, int whence) {

    loff_t newpos;

    switch(whence) {

        case 0:
            newpos = offset;
            break;
        case 1:
            newpos = filp->f_pos + offset;
            break;
        case 2:
            return -EINVAL;
    }

    if (newpos < 0 || newpos > LNCHANNEL_SIZE) {

        return -EINVAL;
    }

    filp->f_pos = newpos;

    return newpos;
}

这段代码是一个文件操作函数lnchannel_llseek(),它用于在文件中进行定位。具体来说,根据参数whenc e的值不同,函数将会根据指定的偏移量进行定位,并返回新的位置。

  • 如果whence为0,则将偏移量直接作为新位置。
  • 如果whence为1,则将当前位置(filp->f_pos)与偏移量相加得到新位置。
  • 如果whence为2,则返回无效参数错误(EINVAL)。

在完成计算新位置之后,函数会对新位置进行合法性检查。如果新位置小于0或大于预定义的文件大小(LNCHANNEL_SIZE),则会返回无效参数错误。

最后,函数更新文件结构体中的当前位置(filp->f_pos)为新位置,并返回新位置值。

c 复制代码
unsigned int lnchannel_poll(struct file *filp, struct poll_table_struct *wait) {

    struct lnchannel* channel = filp->private_data;
    unsigned int mask = 0;

    poll_wait(filp, &channel->inq, wait);
    if (have_data) {

        mask |= (POLLIN | POLLRDNORM);
    }

    return mask;
}

这段代码是一个文件操作函数lnchannel_poll(),用于进行文件的轮询操作。它主要用于检查是否有数据可读,并返回相应的事件掩码。

首先,函数从文件结构体中获取通道指针channel

接下来,使用poll_wait()函数将当前进程加入到等待队列中,以便在有新数据到达时唤醒进程。

然后,在判断是否有数据可读之前,代码中缺少了一个变量have_data的定义和赋值过程。在此处假设该变量为布尔类型,并且已经被正确初始化。

如果存在可读数据,则将事件掩码的对应位(POLLIN | POLLRDNORM)设置为1,表示可读事件发生。

最后,函数返回事件掩码mask给调用者,表示当前的文件状态。

需要注意的是,这只是代码片段,并没有提供完整的上下文环境。如果需要更详细的解释或有其他问题,请随时提问。

完整代码实现

c 复制代码
#include <linux/module.h>
#include <linux/types.h>
#include <linux/fs.h>
#include <linux/errno.h>
#include <linux/mm.h>
#include <linux/sched.h>
#include <linux/init.h>
#include <linux/cdev.h>
#include <linux/io.h>
#include <linux/uaccess.h>
#include <linux/slab.h>
#include <linux/poll.h>

#ifndef LNCHANNEL_MAJOR
#define LNCHANNEL_MAJOR     96
#endif

#ifndef LNCHANNEL_NR_DEVS
#define LNCHANNEL_NR_DEVS   2
#endif

#ifndef LNCHANNEL_SIZE
#define LNCHANNEL_SIZE      4096
#endif

#define ENABLE_POLL     1

struct lnchannel {
    char* data;
    unsigned long size;
#if ENABLE_POLL
    wait_queue_head_t inq;
#endif
};

static int channel_major = LNCHANNEL_MAJOR;
module_param(channel_major, int, S_IRUGO);

struct lnchannel* channel_devp;
struct cdev cdev;

char have_data = 0;

loff_t lnchannel_llseek(struct file *filp, loff_t offset, int whence) {

    loff_t newpos;

    switch(whence) {

        case 0:
            newpos = offset;
            break;
        case 1:
            newpos = filp->f_pos + offset;
            break;
        case 2:
            return -EINVAL;
    }

    if (newpos < 0 || newpos > LNCHANNEL_SIZE) {

        return -EINVAL;
    }

    filp->f_pos = newpos;

    return newpos;
}

ssize_t lnchannel_read(struct file *filp, char __user *buffer, size_t size, loff_t *ppos) {

    int ret = 0;
    unsigned long count = size;
    unsigned long long p = *ppos;

    struct lnchannel* channel = filp->private_data;

    if (p >= LNCHANNEL_SIZE) {

        return 0;
    }

    if (count > LNCHANNEL_SIZE - p) {

        count = LNCHANNEL_SIZE - p;
    }

    while(!have_data) {

        if (filp->f_flags & O_NONBLOCK) {

            return -EAGAIN;
        }
            
        wait_event_interruptible(channel->inq, have_data);
    }

    if (copy_to_user(buffer, (void*)(channel->data + p), count)) {

        ret = -EFAULT;
    }
    else {

        ret = strlen(buffer);
        channel->size -= ret;
        printk(KERN_INFO "read %d btye(s) from %ld\n", ret, p);
    }

    have_data = 0;

    return ret;
}

ssize_t lnchannel_write(struct file *filp, const char __user *buffer, size_t size, loff_t * ppos) {

    int ret = 0;
    unsigned long count = size;
    unsigned long long p = *ppos;
    struct lnchannel* channel = filp->private_data;

    if (p >= LNCHANNEL_SIZE) {

        return ret;
    }

    if (count >= LNCHANNEL_SIZE - p) {

        count = LNCHANNEL_SIZE - p;
    }

    if (copy_from_user(channel->data + p, buffer, count)) {

        return -EFAULT;
    }
    else {

        *(channel->data + p + count) = '\0';
        channel->size += count;
        ret = count;
        *ppos += count;

        printk(KERN_INFO, "written %d btyes from %ld\n", count, p);
    }

    have_data = 1;
    wake_up(channel->inq);

    return ret;
}

int lnchannel_mmap(struct file *filp, struct vm_area_struct *vma) {

    vma->vm_flags |= VM_IO;
    vma->vm_flags |= (VM_DONTEXPAND | VM_DONTDUMP);

    if (remap_pfn_range(vma, vma->vm_start, virt_to_phys(channel->data) >> PAGE_SHIFT, 
    vma->vm_end-vma->vm_start, vma->vm_page_prot)) {
    return -EAGAIN;
	}

    return 0;
}

int lnchannel_open(struct inode *inode, struct file *filp) {

    struct lnchannel* channel;

    int num = MINOR(inode->i_cdev);
    if (num >= LNCHANNEL_NR_DEVS) {

        return -ENODEV;
    }

    channel = &channel_devp[num];

    filp->private_data = channel;

    return 0;

}

int lnchannel_release(struct inode *, struct file *) {

    return 0;
}

unsigned int lnchannel_poll(struct file *filp, struct poll_table_struct *wait) {

    struct lnchannel* channel = filp->private_data;
    unsigned int mask = 0;

    poll_wait(filp, &channel->inq, wait);
    if (have_data) {

        mask |= (POLLIN | POLLRDNORM);
    }

    return mask;
}

static const struct file_operations lnchannel_fops = {
    .owner = THIS_MODULE,
    .llseek = lnchannel_llseek,
    .open = lnchannel_open,
    .write = lnchannel_write,
    .open = lnchannel_open,
    .release = lnchannel_release,
    .mmap = lnchannel_mmap,
    .poll = lnchannel_poll,
};

static int lnchannel_init(void) {

    int result;
    int i;

    dev_t devno = MKDEV(channel_major, 0);
    if (channel_major) {

        result = register_chrdev_region(devno, LNCHANNEL_NR_DEVS, "lnchannel");
    }
    else {

        result = alloc_chrdev_region(&devno, 0, LNCHANNEL_NR_DEVS, "lnchannel");
        channel_major = MAJOR(devno);
    }

    if (result < 0) return result;

    cdev_init(&cdev, &lnchannel_fops);
    cdev.owner = THIS_MODULE;

    cdev_add(&cdev, MKDEV(channel_major, 0), LNCHANNEL_NR_DEVS);

    channel_devp = kmalloc(LNCHANNEL_NR_DEVS * (struct lnchannel), GFP_KERNEL);
    if (!channel_devp) {

        result = -ENOMEM;
        goto fail_malloc;
    }
    memset(channel_devp, 0, sizeof(struct lnchannel));

    for(i = 0; i < LNCHANNEL_NR_DEVS; i++) {

        channel_devp[i].size = LNCHANNEL_SIZE;
        channel_devp[i].data = kmalloc(LNCHANNEL_SIZE, GFP_KERNEL);
        memset(channel_devp[i].data, 0, LNCHANNEL_SIZE);
        init_waitqueue_head(&(channel_devp[i].inq));
    }

    printk(KERN_INFO "linchannel_init");

    return 0;

fail_malloc:
    unregister_chrdev_region(devno, 1);

    return result;
}

static void lnchannel_exit(void) {

    printk(KERN_INFO "lnchannel_exit");
    cdev_del(&cdev);
    kfree(channel_devp);

    unregister_chrdev_region(MKDEV(channel_major, 0), 2);
}

MODULE_AUTHOR("Lenn Louis");
MODULE_LICENSE("GPL");

module_init(lnchannel_init);
module_init(lnchannel_exit);
Makefile 复制代码
#!/bin/bash

ccflags_y += -O2

ifneq ($(KERNELRELEASE),)
obj-m := lnchannel.o
else
KERNELDIR ?= /lib/modules/$(shell uname -r)/build
PWD := $(shell pwd)

default:
	$(MAKE) -C $(KERNELDIR) M=$(PWD) modules 
endif

clean:
	rm -rf *.o *.ko *.mod.c

depend .depend dep:
	$(CC)  -M *.c > .depend 
相关推荐
cominglately40 分钟前
centos单机部署seata
linux·运维·centos
魏 无羡42 分钟前
linux CentOS系统上卸载docker
linux·kubernetes·centos
CircleMouse1 小时前
Centos7, 使用yum工具,出现 Could not resolve host: mirrorlist.centos.org
linux·运维·服务器·centos
木子Linux2 小时前
【Linux打怪升级记 | 问题01】安装Linux系统忘记设置时区怎么办?3个方法教你回到东八区
linux·运维·服务器·centos·云计算
mit6.8242 小时前
Ubuntu 系统下性能剖析工具: perf
linux·运维·ubuntu
鹏大师运维2 小时前
聊聊开源的虚拟化平台--PVE
linux·开源·虚拟化·虚拟机·pve·存储·nfs
watermelonoops2 小时前
Windows安装Ubuntu,Deepin三系统启动问题(XXX has invalid signature 您需要先加载内核)
linux·运维·ubuntu·deepin
滴水之功3 小时前
VMware OpenWrt怎么桥接模式联网
linux·openwrt
ldinvicible3 小时前
How to run Flutter on an Embedded Device
linux
YRr YRr4 小时前
解决Ubuntu 20.04上编译OpenCV 3.2时遇到的stdlib.h缺失错误
linux·opencv·ubuntu