内核态:
#include <linux/kernel.h> #include <linux/init.h> #include <linux/types.h> #include <linux/spinlock.h> #include <linux/blkdev.h> #include <linux/module.h> #include <linux/fs.h> #include <linux/errno.h> #include <linux/mm.h> #include <linux/cdev.h> #include <linux/sched.h> /* 'k'为幻数,要按照Linux内核的约定方法为驱动程序选择ioctl编号, 应该首先看看include/asm/ioctl.h和Documentation/ioctl-number.txt这两个文件. 对幻数的编号千万不能重复定义,如ioctl-number.txt已经说明‘k'的编号已经被占用的范围为: 'k' 00-0F linux/spi/spidev.h conflict! 'k' 00-05 video/kyro.h conflict! 所以我们在这里分别编号为0x1a和0x1b */ #define CMD_MAGIC 'k' #define KDEV_CMD1 _IO(CMD_MAGIC, 0x1a) #define KDEV_CMD2 _IO(CMD_MAGIC, 0x1b) #define IO_CMD_LEN 256 #define CHAR_DEV_NAME "kdev" #if 1 static int user_cmd_proc(char *user_cmd, char *out_str) { if(strncmp(user_cmd, "sendsig", 7) == 0) { send_sig(SIGUSR1, current, 0); //send SIGUSR 1 } if(strncmp(user_cmd, "showpid", 7) == 0) { sprintf(out_str, "pid=%d tgid=%d ", current->pid, current->tgid); } return 0; } #endif #if 1 int mem_open(struct inode *inode, struct file *filp) { return 0; } int mem_release(struct inode *inode, struct file *filp) { return 0; } char user_cmd[IO_CMD_LEN] = {0}; char out_str[IO_CMD_LEN] = {0}; static int mem_ioctl( struct file *file, unsigned int cmd, unsigned long arg) { printk("mem_ioctl: %d ", cmd); switch(cmd) { case KDEV_CMD1: if(copy_from_user(user_cmd, (int *)arg, IO_CMD_LEN)) return -EFAULT; user_cmd_proc(user_cmd, out_str); if(copy_to_user( (int *)arg, out_str, IO_CMD_LEN)) return -EFAULT; break; case KDEV_CMD2: break; } return 0; } static int mem_major = 0; struct class *pclass = NULL; struct cdev my_dev; static const struct file_operations mem_fops = { .owner = THIS_MODULE, .unlocked_ioctl = mem_ioctl, .open = mem_open, .release = mem_release, }; static int memdev_init(void) { int result; dev_t devno = MKDEV(mem_major, 0); if (mem_major) { /* 静态申请设备号*/ result = register_chrdev_region(devno, 2, CHAR_DEV_NAME); } else { /* 动态分配设备号 */ result = alloc_chrdev_region(&devno, 0, 2, CHAR_DEV_NAME); mem_major = MAJOR(devno); } if (result < 0) { printk("alloc_chrdev failed! "); return result; } cdev_init(&my_dev, &mem_fops); my_dev.owner = THIS_MODULE; my_dev.ops = &mem_fops; cdev_add(&my_dev, MKDEV(mem_major, 0), 2); /*设备数2*/ pclass = class_create(THIS_MODULE, CHAR_DEV_NAME); if (IS_ERR(pclass)) { printk("class_create failed! "); goto failed; } device_create(pclass, NULL, devno, NULL, CHAR_DEV_NAME); return 0; failed: cdev_del(&my_dev); unregister_chrdev_region(devno, 1); return result; } static void memdev_exit(void) { device_destroy(pclass, MKDEV(mem_major, 0)); class_destroy(pclass); cdev_del(&my_dev); unregister_chrdev_region(MKDEV(mem_major, 0), 2); } #endif MODULE_AUTHOR("derek yi"); MODULE_LICENSE("GPL"); module_init(memdev_init); module_exit(memdev_exit);
用户态:
#include <stdio.h> #include <fcntl.h> #include <stdlib.h> #include <string.h> #include <sys/types.h> #include <sys/stat.h> #include <unistd.h> #include <sys/ioctl.h> #include <fcntl.h> #include <errno.h> #include <signal.h> #include <sys/syscall.h> #define CMD_MAGIC 'k' #define KDEV_CMD1 _IO(CMD_MAGIC, 0x1a) #define KDEV_CMD2 _IO(CMD_MAGIC, 0x1b) #define IO_CMD_LEN 256 char kdev_io_buf[IO_CMD_LEN] = {0}; void signal_handler(int signo) { printf("signal_handler: %d ", signo); } pid_t gettid(void) { return syscall(SYS_gettid); } int main() { int fd; int ret = 0; signal(SIGUSR1, (void *)signal_handler); printf("main: pid=%d tid=%d ", getpid(), gettid()); fd = open("/dev/kdev", O_RDWR); if( fd < 0 ) { printf("open memdev WRONG! "); return 0; } sprintf(kdev_io_buf, "sendsig"); ret = ioctl(fd, KDEV_CMD1, kdev_io_buf); printf("ioctl: ret=%d rdata:%s ", ret, kdev_io_buf); sprintf(kdev_io_buf, "showpid"); ret += ioctl(fd, KDEV_CMD1, kdev_io_buf); printf("ioctl: ret=%d rdata:%s ", ret, kdev_io_buf); close(fd); return 0; }
测试结果:
derek@ubox:~/share/ldd6$ sudo insmod kdev.ko derek@ubox:~/share/ldd6$ gcc app.c derek@ubox:~/share/ldd6$ sudo ./a.out main: pid=8584 tid=8584 signal_handler: 10 ioctl: ret=0 rdata: ioctl: ret=0 rdata:pid=8584 tgid=8584