写个软驱动作进程/线程通信
1.Linux 进程通信机制
首先来回顾下Linux下进程通信(IPC)机制方式。
【1】信号(signal)
【2】信号量(semophore)
【3】消息队列(message queue)
【4】管道(piple),包括匿名管道(s_pipe)和有名管道(FIFO)
【5】共享内存(shared memory)
【6】套接字(socket)
如果不使用以上进程通信机制,如何实现两个进程通信?可能想到其中一个手段就是“文件”,通过文件实现两个进程通信,但访问磁盘文件速率远不如内存,加之文件寻址、文件系统映射等,更是耗时操作。
2.软驱动
从“共享文件”思路出发,因为Linux思想是“一切皆文件”,外设驱动、管道等都是文件,因此我们可以实现一个“软驱动”作进程通信。整体思路是,申请内核一段内存,实现标准驱动接口,进程通过文件描述符(fd)访问该段内存,从而实现两个进程通信。
从实现思路知道,该方式表象似“共享内存”机制,实质更与“管道”相像。“共享内存”是最快的进程通信机制,两个进程数据交换时只有2次的内存拷贝,进程A内存——>物理内存——进程B内存。而“管道”则存在4次内存拷贝,进程A内存——>内核空间——>物理内存——>内核空间——>进程B内存。
3.实现
3.1实现接口
实现常用的接口:
【1】open,打开一个软驱动
【2】close,关闭一个软驱动
【3】read,读数据
【4】write,写数据
【5】ioctl,控制类,如改变共享内存大小
【6】lseek,地址偏移操作
3.2 Code
【driver】
#include <linux/kernel.h>
#include <linux/device.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/types.h>
#include <linux/errno.h>
#include <linux/fs.h>
#include <linux/mm.h>
#include <linux/sched.h>
#include <linux/cdev.h>
#include <linux/slab.h>
#include <linux/ioctl.h>
#include <asm/system.h>
#include <asm/uaccess.h>
#include <asm/io.h>
#define DEV_MEM_MEMSET _IO('M', 0)
#define DEV_MEM_GET_SIZE _IOR('M', 1, int)
#define DEV_MEM_SET_SIZE _IOW('M', 2, int)
#define MEMORY_DEFULT_SIZE 128
#define DEV_NAME "dev_mem"
struct memory_device
{
struct cdev dev;
dev_t devno;
struct class *devclass;
char *mem_buf;
uint32_t mem_size;
};
static struct memory_device *pmemory_dev = NULL;
static int memory_open(struct inode * inode , struct file * pfile)
{
int state = 0;
if(pmemory_dev == NULL)
{
printk("empty memory.\n");
return -EFAULT;
}
pmemory_dev->mem_buf = kmalloc(MEMORY_DEFULT_SIZE, GFP_KERNEL);
if(pmemory_dev->mem_buf)
{
pmemory_dev->mem_size = MEMORY_DEFULT_SIZE;
state = 0;
}
else
{
state = -1;
printk("kmalloc request memory falied.\n");
}
pfile->private_data = pmemory_dev;
return state;
}
static ssize_t memory_read(struct file * pfile, char __user *buffer, size_t size, loff_t *offset)
{
unsigned long of = 0;
struct memory_device *p;
p = pfile->private_data;
of = *offset;
if(of > p->mem_size)
{
return 0;
}
if (size > (p->mem_size - of))
{
size = p->mem_size - of;
}
if (copy_to_user(buffer, p->mem_buf+of, size))
{
printk("read memory falied.\n");
return -EFAULT;
}
else
{
*offset -= size;
}
return size;
}
static ssize_t memory_write(struct file * pfile, const char __user *buffer, size_t size, loff_t *offset)
{
unsigned long of = 0;
struct memory_device *p;
p = pfile->private_data;
of = *offset;
if(of > p->mem_size)
{
return 0;
}
if (size > (p->mem_size - of))
{
size = p->mem_size -of;
}
if (copy_from_user(p->mem_buf+of, buffer, size))
{
printk("write memory falied.\n");
return -EFAULT;
}
else
{
*offset += size;
}
return size;
}
static ssize_t memory_close(struct inode * inode , struct file * pfile)
{
int state = 0;
struct memory_device *p;
p = pfile->private_data;
if(p->mem_buf)
{
kfree(p->mem_buf);
p->mem_size = 0;
}
return state;
}
static int memory_ioctl(struct inode *inode, struct file *pfile, unsigned int cmd, unsigned long arg)
{
int state = 0;
int temp = 0;
char *pmem = NULL;
struct memory_device *p;
p = pfile->private_data;
switch(cmd)
{
case DEV_MEM_MEMSET:
memset(p->mem_buf, 0, p->mem_size);
break;
case DEV_MEM_GET_SIZE:
if(copy_to_user((int __user*)arg, &p->mem_size, 4))
{
return -ENOTTY;
}
break;
case DEV_MEM_SET_SIZE:
if(copy_from_user(&temp, (int __user*)arg, 4))
{
return -ENOTTY;
}
if(temp != p->mem_size)
{
pmem = kmalloc(temp, GFP_KERNEL);
if(pmem)
{
kfree(p->mem_buf);
p->mem_buf = NULL;
p->mem_size = temp;
p->mem_buf = pmem;
}
}
break;
default:
break;
}
return state;
}
static loff_t memory_llseek(struct file *pfile, loff_t offset, int whence)
{
loff_t of;
struct memory_device *p;
p = pfile->private_data;
switch(whence)
{
case SEEK_SET:
of = offset;
break;
case SEEK_CUR:
of = pfile->f_pos + offset;
break;
case SEEK_END:
of = p->mem_size -1 + offset;
break;
default:
return -EINVAL;
}
if ((of<0) || (of>p->mem_size))
return -EINVAL;
pfile->f_pos = of;
return of;
}
static const struct file_operations memory_fops =
{
.owner = THIS_MODULE,
.open = memory_open,
.read = memory_read,
.write = memory_write,
.release = memory_close,
.ioctl = memory_ioctl,
.llseek = memory_llseek,
};
static int __init memory_init(void)
{
int ret = -1;
dev_t devno = 0;
ret = alloc_chrdev_region(&devno, 0, 1, "dev_mem");
if (ret)
{
printk("alloc dev-no failed.\n");
return ret;
}
pmemory_dev = kmalloc(sizeof(struct memory_device), GFP_KERNEL);
if (NULL == pmemory_dev)
{
ret = -ENOMEM;
printk("kmalloc request memory falied.\n");
return ret;
}
memset(pmemory_dev, 0, sizeof(struct memory_device));
pmemory_dev->devno = devno;
cdev_init(&pmemory_dev->dev, &memory_fops);
pmemory_dev->dev.owner = THIS_MODULE;
pmemory_dev->dev.ops = &memory_fops;
ret = cdev_add(&pmemory_dev->dev, pmemory_dev->devno, 1);
if (ret)
{
unregister_chrdev_region(pmemory_dev->devno, 1);
kfree(pmemory_dev);
return ret;
}
pmemory_dev->devclass = class_create(THIS_MODULE, "mem_class");
if (IS_ERR(pmemory_dev->devclass))
{
printk("class_create failed.\n");
cdev_del(&pmemory_dev->dev);
ret = -EIO;
return ret;
}
device_create(pmemory_dev->devclass, NULL, pmemory_dev->devno, NULL, "dev_mem");
return 0;
}
static void __exit memory_exit(void)
{
device_destroy(pmemory_dev->devclass, pmemory_dev->devno);
class_destroy(pmemory_dev->devclass);
cdev_del(&pmemory_dev->dev);
unregister_chrdev_region(pmemory_dev->devno, 1);
kfree(pmemory_dev->mem_buf);
pmemory_dev->devclass = NULL;
pmemory_dev->mem_buf = NULL;
pmemory_dev->mem_size = 0;
kfree(pmemory_dev);
pmemory_dev = NULL;
}
module_init(memory_init);
module_exit(memory_exit);
MODULE_LICENSE("GPL");
【Makefile】
ifeq ($(KERNELRELEASE),)
KERNELDIR = /usr/src/linux-headers-2.6.32-38-generic
PWD := $(shell pwd)
modules:
$(MAKE) -C $(KERNELDIR) M=$(PWD) modules
clean:
rm -rf *.o *.ko .mod.o *.mod.c *.symvers
else
obj-m := dev_mem.o
endif
【application】
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <signal.h>
#include <string.h>
#include <semaphore.h>
#include <linux/ioctl.h>
#define DEV_MEM_MEMSET _IO('M', 0)
#define DEV_MEM_GET_SIZE _IOR('M', 1, int)
#define DEV_MEM_SET_SIZE _IOW('M', 2, int)
sem_t *r_sem;
int main(int argc, char** argv)
{
int pid;
char buf[16] = {0};
int fd;
int mem_size = 0;
fd = open("/dev/dev_mem", O_RDWR);
if (-1 == fd)
{
perror("open error\n");
return -2;
}
printf("open \"dev_mem\" success\n");
r_sem = sem_open("rse", O_CREAT|O_RDWR, 0777, 0);
if(r_sem == SEM_FAILED)
{
perror("sem_open");
close(fd);
return -1;
}
pid = fork();
if(pid > 0)
{
ioctl(fd, DEV_MEM_GET_SIZE, &mem_size);
printf("Get dev memory size [%d]\n", mem_size);
mem_size = 256;
printf("Set dev memory size [%d]\n", mem_size);
ioctl(fd, DEV_MEM_SET_SIZE, &mem_size);
mem_size = 0;
ioctl(fd, DEV_MEM_GET_SIZE, &mem_size);
printf("Get dev memory size [%d]\n", mem_size);
ioctl(fd, DEV_MEM_MEMSET);
write(fd, "Hello word", 10);
//lseek(fd, 0, SEEK_CUR);
//lseek(fd, 10, SEEK_SET);
write(fd, " ABCD", 5);
sem_post(r_sem);
wait();
}
else if(pid == 0)
{
sem_wait(r_sem);
lseek(fd, 0, SEEK_SET);
read(fd, buf, 15);
printf("read mem:%s\n", buf);
close(fd);
}
else
{
perror("fork error");
close(fd);
}
return 0;
}
3.3 运行
可以直接在桌面Linux系统执行,非必须在嵌入式环境。编译完,加载驱动(.ko),然后编译app执行。
3.4 代码仓库