用户空间内核空间ipc总结(sockopt,ioctl,mmap,netlink,proc,seq,file,copy_user)
2016-05-25 11:51
525 查看
多数的 Linux 内核态程序都需要和用户空间的进程交换数据,但 Linux 内核态无法对传统的 Linux 进程间同步和通 信的方法提供足够的支持! 本文就总结下常见的ipc, getsockopt/setsockopt mmap netlink/socket proc/seq copy_from_user/copy_to_user 文件。 采用先讲解后测试代码的方式,netlink和proc由于江哥和段兄都写的比较好了我就贴了链接... 好了不废话了开始 一.getsockopt/setsockopt 最近看ebtables源码,发现与内核的ipc是采用的getsockopt, 具体实现是在内核中用nf_register_sockopt函数 注册一个nf_sockopt_ops的结构体,比如说: static struct nf_sockopt_ops nso = { .pf = PF_INET, // 协议族 .set_optmin = 常数, // 定义最小set命令字 .set_optmax = 常数+N, // 定义最大set命令字 .set = do_nso_set, // 定义set处理函数 .get_optmin = 常数, // 定义最小get命令字 .get_optmax = 常数+N, // 定义最大get命令字 .get = do_nso_get, // 定义set处理函数 }; 复制代码 其中命令字不能与系统已有的命令字重复。set/get处理函数是直接由用户空间的set/getsockopt函数调用的。 从这个图里面可以看出来,这种方法的本质就是调用是copy_from_user()/copy_to_user()方法完成内核和用户 通信的,这样其实效率不高,多用在传递控制选项信息,不适合用做大量数据的传输。copy_from_user()/copy _to_user()我讲在后面介绍... 当然对于linux任何都是文件那么我想应该也是可以定义自己的ioctl的,这个在后面的 copy_xx_user的块设备中讲解 setsockopt/getsockopt kernel部分代码: static int recv_msg(struct sock *sk, int cmd, void *user, unsigned int len) { int ret = 0; printk(KERN_INFO "sockopt: recv_msg()\n"); /* switch(cmd) { case IMP1_SET: { char umsg[64]; memset(umsg, 0, sizeof(char)*64); copy_from_user(umsg, user, sizeof(char)*64); printk("umsg: %s", umsg); } break; } */ if (cmd == SOCKET_OPS_SET) { char umsg[64]; int len = sizeof(char)*64; memset(umsg, 0, len); ret = copy_from_user(umsg, user, len); printk("recv_msg: umsg = %s. ret = %d\n", umsg, ret); } return 0; } static int send_msg(struct sock *sk, int cmd, void *user, int *len) { int ret = 0; printk(KERN_INFO "sockopt: send_msg()\n"); if (cmd == SOCKET_OPS_GET) { ret = copy_to_user(user, KMSG, KMSG_LEN); printk("send_msg: umsg = %s. ret = %d. success\n", KMSG, ret); } return 0; } static struct nf_sockopt_ops test_sockops = { .pf = PF_INET, .set_optmin = SOCKET_OPS_SET, .set_optmax = SOCKET_OPS_MAX, .set = recv_msg, .get_optmin = SOCKET_OPS_GET, .get_optmax = SOCKET_OPS_MAX, .get = send_msg, }; 复制代码 setsockopt/getsockopt user部分代码: /*call function recv_msg()*/ ret = setsockopt(sockfd, IPPROTO_IP, SOCKET_OPS_SET, UMSG, UMSG_LEN); printf("setsockopt: ret = %d. msg = %s\n", ret, UMSG); len = sizeof(char)*64; /*call function send_msg()*/ ret = getsockopt(sockfd, IPPROTO_IP, SOCKET_OPS_GET, kmsg, &len); printf("getsockopt: ret = %d. msg = %s\n", ret, kmsg); if (ret != 0) { printf("getsockopt error: errno = %d, errstr = %s\n", errno, strerror(errno)); } 复制代码 二. mmap共享内存 采用共享内存通信的一个显而易 见的好处是效率高,因为进程可以直接读写内存,而不需要任何数据的拷贝。 对于像管道和消息队列等通信方式,则需要在内核和用户空间进行四次的数据拷贝,而 共享内存则只拷贝两次 数据[1]:一次从输入文件到共享内存区,另一次从共享内存区到输出文件。实际上,进程之间在共享内存时, 并不总是读写少量数据后就 解除映射,有新的通信时,再重新建立共享内存区域。而是保持共享区域,直到 通信完毕为止,这样,数据内容一直保存在共享内存中,并没有写回文件。共享内存 中的内容往往是在解除 映射时才写回文件的。因此,采用共享内存的通信方式效率是非常高的. kernel: #include <linux/config.h> #include <linux/module.h> #include <linux/moduleparam.h> #include <linux/init.h> #include <linux/kernel.h> /* printk() */ #include <linux/slab.h> /* kmalloc() */ #include <linux/fs.h> /* everything... */ #include <linux/errno.h> /* error codes */ #include <linux/types.h> /* size_t */ #include <linux/mm.h> #include <linux/kdev_t.h> #include <asm/page.h> #include <linux/cdev.h> #include <linux/device.h> #include <linux/gfp.h> static unsigned char *myaddr=NULL; static int simple_major = 0; module_param(simple_major, int, 0); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Kenthy@163.com."); MODULE_DESCRIPTION("Kernel study and test."); /* * Common VMA ops. */ void simple_vma_open(struct vm_area_struct *vma) { printk(KERN_NOTICE "Simple VMA open, virt %lx, phys %lx\n", vma->vm_start, vma->vm_pgoff << PAGE_SHIFT); } void simple_vma_close(struct vm_area_struct *vma) { printk(KERN_NOTICE "Simple VMA close.\n"); } struct page *simple_vma_nopage(struct vm_area_struct *vma, unsigned long address, int *type) { struct page *pageptr; unsigned long offset = (address - vma->vm_start); if (offset>PAGE_SIZE*2) { printk("out of size\n"); return NULL; } printk("in vma_nopage: offset=%u\n", offset); if(offset<PAGE_SIZE) // the first page pageptr=virt_to_page(myaddr); else // the second page pageptr=virt_to_page(myaddr+PAGE_SIZE); get_page(pageptr); return pageptr; } static struct vm_operations_struct simple_nopage_vm_ops = { .open = simple_vma_open, .close = simple_vma_close, .nopage = simple_vma_nopage, }; static int simple_open (struct inode *inode, struct file *filp) { return 0; } static int simple_release(struct inode *inode, struct file *filp) { return 0; } static int simple_nopage_mmap(struct file *filp, struct vm_area_struct *vma) { unsigned long offset = vma->vm_pgoff << PAGE_SHIFT; printk("enter simple_nopage_mmap: offset=%u, vma->vm_pgoff=%u\n", offset, vma->vm_pgoff); if (offset >= __pa(high_memory) || (filp->f_flags & O_SYNC)) vma->vm_flags |= VM_IO; vma->vm_flags |= VM_RESERVED; vma->vm_ops = &simple_nopage_vm_ops; simple_vma_open(vma); return 0; } /* * Set up the cdev structure for a device. */ static void simple_setup_cdev(struct cdev *dev, int minor, struct file_operations *fops) { int err, devno = MKDEV(simple_major, minor); cdev_init(dev, fops); dev->owner = THIS_MODULE; dev->ops = fops; err = cdev_add (dev, devno, 1); /* Fail gracefully if need be */ if (err) printk (KERN_NOTICE "Error %d adding simple%d", err, minor); } static struct file_operations simple_nopage_ops = { .owner = THIS_MODULE, .open = simple_open, .release = simple_release, .mmap = simple_nopage_mmap, }; /* * We export two simple devices. There's no need for us to maintain any * special housekeeping info, so we just deal with raw cdevs. */ static struct cdev SimpleDevs; /* * Module housekeeping. */ static int simple_init(void) { int result; //unsigned int addr1, addr2; dev_t dev = MKDEV(simple_major, 0); /* Figure out our device number. */ if (simple_major) result = register_chrdev_region(dev, 1, "simple_nopage"); else { result = alloc_chrdev_region(&dev, 0, 1, "simple_nopage"); simple_major = MAJOR(dev); } if (result < 0) { printk(KERN_WARNING "simple_nopage: unable to get major %d\n", simple_major); return result; } if (simple_major == 0) simple_major = result; /* Now set up two cdevs. */ simple_setup_cdev(&SimpleDevs, 0, &simple_nopage_ops); myaddr = __get_free_pages(GFP_KERNEL, 1); if (!myaddr) return -ENOMEM; // for test strcpy(myaddr, "1234567890"); strcpy(myaddr+PAGE_SIZE, "abcdefghij"); return 0; } static void simple_cleanup(void) { cdev_del(&SimpleDevs); unregister_chrdev_region(MKDEV(simple_major, 0), 1); } module_init(simple_init); module_exit(simple_cleanup); 复制代码 user: #include </work/apue/ourhdr.h> #include <fcntl.h> #include <sys/mman.h> int main(int argc, char *argv[]) { int fdin, fdout; void *src, *dst; struct stat statbuf; unsigned char sz[1024]={0}; if ((fdin = open("/dev/simple_nopage", O_RDONLY)) < 0) err_sys("can't open /dev/simple_nopage for reading"); if ((src = mmap(NULL, 4096*2, PROT_READ, MAP_SHARED, fdin, 0)) == MAP_FAILED) err_sys("mmap error for simplen"); memcpy(sz, src, 11); sz[10]='\0'; printf("%x\n", src); printf("%s\n\n", sz); memcpy(sz, src+4096, 11); printf("%x\n", src+4096); printf("%s\n", sz); exit(0); } 复制代码 mmap加载文件后注意还要mknod 三. netlink 看看duanjigang兄的这两篇文章就可以了 netlink socket 编程之 why & how http://linux.chinaunix.net/bbs/viewthread.php?tid=1031932&extra=page%3D2%26amp%3Bfilter%3Ddigest 使用netlink通讯时需要注意的一些问题 http://linux.chinaunix.net/bbs/viewthread.php?tid=1144547&extra=page%3D2%26amp%3Bfilter%3Ddigest 四. proc/seq 记得proc和seq是我面试实习的时候一个小笔试题,当时小弟我很是无助在dreamice大哥的无私的指点,甚至可以说你 替我完成了作业,小弟我真是惭愧,也正是下面两个帖子诞生的背景^_^ proc文件系统剖析 http://linux.chinaunix.net/bbs/viewthread.php?tid=1044497&extra=page%3D2%26amp%3Bfilter%3Ddigest Seq_file File System实例剖析 http://linux.chinaunix.net/bbs/viewthread.php?tid=1044672&extra=page%3D2%26amp%3Bfilter%3Ddigest
|
相关文章推荐
- Linux System Parameter Tuning
- Linux下利用rsync实现多服务器文件同步
- Python centOS 安装
- Linux下的split 命令(将一个大文件根据行数平均分成若干个小文件)
- IIS服务器发布同一个IP多个域名多个网站
- 如何解决日志文件过大问题
- ClassCastException Log4jLoggerFactory LoggerContex
- LINUX 内核算杂 七杂 八
- 使用JMeter对Tomcat进行压力测试与Tomcat性能调优
- 使用xshell 登陆aws的ec2
- yarn对mapreducev1的重构,根本的思想是将 JobTracker 两个主要的功能分离成单独的组件,这两个功能是资源管理和任务调度 / 监控。
- Sort类——Hadoop
- Gnu/Linux系统C编程之-系统限制
- tomcat报错org.springframework.web.context.ContextLoaderListener找不到
- linux内核系统调用和标准C库函数的关系分析
- zabbix如何监控nginx性能
- linux evaluate inode
- 检查Linux服务器性能
- Linux免密码登录
- Linux内核学习笔记2