转:内核空间与用户空间数据交换的方式之一 --ioctl(通过字符设备演示)

时间:2024-01-10 23:18:08

对于linux而言,内核程序和用户程序分别运行在内核空间和用户空间,要实现两者的数据交换,主要有以下几种方式:系统调用,读写系统文件(procfs,sysfs, seq_file,debugfs等), Netlink, 内核模块加载参数,内核启动参数,以及设备驱动实现的设备读、写、控制(ioctl)(这种方式可以归结到读写系统文件)。

设备驱动的实现过程中一般都会通过struct file_operations来实现对设备文件读、写以及控制命令。下面就仅通过ioctl的实现来说明通过字符设备,如何实现内核空间与用户空间的数据交换的。本例分为两个部分,内核代码为ict_k.c和ict_k.h,用户代码为ict_u.c

下面为内核部分代码:

1.ict_k.h

  1. #ifndef __ICT_K_H__
  2. #define __ICT_K_H__
  3. #define MAX_BUFFER_SIZE            64
  4. #define ICTDEV_MAJOR                  250
  5. #define ICT_IOCTL_MAGIC_NUM    'K'
  6. #define ICTIOC_GETDEV_INFO       _IOR(ICT_IOCTL_MAGIC_NUM, 0, int)
  7. #define ICTIOC_SETDEV_INFO       _IOWR(ICT_IOCTL_MAGIC_NUM, 1, int)
  8. #endif

2.ict_k.c

  1. /********************************************************************************
  2. *FileName          :ict_k.c
  3. *
  4. *Description       :This program is the kernel part which is used to illustrate the usage of ioctl.
  5. *
  6. *Author              :Michael Zhang <zhang_mq@sina.com>
  7. *
  8. *Version             :V0.1  2013-09-02
  9. *********************************************************************************/
  10. #include <linux/kernel.h>
  11. #include <linux/module.h>
  12. #include <linux/cdev.h>
  13. #include <linux/types.h>
  14. #include <asm/uaccess.h>
  15. #include <linux/fs.h>     /*struct file*/
  16. #include <linux/slab.h>  /*kfree*/
  17. #include "ict_k.h"
  18. struct ict_dev
  19. {
  20. struct cdev cdev;
  21. char buffer[MAX_BUFFER_SIZE];
  22. };
  23. static int mod_param = 0;
  24. static int ictdev_major = ICTDEV_MAJOR;
  25. static struct ict_dev *ict_devp;
  26. static int devinfo = 0;
  27. /*Device open function*/
  28. static int ictdev_open(struct inode *inode, struct file *filp)
  29. {
  30. printk(KERN_INFO"Ict device has been open.\n");
  31. return 0;
  32. }
  33. /*ioctl: device control function*/
  34. //static long ictdev_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
  35. static long ictdev_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg)
  36. {
  37. int ret = 0;
  38. int tmp;
  39. void __user *argp = (void __user *)arg;
  40. switch(cmd)
  41. {
  42. case ICTIOC_GETDEV_INFO:
  43. put_user(devinfo, (int *)arg);
  44. break;
  45. case ICTIOC_SETDEV_INFO:
  46. get_user(tmp, (int __user *)argp);
  47. devinfo = tmp;
  48. printk(KERN_INFO"Set devinfo as: %d\n", devinfo);
  49. break;
  50. default:
  51. break;
  52. }
  53. return ret;
  54. }
  55. static  int ictdev_release(struct inode *inode, struct file *filp)
  56. {
  57. printk(KERN_INFO"Ict device will be closed.\n");
  58. return 0;
  59. }
  60. struct file_operations ictdev_fops =
  61. {
  62. .owner = THIS_MODULE,
  63. .open = ictdev_open,
  64. .ioctl = ictdev_ioctl,
  65. //.unlock_ioct = ictdev_ioctl,
  66. .release = ictdev_release,
  67. };
  68. static void ict_dev_setup(dev_t devno, struct ict_dev *dev)
  69. {
  70. int ret;
  71. cdev_init(&dev->cdev, &ictdev_fops);
  72. dev->cdev.owner = THIS_MODULE;
  73. ret = cdev_add(&dev->cdev, devno, 1);
  74. if(ret)
  75. {
  76. printk(KERN_ERR"Add cdev fail.\n");
  77. }
  78. return;
  79. }
  80. /*Ict module intilization*/
  81. static int __init ict_ill_init(void)
  82. {
  83. int result;
  84. dev_t devno;
  85. devno = MKDEV(ictdev_major, 0);
  86. if(ictdev_major)
  87. {
  88. result = register_chrdev_region(devno, 1, "ictdev");
  89. }
  90. else
  91. {
  92. result = alloc_chrdev_region(&devno, 0, 1, "ictdev");
  93. ictdev_major = MAJOR(devno);
  94. }
  95. printk(KERN_INFO"ictdev_major is %d\n", ictdev_major);
  96. if(result < 0)
  97. {
  98. printk("Register/Allocate device number fail.\n");
  99. return result;
  100. }
  101. ict_devp = kmalloc(sizeof(struct ict_dev), GFP_KERNEL);
  102. if(!ict_devp)
  103. {
  104. printk("Memory allocation fail.\n");
  105. result = -ENOMEM;
  106. goto fail_malloc;
  107. }
  108. memset(ict_devp, 0, sizeof(struct ict_dev));
  109. ict_dev_setup(devno, ict_devp);
  110. return 0;
  111. fail_malloc:
  112. unregister_chrdev_region(devno, 1);
  113. return result;
  114. }
  115. static void __exit ict_ill_exit(void)
  116. {
  117. cdev_del(&ict_devp->cdev);
  118. kfree(ict_devp);
  119. unregister_chrdev_region(MKDEV(ictdev_major, 0), 1);
  120. }
  121. module_init(ict_ill_init);
  122. module_exit(ict_ill_exit);
  123. MODULE_LICENSE("GPL");
  124. MODULE_AUTHOR("Michael Zhang <zhang_mq@sina.com>");
  125. module_param(mod_param, int, S_IRUGO);
  126. MODULE_PARM_DESC(mod_param, "Initialization param of ioctl illustration program.");
  127. module_param(ictdev_major, int, S_IRUGO);
  128. MODULE_PARM_DESC(mod_param, "Initialization param of ioctl illustration program.");

