内核中ioctl和mmap使用
附上最基本的ioctl和mmap的操作。
ioctl用switch case 的格式实现, 切记不能用 if else. 其中命令的格式是有规定的,它是由32bit组成,包含dir, type, nr, size。
type和nr是幻数和分命令序号,本人认为这些数随便设置,主要用于避免重复宏定义。
dir是命令类型:读, 写, 读写, 不读不写。
size是要传入参数占多少字节。
mmap中共享的内存,要使用用kmalloc。
内核态 dipper_log.c
#include <linux/module.h>
#include <linux/cdev.h>
#include <linux/fs.h>
#include <linux/device.h>
#include <asm/uaccess.h>
#include "dipper_log.h"
#include <linux/errno.h>
#include <linux/mm.h>
#include <linux/slab.h>
unsigned int major;
dev_t devno;
struct class *cls = {0};
char *shared_mem = NULL;
int log_write(char *content)
{
return 0;
}
int dipper_open(struct inode *a, struct file *b)
{
printk("hello\n");
return 0;
}
long dipper_ioctl(struct file *fd, unsigned int cmd, unsigned long arg)
{
unsigned long ret = 0;
char shm_t[100] = {0};
long arg_v = 666;
printk("ioctl entry!!! \n");
printk("the num arg is %lu", arg+1);
switch(cmd)
{
case DIPPER_HELLO:
//ret |= copy_from_user((void *)&arg_v, (void __user *)&arg, sizeof(long));
printk("the num arg_v is %lu\n\r", arg_v);
sprintf(shm_t, "helloworld:%lu\n", arg_v);
ret |= copy_to_user((void *)arg, &arg_v, 8);
break;
}
if (ret != 0)
{
printk("copy to user failed ! the ret num is %lu\n\r", ret);
}
else
{
printk("copy to user suceed ! %s:%lu\n\r", shm_t, arg_v);
}
return 0;
}
static int dipper_map(struct file *fd, struct vm_area_struct *vma)
{
unsigned long page;
page = virt_to_phys(shared_mem);
remap_pfn_range(vma, (unsigned long)vma->vm_start, page>>PAGE_SHIFT, (unsigned long)(vma->vm_end - vma->vm_start), PAGE_SHARED);
*shared_mem = 100;
return 0;
}
struct file_operations dipper_log_fop=
{
.open = dipper_open,
.unlocked_ioctl = dipper_ioctl,
.mmap = dipper_map,
};
static int log_init(void)
{
int ret;
int minor;
shared_mem = (char *)kmalloc(4096, GFP_KERNEL);
ret = register_chrdev(0, "dipper_log", &dipper_log_fop);
if (ret < 0)
{
printk("register_chrdev failed!\n\r");
}
else
{
printk("register_chrdev succeed!the ret num is %d\n\r", ret);
}
major = ret;
minor = 0;
devno = MKDEV(major, minor);
cls = class_create(THIS_MODULE, "logclass");
device_create(cls, NULL, devno, NULL, "dipper_log");
return 0;
}
static void log_exit(void)
{
device_destroy(cls, devno);
class_destroy(cls);
unregister_chrdev(major, "dipper_log");
return;
}
MODULE_LICENSE("GPL");
module_init(log_init);
module_exit(log_exit);
头文件 dipper_log.h
#define DIPPER_HELLO _IOWR('I', 1, long*)
用户态 user.c
#include <stdio.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <fcntl.h>
#include <errno.h>
#include "dipper_log.h"
#include <sys/mman.h>
int main()
{
int ret = 0;
int fd;
long arg;
char *shared_mem;
fd = open("/dev/dipper_log",O_RDWR);
printf("fd is %d", fd);
ret = ioctl(fd, DIPPER_HELLO, &arg);
printf("the errno is %d", errno);
if(ret != 0)
{
printf("ioctl failed\n\rthe ret num is %d", ret);
}
else
{
printf("success!!\n\rthe num of arg is %lu\n\r", arg);
}
shared_mem = (char*)mmap(0, 4096, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
while(1)
{
sleep(1);
printf("%s\n\r", shared_mem);
}
close(fd);
}
用户态user2
#include <stdio.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <fcntl.h>
#include <errno.h>
#include "dipper_log.h"
#include <sys/mman.h>
int main()
{
int ret = 0;
int fd;
long arg;
char *shared_mem;
fd = open("/dev/dipper_log",O_RDWR);
shared_mem = (char*)mmap(0, 4096, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
sprintf(shared_mem, "Write from user2, hello back !!!\n\r");
close(fd);
}
Makefile
obj-m+=dipper_log.o
all:user user2
make -C /lib/modules/$(shell uname -r)/build M=$(shell pwd) modules;
user:user.c
gcc $< -o $@
user2:user2.c
gcc $< -o $@
clean:
make -C /lib/modules/$(shell uname -r)/build M=$(shell pwd) clean;
本代码实验用ioctl读取一个参数666。
同时两个进程,访问内核态内存,一个写入,一个监听。
所以mmap最好配和mutex进行操作,并封装一层函数,避免踩内存。