• 用户空间内核空间ipc总结(sockopt,ioctl,mmap,netlink,proc,seq,file,copy_user)


    多数的 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的结构体,比如说:

    1. static struct nf_sockopt_ops nso = {  
    2.      .pf  = PF_INET,       // 协议族  
    3.      .set_optmin = 常数,    // 定义最小set命令字  
    4.      .set_optmax = 常数+N,  // 定义最大set命令字  
    5.      .set  = do_nso_set,   // 定义set处理函数  
    6.      .get_optmin = 常数,    // 定义最小get命令字  
    7.      .get_optmax = 常数+N,  // 定义最大get命令字  
    8.      .get  = do_nso_get,   // 定义set处理函数  
    9. };  
    复制代码



    其中命令字不能与系统已有的命令字重复。set/get处理函数是直接由用户空间的set/getsockopt函数调用的。

    nf_sockopt.jpg

    从这个图里面可以看出来,这种方法的本质就是调用是copy_from_user()/copy_to_user()方法完成内核和用户通信的,这样其实效率不高,多用在传递控制选项信息,不适合用做大量数据的传输。copy_from_user()/copy_to_user()我讲在后面介绍...  当然对于linux任何都是文件那么我想应该也是可以定义自己的ioctl的,这个在后面的
    copy_xx_user的块设备中讲解
      setsockopt/getsockopt  kernel部分代码:
      

    1.       static int recv_msg(struct sock *sk, int cmd, void *user, unsigned int len)
    2. {
    3.     int ret = 0;
    4.     printk(KERN_INFO "sockopt: recv_msg()/n"); 
    5.     /*
    6.     switch(cmd)
    7.     {
    8.     case IMP1_SET:
    9.     {
    10.         char umsg[64];
    11.         memset(umsg, 0, sizeof(char)*64);
    12.         copy_from_user(umsg, user, sizeof(char)*64);
    13.         printk("umsg: %s", umsg);
    14.     }
    15.     break;
    16.     }
    17.     */
    18.     if (cmd == SOCKET_OPS_SET)
    19.     {
    20.         char umsg[64];
    21.         int len = sizeof(char)*64;
    22.         memset(umsg, 0, len);
    23.         ret = copy_from_user(umsg, user, len);
    24.         printk("recv_msg: umsg = %s. ret = %d/n", umsg, ret);        
    25.     }
    26.     return 0;
    27. } 

    28. static int send_msg(struct sock *sk, int cmd, void *user, int *len)
    29. {
    30.     int ret = 0;
    31.     printk(KERN_INFO "sockopt: send_msg()/n"); 
    32.     if (cmd == SOCKET_OPS_GET)
    33.     {
    34.         ret = copy_to_user(user, KMSG, KMSG_LEN);
    35.         printk("send_msg: umsg = %s. ret = %d. success/n", KMSG, ret);    
    36.     }
    37.     return 0;
    38. } 

    39. static struct nf_sockopt_ops test_sockops =
    40. {
    41.     .pf = PF_INET,
    42.     .set_optmin = SOCKET_OPS_SET,
    43.     .set_optmax = SOCKET_OPS_MAX,
    44.     .set = recv_msg,
    45.     .get_optmin = SOCKET_OPS_GET,
    46.     .get_optmax = SOCKET_OPS_MAX,
    47.     .get = send_msg,
    48. }; 
    49.    
    复制代码



    setsockopt/getsockopt  user部分代码:

    1. /*call function recv_msg()*/
    2.     ret = setsockopt(sockfd, IPPROTO_IP, SOCKET_OPS_SET, UMSG, UMSG_LEN);
    3.     printf("setsockopt: ret = %d. msg = %s/n", ret, UMSG);
    4.     len = sizeof(char)*64; 

    5.     /*call function send_msg()*/
    6.     ret = getsockopt(sockfd, IPPROTO_IP, SOCKET_OPS_GET, kmsg, &len);
    7.     printf("getsockopt: ret = %d. msg = %s/n", ret, kmsg);
    8.     if (ret != 0)
    9.     {
    10.         printf("getsockopt error: errno = %d, errstr = %s/n", errno, strerror(errno));
    11.     } 
    复制代码




    二. mmap共享内存
      采用共享内存通信的一个显而易 见的好处是效率高,因为进程可以直接读写内存,而不需要任何数据的拷贝。对于像管道和消息队列等通信方式,则需要在内核和用户空间进行四次的数据拷贝,而 共享内存则只拷贝两次数据[1]:一次从输入文件到共享内存区,另一次从共享内存区到输出文件。实际上,进程之间在共享内存时,并不总是读写少量数据后就 解除映射,有新的通信时,再重新建立共享内存区域。而是保持共享区域,直到通信完毕为止,这样,数据内容一直保存在共享内存中,并没有写回文件。共享内存 中的内容往往是在解除映射时才写回文件的。因此,采用共享内存的通信方式效率是非常高的.
    kernel: 

    1.    #include <linux/config.h>
    2. #include <linux/module.h>
    3. #include <linux/moduleparam.h>
    4. #include <linux/init.h>

    5. #include <linux/kernel.h>   /* printk() */
    6. #include <linux/slab.h>   /* kmalloc() */
    7. #include <linux/fs.h>       /* everything... */
    8. #include <linux/errno.h>    /* error codes */
    9. #include <linux/types.h>    /* size_t */
    10. #include <linux/mm.h>
    11. #include <linux/kdev_t.h>
    12. #include <asm/page.h>
    13. #include <linux/cdev.h>
    14. #include <linux/device.h>
    15. #include <linux/gfp.h>

    16. static unsigned char *myaddr=NULL;
    17. static int simple_major = 0;
    18. module_param(simple_major, int, 0);


    19. MODULE_LICENSE("GPL");
    20. MODULE_AUTHOR("Kenthy@163.com."); 
    21. MODULE_DESCRIPTION("Kernel study and test."); 


    22. /*
    23. * Common VMA ops.
    24. */

    25. void simple_vma_open(struct vm_area_struct *vma)
    26. {
    27.         printk(KERN_NOTICE "Simple VMA open, virt %lx, phys %lx/n",
    28.                         vma->vm_start, vma->vm_pgoff << PAGE_SHIFT);
    29. }

    30. void simple_vma_close(struct vm_area_struct *vma)
    31. {
    32.         printk(KERN_NOTICE "Simple VMA close./n");
    33. }

    34. struct page *simple_vma_nopage(struct vm_area_struct *vma,
    35.                 unsigned long address, int *type)
    36. {
    37.         struct page *pageptr;
    38.         unsigned long offset = (address - vma->vm_start); 
    39.         if (offset>PAGE_SIZE*2)
    40.         {
    41.                 printk("out of size/n");
    42.                 return NULL;
    43.         }
    44.         printk("in vma_nopage: offset=%u/n", offset);

    45.         if(offset<PAGE_SIZE) // the first page
    46.                 pageptr=virt_to_page(myaddr);
    47.         else        // the second page
    48.                 pageptr=virt_to_page(myaddr+PAGE_SIZE);

    49.         get_page(pageptr);

    50.         return pageptr;
    51. }

    52. static struct vm_operations_struct simple_nopage_vm_ops = {
    53.         .open =   simple_vma_open,
    54.                 .close =  simple_vma_close,
    55.                 .nopage = simple_vma_nopage,
    56. };


    57. static int simple_open (struct inode *inode, struct file *filp)
    58. {
    59.         return 0;
    60. }

    61. static int simple_release(struct inode *inode, struct file *filp)
    62. {
    63.         return 0;
    64. }

    65. static int simple_nopage_mmap(struct file *filp, struct vm_area_struct *vma)
    66. {
    67.         unsigned long offset = vma->vm_pgoff << PAGE_SHIFT;

    68.         printk("enter simple_nopage_mmap: offset=%u, vma->vm_pgoff=%u/n", offset, vma->vm_pgoff);
    69.         if (offset >= __pa(high_memory) || (filp->f_flags & O_SYNC))
    70.                 vma->vm_flags |= VM_IO;
    71.         vma->vm_flags |= VM_RESERVED;

    72.         vma->vm_ops = &simple_nopage_vm_ops;
    73.         simple_vma_open(vma);
    74.         return 0;
    75. }

    76. /*
    77. * Set up the cdev structure for a device.
    78. */
    79. static void simple_setup_cdev(struct cdev *dev, int minor,
    80.                 struct file_operations *fops)
    81. {
    82.         int err, devno = MKDEV(simple_major, minor);

    83.         cdev_init(dev, fops);
    84.         dev->owner = THIS_MODULE;
    85.         dev->ops = fops;
    86.         err = cdev_add (dev, devno, 1);
    87.         /* Fail gracefully if need be */
    88.         if (err)
    89.                 printk (KERN_NOTICE "Error %d adding simple%d", err, minor);
    90. }

    91. static struct file_operations simple_nopage_ops = {
    92.         .owner   = THIS_MODULE,
    93.         .open    = simple_open,
    94.         .release = simple_release,
    95.         .mmap    = simple_nopage_mmap,
    96. };

    97. /*
    98. * We export two simple devices.  There's no need for us to maintain any
    99. * special housekeeping info, so we just deal with raw cdevs.
    100. */
    101. static struct cdev SimpleDevs;

    102. /*
    103. * Module housekeeping.
    104. */
    105. static int simple_init(void)
    106. {
    107.         int result;
    108.         //unsigned int addr1, addr2;
    109.         dev_t dev = MKDEV(simple_major, 0);

    110.         /* Figure out our device number. */
    111.         if (simple_major)
    112.                 result = register_chrdev_region(dev, 1, "simple_nopage");
    113.         else {
    114.                 result = alloc_chrdev_region(&dev, 0, 1, "simple_nopage");
    115.                 simple_major = MAJOR(dev);
    116.         }
    117.         if (result < 0) {
    118.                 printk(KERN_WARNING "simple_nopage: unable to get major %d/n", simple_major);
    119.                 return result;
    120.         }
    121.         if (simple_major == 0)
    122.                 simple_major = result;

    123.         /* Now set up two cdevs. */
    124.         simple_setup_cdev(&SimpleDevs, 0, &simple_nopage_ops);

    125.         myaddr = __get_free_pages(GFP_KERNEL, 1);
    126.         if (!myaddr)
    127.                 return -ENOMEM;
    128.         // for test
    129.         strcpy(myaddr, "1234567890");
    130.         strcpy(myaddr+PAGE_SIZE, "abcdefghij");
    131.         return 0;
    132. }


    133. static void simple_cleanup(void)
    134. {
    135.         cdev_del(&SimpleDevs);
    136.         unregister_chrdev_region(MKDEV(simple_major, 0), 1);
    137. }


    138. module_init(simple_init);
    139. module_exit(simple_cleanup);
    140.   
    复制代码



    user:

    1. #include </work/apue/ourhdr.h>
    2. #include <fcntl.h>
    3. #include <sys/mman.h>

    4. int main(int argc, char *argv[])
    5. {
    6.         int fdin, fdout;
    7.         void *src, *dst;
    8.         struct stat statbuf;
    9.         unsigned char sz[1024]={0};
    10.         if ((fdin = open("/dev/simple_nopage", O_RDONLY)) < 0)
    11.                 err_sys("can't open /dev/simple_nopage for reading");

    12.         if ((src = mmap(NULL, 4096*2, PROT_READ, MAP_SHARED,
    13.                                         fdin, 0)) == MAP_FAILED)
    14.                 err_sys("mmap error for simplen");

    15.         memcpy(sz, src, 11);
    16.         sz[10]='/0';
    17.         printf("%x/n", src);
    18.         printf("%s/n/n", sz);

    19.         memcpy(sz, src+4096, 11);
    20.         printf("%x/n", src+4096);
    21.         printf("%s/n", sz);

    22.         exit(0);
    23. }
    复制代码



    mmap加载文件后注意还要mknod

    三. netlink
      看看duanjigang兄的这两篇文章就可以了
    netlink socket 编程之 why & how 
      http://linux.chinaunix.net/bbs/viewthread.php?tid=1031932&extra=page%3D2%26amp%3Bfilter%3Ddigest
    使用netlink通讯时需要注意的一些问题   
    [url]http://linux.chinaunix.net/bbs/viewthread.php?tid=1144547&extra=page%3D2%26amp%3Bfilter%3Ddigest[/url]

    四. 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

  • 相关阅读:
    VueJS中学习使用Vuex详解
    https://www.cnblogs.com/chinabin1993/p/9848720.html
    5分钟带你入门vuex(vue状态管理)
    引用第三方 chalk 模块
    Vue-Grid-Layout分享一款好用的可拖拽组件
    vue-grid-layout
    拖拽 ‘vue-grid-layout’ 插件了解下
    Vue国际化处理 vue-i18n 以及项目自动切换中英文
    Java线程池ThreadPoolExecutor使用和分析(三)
    Java线程池ThreadPoolExecutor使用和分析(二)
  • 原文地址:https://www.cnblogs.com/shaoguangleo/p/2805887.html
Copyright © 2020-2023  润新知