免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
12下一页
最近访问板块 发新帖
查看: 29276 | 回复: 15
打印 上一主题 下一主题

用户空间内核空间ipc总结(sockopt,ioctl,mmap,netlink,proc,seq,file,copy_user) [复制链接]

论坛徽章:
0
跳转到指定楼层
1 [收藏(0)] [报告]
发表于 2010-01-16 16:05 |只看该作者 |倒序浏览
多数的 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函数调用的。



从这个图里面可以看出来,这种方法的本质就是调用是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

[ 本帖最后由 ubuntuer 于 2010-1-16 16:10 编辑 ]

评分

参与人数 1可用积分 +15 收起 理由
Godbach + 15 多谢分享

查看全部评分

论坛徽章:
0
2 [报告]
发表于 2010-01-16 16:07 |只看该作者
五. 文件
  使用文件来通信其实是有点牵强的,不过这个当时在bell的一个项目中,我确实这个干过就来出来了
  

  1.    #include <linux/kernel.h>
  2. #include <linux/module.h>
  3. #include <linux/fs.h>
  4. #include <asm/uaccess.h>
  5. #include <linux/mm.h>

  6. MODULE_LICENSE("GPL");
  7. MODULE_AUTHOR("Kenthy@163.com.");
  8. MODULE_DESCRIPTION("Kernel study and test.");

  9. struct file *filp;
  10. mm_segment_t fs;
  11.   
  12. void SLEEP_MILLI_SEC(int nMilliSec)
  13. {
  14. long timeout = (nMilliSec) * 100 / 1000;
  15.   while(timeout > 0)
  16.    {
  17.      timeout = schedule_timeout(timeout);
  18.    }
  19. }

  20. void fileread(const char * filename)
  21. {
  22.   struct file *filp;
  23.   struct inode *inode;
  24.   mm_segment_t fs;
  25.   off_t fsize;
  26.   char *buf;
  27.   unsigned long magic;
  28.   filp=filp_open(filename,O_RDONLY,0);
  29.   inode=filp->f_dentry->d_inode;
  30.   
  31.   magic=inode->i_sb->s_magic;
  32.   printk("file system magic:%li \n",magic);
  33.   printk("super blocksize:%li \n",inode->i_sb->s_blocksize);
  34.   printk("inode %li \n",inode->i_ino);
  35.   fsize=inode->i_size;
  36.   printk("file size:%i \n",(int)fsize);
  37.   buf=(char *) kmalloc(fsize+1,GFP_ATOMIC);

  38.   fs=get_fs();
  39.   set_fs(KERNEL_DS);
  40.   filp->f_op->read(filp,buf,fsize,&(filp->f_pos));
  41.   set_fs(fs);

  42.   buf[fsize]='\0';
  43.   printk("The File Content is:\n");
  44.   printk("%s\n",buf);

  45.   kfree(buf);
  46.   filp_close(filp,NULL);
  47. }

  48. void filewrite(char* filename, char* data)
  49. {
  50.   fs=get_fs();
  51.   set_fs(KERNEL_DS);
  52.   filp->f_op->write(filp, data, strlen(data),&filp->f_pos);
  53.   set_fs(fs);
  54. }

  55. void test_write(char* filename)
  56. {
  57. int i;
  58. char data[20];
  59.   filp = filp_open(filename, O_RDWR|O_APPEND, 0644);
  60.   if(IS_ERR(filp))
  61.    {
  62.     printk("open error...\n");
  63.     return;
  64.    }


  65. for(i=0;i<10;i++)
  66. {
  67.   sprintf(data, "%s, %d\n", "kernel write test", i);
  68.   filewrite(filename, data);
  69.   SLEEP_MILLI_SEC(1000);
  70. }
  71. }

  72. int init_module()
  73. {
  74.   char *filename="/root/log";

  75.   printk("%s\n", "begin write data");
  76.   test_write(filename);
  77.   
  78.   printk("%s\n", "begin read data");
  79.   fileread(filename);
  80.   return 0;
  81. }

  82. void cleanup_module()
  83. {
  84.   filp_close(filp,NULL);
  85.   printk("Good,Bye!\n");
  86. }
  87.    
复制代码


   上面的代码还附送了内核中如何sleep

六. copy_xxx_user
  copy_from_user函数的目的是从用户空间拷贝数据到内核空间,失败返回没有被拷贝的字节数,成功返回0.这么简单的一个函数却含盖了许多关于内核方面的知识,比如内核关于异常出错的处理.从用户空间拷贝数据到内核中时必须很小心,假如用户空间的数据地址是个非法的地址,或是超出用户空间的范围,或是那些地址还没有被映射到,都可能对内核产生很大的影响,如oops,或被造成系统安全的影响.所以copy_from_user函数的功能就不只是从用户空间拷贝数据那样简单了,他还要做一些指针检查连同处理这些
