0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

如何实现一套linux进程间通信的机制

科技绿洲 来源:Linux开发架构之路 作者:Linux开发架构之路 2023-11-10 14:56 次阅读

我们知道linux的进程的间通信的组件有管道,消息队列,socket, 信号量,共享内存等。但是我们如果自己实现一套进程间通信的机制的话,要怎么做?了解android 开发的可能会知道,android里面有个binder机制,简单来说,就是一个进程往binder里面写数据,另一个进程从binder里面读出数据。

图片

所以我们也可以按照binder的思路来设计一个自己的进程间通信组件。

原理

图片

我们的设计思路很简单,我们首先需要注册一个字符设备文件叫**/dev/channel**, 同时需要为这个设备编写驱动,此时某个进程A向设备文件写数据,同时如果该设备可读,我们就通知另一个进程B去读该进程。 我们怎么知道该设备是否可读可写呢?使用poll来管理,因为该设备驱动属于一个IO, 打开一个设备就有fd, 有了fd我们就可以使用poll来管理。

代码实现

首先驱动程序:

#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include


#ifndef CHANNEL_MAJOR
#define CHANNEL_MAJOR 96
#endif

#ifndef CHANNEL_NR_DEVS
#define CHANNEL_NR_DEVS 2
#endif

#ifndef CHANNEL_SIZE
#define CHANNEL_SIZE 4096
#endif

#define ENABLE_POLL 1

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

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

struct channel *channel_devp;
struct cdev cdev;

char have_data = 0;

int channel_open (struct inode *inode, struct file *filp) {
struct channel *channel;

int num = MINOR(inode->i_rdev); //设备读了多少次
if (num >= CHANNEL_NR_DEVS)
return -ENODEV;

channel = &channel_devp[num];
filp->private_data = channel;

return 0;
}

int channel_release (struct inode *inode, struct file *filp) {
return 0;
}

#if ENABLE_POLL
unsigned int channel_poll (struct file *filp, struct poll_table_struct *wait) {
struct channel *channel = filp->private_data;
unsigned int mask = 0;

poll_wait(filp, &channel->inq, wait); // poll 阻塞

if (have_data)
mask |= (POLLIN | POLLRDNORM);

return mask;
}
#endif


int channel_mmap (struct file *filp, struct vm_area_struct *vma) {
struct channel *channel = filp->private_data;

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;
}

ssize_t channel_read (struct file *filp, char __user * buffer, size_t size, loff_t *ppos) {
unsigned long p = *ppos;
unsigned int count = size;

int ret = 0;
struct channel *channel = filp->private_data; // 读私有空间

if (p >= CHANNEL_SIZE) return 0;
if (count > CHANNEL_SIZE- p)
count = CHANNEL_SIZE- p;

#if ENABLE_POLL
while (!have_data) {
if (filp->f_flags & O_NONBLOCK) return -EAGAIN;

wait_event_interruptible(channel->inq, have_data);
}
#endif
if (copy_to_user(buffer, (void*)(channel->data + p), count)) { //拷贝到用户空间
ret = -EFAULT;
} else {
ret = strlen(buffer);
channel->size -= ret;
printk(KERN_INFO "read %d byte(s) from %ldn", ret, p);
}

have_data = 0;
return ret;
}

ssize_t channel_write (struct file *filp , const char __user * buffer, size_t size, loff_t *ppos) {
int ret = 0;
unsigned long p = *ppos;
unsigned int count = size;

struct channel *channel = filp->private_data; // 写道文件的私有空间
if (p >= CHANNEL_SIZE) return 0;
if (count > CHANNEL_SIZE- p)
count = CHANNEL_SIZE- p;

if (copy_from_user(channel->data +p, buffer, count)) { // 从user -> kernel
return -EFAULT;
} else {
*ppos += count;
ret = count;
channel->size += count;
*(channel->data+p + count) = '�';

printk(KERN_INFO "written %d byte(s) from %ldn", count, p);
}

#if ENABLE_POLL
have_data = 1;
wake_up(&channel->inq);
#endif

return ret;
}