3.内核部分代码通过下面的Makefile文件可编译成ict_u.ko

    1. ifneq ($(KERNELRELEASE),)
    2. obj-m := ict_k.o
    3. else
    4. KERNELDIR ?= /lib/modules/$(shell uname -r)/build
    5. PWD := $(shell pwd)
    6. default:
    7. $(MAKE) -C $(KERNELDIR) M=$(PWD) modules
    8. clean:
    9. rm -rf *.o *.mod.c *.ko
    10. endif
    11. 在编译出ict_k.ko后,将模块记载到内核,查看设备的主设备号。
    12. 在/dev目录下通过运行以下命令来生成设备节点: sudo mkdev ictdev c 250 0
    13. 对于内核代码中,struct file_operations实现了成员ioctl,但是对于大于2.6.36内核版本,其将会被unlocked_ioctl取代。
    14. 4.用户空间代码ict_u.c
    15. <pre name="code" class="cpp">/********************************************************************************
    16. *FileName          :ict_u.c
    17. *
    18. *Description       :This program is the user part which is used to illustrate the usage of ioctl.
    19. *
    20. *Author              :Michael Zhang <zhang_mq@sina.com>
    21. *
    22. *Version             :V0.1  2013-09-04
    23. *********************************************************************************/
    24. #include <stdio.h>
    25. #include <unistd.h>
    26. #include <string.h>
    27. #include <sys/types.h>
    28. #include <sys/ioctl.h>
    29. #include <sys/stat.h>
    30. #include <fcntl.h>
    31. /**********************Define Error Return Value**********************************/
    32. #define ICT_SUCCESS            0
    33. #define ICT_ERROR_DEV        1
    34. #define ICT_ERROR_PARAM   2
    35. #define ICT_ERROR_IOCTL     3
    36. /**************************IOCTL Releate Macro**********************************/
    37. #define ICT_IOCTL_MAGIC_NUM    'K'
    38. #define ICTIOC_GETDEV_INFO       _IOR(ICT_IOCTL_MAGIC_NUM, 0, int)
    39. #define ICTIOC_SETDEV_INFO       _IOWR(ICT_IOCTL_MAGIC_NUM, 1, int)
    40. #define ICT_DEV_FILE        "/dev/ictdev"
    41. static int ict_fd;
    42. static void usage()
    43. {
    44. printf("************************************************\n");
    45. printf("ictdev [get | set [0|1]]\n");
    46. printf("        -get    Get ict device info\n");
    47. printf("        -set    Set ict device info\n");
    48. printf("                -0    Set ict device info as 0\n");
    49. printf("                -1    Set ict device info as 0\n");
    50. printf("************************************************\n");
    51. }
    52. static int parse_param(char *param)
    53. {
    54. if(*param == '1')
    55. return 1;
    56. else if(*param == '0')
    57. return 0;
    58. else
    59. usage();
    60. return -1;
    61. }
    62. int main(int argc, char **argv)
    63. {
    64. int fd;
    65. int devinfo;
    66. if(argc < 2)
    67. {
    68. usage();
    69. return ICT_ERROR_PARAM;
    70. }
    71. /*Open device file*/
    72. ict_fd = open(ICT_DEV_FILE, O_RDWR);
    73. if(ict_fd < 0)
    74. {
    75. printf("Open ict device fail\n");
    76. return ICT_ERROR_DEV;
    77. }
    78. if(strcmp("get", argv[1]) == 0)
    79. {
    80. if(ioctl(ict_fd, ICTIOC_GETDEV_INFO, &devinfo) < 0)
    81. {
    82. printf("Get ICT device info fail.\n");
    83. return ICT_ERROR_IOCTL;
    84. }
    85. printf("ICT device info is: %d\n", devinfo);
    86. return ICT_SUCCESS;
    87. }
    88. else if(strcmp("set", argv[1]) == 0)
    89. {
    90. devinfo = parse_param(argv[2]);
    91. if(devinfo == -1)
    92. {
    93. return ICT_ERROR_PARAM;
    94. }
    95. if(ioctl(ict_fd, ICTIOC_SETDEV_INFO, &devinfo))
    96. {
    97. printf("Set ICT device info fail.\n");
    98. return ICT_ERROR_IOCTL;
    99. }
    100. return ICT_SUCCESS;
    101. }
    102. else
    103. {
    104. usage();
    105. return ICT_ERROR_PARAM;
    106. }
    107. }
    108. </pre><br>
    109. <br>
    110. 通过gcc将其便以为可执行文件,如: gcc ict_u.c -o ict_app<br>
    111. 通过运行 ./ict_app get 或./ict_app set [0/1] 就可观察内核与用户空间是如何实现数据交换的。<br>
    112. 在运行ict_app是会去打开设备/dev/ictdev,故可能会因读写权限问题出现打开失败的问题,则可先修改/dev/ictdev的读写权限。<br>
    113. <pre></pre>
    114. <p></p>
    115. <pre></pre>
    116. <p><br>
    117. </p>