问题的方法. 函数原型在[arch/i386/lib/usercopy.c]中
  

  1. unsigned long
  2. copy_from_user(void *to, const void __user *from, unsigned long n)
  3. {
  4.     might_sleep();   
  5.     if (access_ok(VERIFY_READ, from, n))
  6.         n = __copy_from_user(to, from, n);
  7.     else
  8.         memset(to, 0, n);
  9.     return n;
  10. }
  11.   
复制代码

  具体的代码实现我就不讲了,我自己也看的懵懂!!!还是来使用使用吧

  1. #include <linux/kernel.h>
  2. #include <linux/module.h>
  3. #include <linux/init.h>
  4. #include <linux/cdev.h>
  5. #include <linux/fs.h>
  6. #include <linux/sched.h>
  7. #include <asm/uaccess.h>

  8. MODULE_LICENSE("GPL");
  9. MODULE_AUTHOR("Kenthy@163.com.");
  10. MODULE_DESCRIPTION("Kernel study and test.");


  11. #define DP_MAJOR 251//主设备号
  12. #define DP_MINOR 0  //次设备号
  13. #define DEV_NAME "kenthy"

  14. static int char_read(struct file *filp, char __user *buffer, size_t, loff_t*);
  15. static int char_open(struct inode*, struct file*);
  16. static int char_write(struct file *filp, const char __user *buffer, size_t, loff_t*);
  17. static int char_release(struct inode*, struct file*);

  18. static int chropen;
  19. struct cdev *chardev;
  20. static int len;

  21. static char *to;
  22. static const struct file_operations char_ops={
  23.     .read = char_read,
  24.     .write = char_write,
  25.     .open = char_open,
  26.     .release = char_release,
  27. };

  28. static int __init char_init(void)
  29. {
  30.     dev_t dev;
  31.     printk(KERN_ALERT"Initing......\n");
  32.     dev = MKDEV(DP_MAJOR, DP_MINOR);
  33.     chardev = cdev_alloc();

  34.     if(chardev == NULL){
  35.         return -1;
  36.     }
  37.     if(register_chrdev_region(dev, 10, DEV_NAME)){
  38.         printk(KERN_ALERT"Register char dev error\n");
  39.         return -1;
  40.     }
  41.     chropen = 0;
  42.     len = 0;
  43.     cdev_init(chardev, &char_ops);
  44.     if(cdev_add(chardev, dev, 1)){
  45.         printk(KERN_ALERT"Add char dev error!\n");
  46.     }
  47.     return 0;
  48. }

  49. static int char_open(struct inode *inode, struct file *file)
  50. {
  51.     if(chropen == 0)
  52.         chropen++;
  53.     else{
  54.         printk(KERN_ALERT"Another process open the char device\n");
  55.         return -1;
  56.     }
  57.     try_module_get(THIS_MODULE);
  58.     return 0;
  59. }

  60. static int char_release(struct inode *inode,struct file *file)
  61. {
  62.     chropen--;
  63.     module_put(THIS_MODULE);
  64.     return 0;
  65. }

  66. static int char_read(struct file *filp,char __user *buffer,size_t length,loff_t *offset)
  67. {
  68.     unsigned long nn;
  69.     nn = copy_to_user(buffer, to, length);
  70.     printk("nn = %ld\n", nn);
  71.     printk("buffer = %s\n", buffer);
  72.     return length;
  73. }

  74. static int char_write(struct file *filp, const char __user *buffer, size_t length, loff_t *offset)
  75. {
  76.     unsigned long n;
  77.     to = (char *)kmalloc((sizeof(char)) * (length+1), GFP_KERNEL);
  78.     memset(to, '\0', length+1);
  79.     n = copy_from_user(to, buffer, length);
  80.     printk("n = %ld\n", n);
  81.     printk("to = %s\n", to);
  82.     return length;
  83. }

  84. static void __exit module_close(void)
  85. {
  86.         len=0;
  87.         printk(KERN_ALERT"Unloading..........\n");

  88.         unregister_chrdev_region(MKDEV(DP_MAJOR,DP_MINOR),10);
  89.         cdev_del(chardev);
  90. }

  91. module_init(char_init);
  92. module_exit(module_close);
复制代码

