我們知道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
#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文件:
KERNELDIR ?= /lib/modules/$(shell uname -r)/build
all:
make -C $(KERNELDIR) M=$(PWD) modules
clean:
make -C $(KERNELDIR) M=$(PWD) clean
- 使用 make 命令。編譯出我們需要的channel.ko文件。
- 使用 insmod channel.ko, 向kernel中插入 我們的module
- 使用mknod /dev/channel c 96 0, 創(chuàng)建一個/dev/channel 的字符設備,主設備號為96,次設備號為0;
編寫我們的應用程序:
channel_app.c
#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 去向設備文件中寫就可以了。
-
數(shù)據(jù)
+關注
關注
8文章
7035瀏覽量
89045 -
通信
+關注
關注
18文章
6032瀏覽量
135999 -
Linux
+關注
關注
87文章
11304瀏覽量
209537 -
字符
+關注
關注
0文章
233瀏覽量
25210 -
組件
+關注
關注
1文章
512瀏覽量
17829
發(fā)布評論請先 登錄
相關推薦
評論