loff_t channel_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:
newpos = CHANNEL_SIZE - 1 + offset;
break;
default:
return -EINVAL;
}

if (newpos < 0 || newpos > CHANNEL_SIZE) return -EINVAL;

filp->f_pos = newpos;

return newpos;
}

static const struct file_operations channel_fops =
{
.owner = THIS_MODULE,
.llseek = channel_llseek,
.read = channel_read,
.write = channel_write,
.open = channel_open,
.release = channel_release,
.poll = channel_poll,
.mmap = channel_mmap,
};


static int channel_init(void) {
int reslut;
int i;

dev_t devno = MKDEV(channel_major, 0); // 创建一个主设备号为96,次设备号为0的设备
if (channel_major) {
reslut = register_chrdev_region(devno, CHANNEL_NR_DEVS, "channel"); // 注册设备
} else {
reslut = alloc_chrdev_region(&devno, 0, CHANNEL_NR_DEVS, "channel");
}

if (reslut < 0) return reslut;

cdev_init(&cdev, &channel_fops); //初始化字符设备
cdev.owner = THIS_MODULE;

cdev_add(&cdev, MKDEV(channel_major, 0), CHANNEL_NR_DEVS); //添加到字符设备中

channel_devp = kmalloc(CHANNEL_NR_DEVS *sizeof(struct channel), GFP_KERNEL); //为 我们的buffer 分配一块空间
if (!channel_devp) {
reslut = -ENOMEM;
goto fail_malloc;
}
memset(channel_devp, 0, sizeof(struct channel));

for (i = 0; i < CHANNEL_NR_DEVS; i++) {
channel_devp[i].size = CHANNEL_SIZE;
channel_devp[i].data = kmalloc(CHANNEL_SIZE, GFP_KERNEL);
memset(channel_devp[i].data, 0, CHANNEL_SIZE);
#if ENABLE_POLL
init_waitqueue_head(&(channel_devp[i].inq));
#endif
}
printk(KERN_INFO "ntychannel_init");

return 0;
fail_malloc:
unregister_chrdev_region(devno, 1);
return reslut;
}

static void channel_exit(void) {
printk(KERN_INFO "channel_exit");
cdev_del(&cdev);
kfree(channel_devp);

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


MODULE_AUTHOR("birate");
MODULE_LICENSE("GPL");

module_init(channel_init); // 设备初始化
module_exit(channel_exit); //设备退出

编写Makefile文件:

obj-m += channel.o
KERNELDIR ?= /lib/modules/$(shell uname -r)/build
all:
make -C $(KERNELDIR) M=$(PWD) modules
clean:
make -C $(KERNELDIR) M=$(PWD) clean
  1. 使用 make 命令。编译出我们需要的channel.ko文件。
  2. 使用 insmod channel.ko, 向kernel中插入 我们的module
  3. 使用mknod /dev/channel c 96 0, 创建一个/dev/channel 的字符设备,主设备号为96,次设备号为0;

编写我们的应用程序:

channel_app.c

#include
#include
#include

#include
#include
#include
#include
#include

#include

#define BUFFER_LENGTH 128

int main () {
int fd = open("/dev/channel", O_RDWR);
if (fd < 0) {
printf("open failed: errno : %sn", strerror(errno));
return -1;
}

char *buffer = (char *)malloc(BUFFER_LENGTH);
memset(buffer, 0, BUFFER_LENGTH);

char *start = mmap(NULL, BUFFER_LENGTH, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);

fd_set rds;
FD_ZERO(&rds);
FD_SET(fd, &rds);

while(1) {
int ret = select(fd+1, &rds, NULL, NULL, NULL);
if (ret < 0) {
printf("select errorn");
exit(1);
}
if (FD_ISSET(fd, &rds)) {
#if 0
strcpy(buffer, start);
printf("channel: %sn", buffer);
#else
read(fd, buffer, BUFFER_LENGTH);
printf("channel: %sn", buffer);
#endif
}
}

munmap(start, BUFFER_LENGTH);
free(buffer);
close(fd);

return 0;
}

应用程序很简单,我们使用 gcc -o channel_app channel_app.c , 编译出可执行文件,在一个进程中执行channel_app, 另一个进程使用echo " " > /dev/channel 去向设备文件中写就可以了。

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • 数据
    +关注

    关注

    8

    文章

    6511

    浏览量

    87600
  • 通信
    +关注

    关注

    18

    文章

    5706

    浏览量

    134396
  • Linux
    +关注

    关注

    87

    文章

    10991

    浏览量

    206736
  • 字符
    +关注

    关注

    0

    文章

    229

    浏览量

    24888
  • 组件
    +关注

    关注

    1

    文章

    336

    浏览量

    17584
收藏 人收藏

    评论

    相关推荐

    Linux进程间如何实现共享内存通信

    这次我们来讲一下Linux进程通信中重要的通信方式:共享内存作为Linux软件开发攻城狮,进程
    发表于 04-26 17:14 573次阅读

    linux操作系统下的进程通信设计

    )的进程通信机制Linux则把两者继承了下来,如图示:其中,最初Unix IPC包括:管道、FIFO、信号;System V IPC包括
    发表于 04-16 09:17

    Linux进程通信方式-管道

    Linux进程通信方式-管道分享到: 本文关键字: linux 管道通信
    发表于 08-29 15:29

    Linux进程通信

    华清远见嵌入式linux学习资料《Linux进程通信》,通过前面的学习,读者已经知道了进程
    发表于 09-04 10:07

    Linux学习杂谈】之进程通信

    本帖最后由 michael_llh 于 2016-10-17 13:14 编辑 我们在Linux应用编程当中如果需要用到多个进程来完成个任务的话那么我们就没有办法避开进程
    发表于 10-15 14:45

    管道文件如何实现两个进程通信

    管道文件如何实现两个进程通信
    发表于 01-11 16:54

    Linux进程通信——使用共享内存

    Linux进程通信——使用共享内存 图文详情见附件
    发表于 11-21 10:53

    进程通信的分类及机制中的数据结构

      进程通信就是在不同进程之间传播或交换信息,进程控制信息的交换称为低级
    发表于 08-05 08:09

    Linux现有的所有进程IPC方式

    在开始回答前,先简单概括性地说说Linux现有的所有进程IPC方式:1. **管道:**在创建时分配个page大小的内存,缓存区大小比较有限;2. 消息队列:信息复制两次,额外的C
    发表于 08-20 06:17

    怎样通过匿名管道去实现进程通信

    进程通信是指什么?怎样通过匿名管道去实现进程通信呢?有哪些步骤?
    发表于 12-24 06:45

    哪些方式可以实现Linux系统下的进程通信

    哪些方式可以实现Linux系统下的进程通信进程与线程有哪些不同之处呢?
    发表于 12-24 06:38

    任务通信的目的是什么

    嵌入式操作系统中任务之间的交互与Linux一进程的不同线程之间的交互完全类似,可以通过全局变量和任务通信机制两种方法来
    发表于 12-24 08:16

    RT-thread内核之进程通信设计实现

    1、RT-thread内核之进程通信特性及使用场合介绍  rt-thread操作系统的IPC(Inter-ProcessCommunication,进程
    发表于 09-01 15:13

    基于Linux内核2_6的进程拦截机制的研究和实现_王全民

    基于Linux内核2_6的进程拦截机制的研究和实现_王全民
    发表于 03-18 09:15 3次下载

    进程通信机制有哪些

    比较难,Linux内核提供了多种进程通信机制。 同一个进程的不同模块(譬如不同的函数)之间进行通信
    的头像 发表于 07-21 11:23 652次阅读
    <b class='flag-5'>进程</b>间<b class='flag-5'>通信</b>的<b class='flag-5'>机制</b>有哪些