这个与mmap的类似都依赖于一个字符设备文件
sudo insmod chardev.ko
在使用测试程序前我们要创建一个字符设备:
#mknod  /dev/chardev0  c  251  0
测试程序代码如下:
  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include <string.h>
  4. #include <unistd.h>
  5. #include <fcntl.h>
  6. int main(int args, char *argv[])
  7. {
  8.     int testdev;
  9.     int i, rf = 0;
  10.     char buf[15];

  11.     memset(buf, '\0', 15);
  12.     testdev = open("/dev/chardev0", O_RDWR);
  13.     if(testdev == -1){
  14.         perror("open\n");
  15.         exit(0);
  16.     }
  17.     if((write(testdev, "1111111111", 10)) < 0){
  18.         perror("Write error!\n");
  19.         exit(0);
  20.     }
  21.     close(testdev);
  22.     printf("write finish!\n");
  23.     testdev = open("/dev/chardev0", O_RDWR);   
  24.     rf = read(testdev, buf, 3);
  25.     if(rf < 0)
  26.         perror("read error\n");
  27.     printf("Read: %s\n", buf);
  28.     close(testdev);
  29.    
  30.     return 0;
  31. }
复制代码


运行程序:
#./test
Read: 111
看一下内核信息:
#dmesg
最后几行信息如下:
nn = 0
buffer = 1111111111
n = 0
to = 111
这就说明设备注册正确,从用户到内核copy_from_user和从内核到用户copy_to_user都顺利完成了。呵呵,你是不是觉得还少了点什么东西,对我前面说还有ioctl没有讲
在内核空间中ioctl是很多内核操作结构的一个成员函数,如文件操作结构struct file_operations(include/linux/fs.h)、协议操作结构struct proto_ops(include/linux/net.h)等、tty操作结构struct tty_driver(include/linux/tty_driver.h)等,而这些操作结构分别对应各种内核设备,只要在用户空间打开这些设备,如I/O设备可用open(2)打开,网络协议可用socket(2)打开等,获取一个文件描述符后,就可以在这个描述符上调用ioctl(2)来向内核交换数据。
ioctl(2)函数的基本使用格式为:
int ioctl(int fd, int cmd, void *data)
第一个参数是文件描述符;cmd是操作命令,一般分为GET、SET以及其他类型命令,GET是用户空间进程从内核读数据,SET是用户空间进程向内核写数据,cmd虽然是一个整数,但是有一定的参数格式的,下面再详细说明;第三个参数是数据起始位置指针,
cmd命令参数是个32位整数,分为四部分:
dir(2b)  size(14b)  type(8b) nr(8b)
详细定义cmd要包括这4个部分时可使用宏_IOC(dir,type,nr,size)来定义,而最简单情况下使用_IO(type, nr)来定义就可以了,这些宏都在include/asm/ioctl.h中定义
本文cmd定义为:
#define NEWCHAR_IOC_MAGIC   'M'
#define NEWCHAR_SET    _IO(NEWCHAR_IOC_MAGIC, 0)
#define NEWCHAR_GET    _IO(NEWCHAR_IOC_MAGIC, 1)
#define NEWCHAR_IOC_MAXNR   1

要定义自己的ioctl操作,可以有两个方式,一种是在现有的内核代码中直接添加相关代码进行支持,比如想通过socket描述符进行ioctl操作,可在net/ipv4/af_inet.c中的inet_ioctl()函数中添加自己定义的命令和相关的处理函数,重新编译内核即可,不过这种方法一般不推荐;第二种方法是定义自己的内核设备,通过设备的ioctl()来操作,可以编成模块,这样不影响原有的内核,这是最通常的做法。
   大致过程如下

  1. 进行ioctl调用的基本处理函数
  2. static int newchar_ioctl(struct inode *inode, struct file *filep,
  3.       unsigned int cmd, unsigned long arg)
  4. {
  5. int  ret;
  6. // 首先检查cmd是否合法
  7. if (_IOC_TYPE(cmd) != NEWCHAR_IOC_MAGIC) return -EINVAL;
  8. if (_IOC_NR(cmd) > NEWCHAR_IOC_MAXNR) return -EINVAL;
  9. // 错误情况下的缺省返回值
  10. ret = EINVAL;
  11. switch(cmd)
  12. {
  13. case KNEWCHAR_SET:
  14. // 设置操作,将数据从用户空间拷贝到内核空间
  15.   {
  16.    struct  newchar  nc;
  17.    if(copy_from_user(&nc, (const char*)arg, sizeof(nc)) != 0)
  18.     return -EFAULT;
  19.    ret = do_set_newchar(&nc);
  20.   }
  21.   break;
  22. case KNEWCHAR_GET:
  23. // GET操作通常会在数据缓冲区中先传递部分初始值作为数据查找条件,获取全部
  24. // 数据后重新写回缓冲区
  25. // 当然也可以根据具体情况什么也不传入直接向内核获取数据
  26.   {
  27.    struct  newchar  nc;
  28.    if(copy_from_user(&nc, (const char*)arg, sizeof(nc)) != 0)
  29.     return -EFAULT;
  30.    ret = do_get_newchar(&nc);
  31.    if(ret == 0){
  32.     if(copy_to_user((unsigned char *)arg, &nc, sizeof(nc))!=0)
  33.      return -EFAULT;
  34.    }
  35.   }
  36.   break;
  37. }
  38. return ret;
  39. }
