0
  • 聊天消息
  • 系統(tǒng)消息
  • 評論與回復
登錄后你可以
  • 下載海量資料
  • 學習在線課程
  • 觀看技術視頻
  • 寫文章/發(fā)帖/加入社區(qū)
會員中心
創(chuàng)作中心

完善資料讓更多小伙伴認識你,還能領取20積分哦,立即完善>

3天內(nèi)不再提示

如何實現(xiàn)一套linux進程間通信的機制

科技綠洲 ? 來源:Linux開發(fā)架構之路 ? 作者:Linux開發(fā)架構之路 ? 2023-11-10 14:56 ? 次閱讀

我們知道linux的進程的間通信的組件有管道,消息隊列,socket, 信號量,共享內(nèi)存等。但是我們?nèi)绻约簩崿F(xiàn)一套進程間通信的機制的話,要怎么做?了解android 開發(fā)的可能會知道,android里面有個binder機制,簡單來說,就是一個進程往binder里面寫數(shù)據(jù),另一個進程從binder里面讀出數(shù)據(jù)。

圖片

所以我們也可以按照binder的思路來設計一個自己的進程間通信組件。

原理

圖片

我們的設計思路很簡單,我們首先需要注冊一個字符設備文件叫**/dev/channel**, 同時需要為這個設備編寫驅動,此時某個進程A向設備文件寫數(shù)據(jù),同時如果該設備可讀,我們就通知另一個進程B去讀該進程。 我們怎么知道該設備是否可讀可寫呢?使用poll來管理,因為該設備驅動屬于一個IO, 打開一個設備就有fd, 有了fd我們就可以使用poll來管理。

代碼實現(xiàn)

首先驅動程序:

#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); // 創(chuàng)建一個主設備號為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, 創(chuàng)建一個/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 , 編譯出可執(zhí)行文件,在一個進程中執(zhí)行channel_app, 另一個進程使用echo " " > /dev/channel 去向設備文件中寫就可以了。

聲明:本文內(nèi)容及配圖由入駐作者撰寫或者入駐合作網(wǎng)站授權轉載。文章觀點僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場。文章及其配圖僅供工程師學習之用,如有內(nèi)容侵權或者其他違規(guī)問題,請聯(lián)系本站處理。 舉報投訴
  • 數(shù)據(jù)

    關注

    8

    文章

    7035

    瀏覽量

    89045
  • 通信
    +關注

    關注

    18

    文章

    6032

    瀏覽量

    135999
  • Linux
    +關注

    關注

    87

    文章

    11304

    瀏覽量

    209537
  • 字符
    +關注

    關注

    0

    文章

    233

    瀏覽量

    25210
  • 組件
    +關注

    關注

    1

    文章

    512

    瀏覽量

    17829
收藏 人收藏

    評論

    相關推薦

    Linux進程如何實現(xiàn)共享內(nèi)存通信

    這次我們來講Linux進程通信中重要的通信方式:共享內(nèi)存作為Linux軟件開發(fā)攻城獅,
    發(fā)表于 04-26 17:14 ?697次閱讀

    linux操作系統(tǒng)下的進程通信設計

    )的進程通信機制。Linux則把兩者繼承了下來,如圖示:其中,最初Unix IPC包括:管道、FIFO、信號;System V IPC包括
    發(fā)表于 04-16 09:17

    Linux進程通信方式-管道

    Linux進程通信方式-管道分享到: 本文關鍵字: linux 管道通信,
    發(fā)表于 08-29 15:29

    Linux進程通信

    華清遠見嵌入式linux學習資料《Linux進程通信》,通過前面的學習,讀者已經(jīng)知道了進程
    發(fā)表于 09-04 10:07

    Linux學習雜談】之進程通信

    本帖最后由 michael_llh 于 2016-10-17 13:14 編輯 我們在Linux應用編程當中如果需要用到多個進程來完成個任務的話那么我們就沒有辦法避開進程
    發(fā)表于 10-15 14:45

    Linux進程通信——使用共享內(nèi)存

    Linux進程通信——使用共享內(nèi)存 圖文詳情見附件
    發(fā)表于 11-21 10:53

    Linux現(xiàn)有的所有進程IPC方式

    在開始回答前,先簡單概括性地說說Linux現(xiàn)有的所有進程IPC方式:1. **管道:**在創(chuàng)建時分配個page大小的內(nèi)存,緩存區(qū)大小比較有限;2. 消息隊列:信息復制兩次,額外的C
    發(fā)表于 08-20 06:17

    哪些方式可以實現(xiàn)Linux系統(tǒng)下的進程通信

    哪些方式可以實現(xiàn)Linux系統(tǒng)下的進程通信進程與線程有哪些不同之處呢?
    發(fā)表于 12-24 06:38

    進程通信Linux進程通信概述

    人們現(xiàn)在廣泛使用的手機等方式。本章就是講述如何建立這些不同的通話方式,就像人們有多種通信方式樣。 Linux下的進程通信手段基本上是從UN
    發(fā)表于 10-18 16:21 ?0次下載

    進程與線程通信方式

    進程通信則不同,它的數(shù)據(jù)空間的獨立性決定了它的通信相對比較復雜,需要通過操作系統(tǒng)。以前進程
    的頭像 發(fā)表于 04-09 15:58 ?8927次閱讀
    <b class='flag-5'>進程</b><b class='flag-5'>間</b>與線程<b class='flag-5'>間</b>的<b class='flag-5'>通信</b>方式

    你知道linux socket進程通信是怎樣實現(xiàn)的?

    socket進程通信與網(wǎng)絡通信使用的是統(tǒng)一套接口,只是地址結構與某些參數(shù)不同
    發(fā)表于 04-23 14:49 ?2623次閱讀
    你知道<b class='flag-5'>linux</b> socket<b class='flag-5'>進程</b><b class='flag-5'>通信</b>是怎樣<b class='flag-5'>實現(xiàn)</b>的?

    Linux進程通信

    、進程通信概述進程通信有如下
    發(fā)表于 04-02 14:46 ?513次閱讀

    Linux進程通信方式——管道

    管道是Linux進程通信種方式,它把個程序的輸出直接連接到另
    發(fā)表于 06-01 09:13 ?1438次閱讀
    <b class='flag-5'>Linux</b><b class='flag-5'>進程</b><b class='flag-5'>間</b><b class='flag-5'>通信</b>方式——管道

    Linux進程通信方法之管道

    上文中我們介紹了進程通信的方法之:信號,本文將繼續(xù)介紹另進程
    的頭像 發(fā)表于 05-14 15:47 ?1968次閱讀
    <b class='flag-5'>Linux</b><b class='flag-5'>進程</b><b class='flag-5'>間</b><b class='flag-5'>通信</b>方法之管道

    進程通信機制有哪些

    比較難,Linux內(nèi)核提供了多種進程通信機制。 同
    的頭像 發(fā)表于 07-21 11:23 ?934次閱讀
    <b class='flag-5'>進程</b><b class='flag-5'>間</b><b class='flag-5'>通信</b>的<b class='flag-5'>機制</b>有哪些