复制代码


  由于能力有限,难免会有错误和纰漏,还望给位海涵并指正, 当然也还存在其他的user kernel ipc, 也希望给位指出来。工作量比较大我的代码很多都只是提供了一种思路,要像深究研究的话,还必须DIY了!!!马上放寒假了,要回家过最后一个舒服的大年了,也要离开cu一段时间了,念念不舍啊...

参考文献:
   Linux 系统内核空间与用户空间通信的实现与分析:
    http://www.ibm.com/developerworks/cn/linux/l-netlink/index.html
   Linux Netfilter实现机制和扩展技术:
    [url]http://www.ibm.com/developerworks/cn/linux/l-ntflt/index.html[/url]

[ 本帖最后由 ubuntuer 于 2010-1-16 16:16 编辑 ]

论坛徽章:
0
3 [报告]
发表于 2010-01-16 23:30 |只看该作者
ubuntuer 的又一力作,不错不错,顶上去

论坛徽章:
0
4 [报告]
发表于 2010-01-16 23:52 |只看该作者
不错。支持

论坛徽章:
0
5 [报告]
发表于 2010-01-17 23:29 |只看该作者
分析的很细致,支持

论坛徽章:
0
6 [报告]
发表于 2010-01-18 15:12 |只看该作者
原帖由 mfan 于 2010-1-17 23:29 发表
分析的很细致,支持

谢谢兄弟你的支持,不过貌似没什么人气...   文笔还是不行

论坛徽章:
36
IT运维版块每日发帖之星
日期:2016-04-10 06:20:00IT运维版块每日发帖之星
日期:2016-04-16 06:20:0015-16赛季CBA联赛之广东
日期:2016-04-16 19:59:32IT运维版块每日发帖之星
日期:2016-04-18 06:20:00IT运维版块每日发帖之星
日期:2016-04-19 06:20:00每日论坛发贴之星
日期:2016-04-19 06:20:00IT运维版块每日发帖之星
日期:2016-04-25 06:20:00IT运维版块每日发帖之星
日期:2016-05-06 06:20:00IT运维版块每日发帖之星
日期:2016-05-08 06:20:00IT运维版块每日发帖之星
日期:2016-05-13 06:20:00IT运维版块每日发帖之星
日期:2016-05-28 06:20:00每日论坛发贴之星
日期:2016-05-28 06:20:00
7 [报告]
发表于 2010-01-18 15:41 |只看该作者
总结的不错。这两天比较忙,没来及仔细拜读。。:wink:

论坛徽章:
12
2015年辞旧岁徽章
日期:2015-03-03 16:54:1515-16赛季CBA联赛之同曦
日期:2017-03-17 19:13:162016科比退役纪念章
日期:2016-11-07 08:28:12luobin
日期:2016-06-17 17:46:36wusuopu
日期:2016-06-17 17:43:4515-16赛季CBA联赛之福建
日期:2016-01-14 12:49:22程序设计版块每日发帖之星
日期:2015-12-13 06:20:00程序设计版块每日发帖之星
日期:2015-06-08 22:20:00程序设计版块每日发帖之星
日期:2015-06-08 22:20:002015年亚洲杯之科威特
日期:2015-03-24 14:21:272015年迎新春徽章
日期:2015-03-04 09:57:092016科比退役纪念章
日期:2018-04-10 16:20:18
8 [报告]
发表于 2010-01-18 15:44 |只看该作者
lz高产,拜读神贴。

论坛徽章:
0
9 [报告]
发表于 2012-12-18 09:27 |只看该作者
多谢多谢,受益匪浅

论坛徽章:
0
10 [报告]
发表于 2012-12-18 16:38 来自手机 |只看该作者
好帖,我来顶
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP