• GPU in container


    红帽, OpenShift 4.5 - an insider's view for technology partners support GPU sharing

    阿里解决方案:   

    使用cGPU服务隔离GPU资源

    代码地址:

    wget http://cgpu.oss-cn-hangzhou.aliyuncs.com/cgpu-0.8.tar.gz
    tar -xvf cgpu-0.8.tar.gz
    cd cgpu
    ls
    # cgpu-container-wrapper  cgpu-km.c  cgpu.o  cgpu-procfs.c  install.sh  Makefile  os-interface.c  README  uninstall.sh  upgrade.sh  version.h

    github 

    git clone https://github.com/lvmxh/cgpu.git
    cd cgpu

    由于nvidia的驱动并不开源, 所以阿里应该进行不了相关源码级别的改造。

    我们可以看到,cgpu.o 调用了 filp_open, 猜测阿里的做法,应该是打开了用户态的nv显卡, 截获了对应nv显卡的所有的操作。

    但是我们可以导出cgpu的符号表可以大概了解一下,是啥东西。 

    objdump -t cgpu.o
      1 objdump -t cgpu.o
      2 
      3 cgpu.o:     file format elf64-x86-64
      4 
      5 SYMBOL TABLE:
      6 0000000000000000 l    d  .text  0000000000000000 .text
      7 0000000000000000 l    d  .text.unlikely 0000000000000000 .text.unlikely
      8 0000000000000000 l    d  .rodata        0000000000000000 .rodata
      9 0000000000000000 l    d  .rodata.str1.1 0000000000000000 .rodata.str1.1
     10 0000000000000000 l    d  .rodata.str1.8 0000000000000000 .rodata.str1.8
     11 0000000000000000 l    d  .data  0000000000000000 .data
     12 0000000000000000 l    d  .bss   0000000000000000 .bss
     13 0000000000000248 l       .rodata.str1.8 0000000000000000 .LC43
     14 0000000000000000 l     F .text.unlikely 00000000000000e7 mbedtls_mpi_shift_r
     15 0000000000000000 l    d  __mcount_loc   0000000000000000 __mcount_loc
     16 0000000000000000 l    d  .comment       0000000000000000 .comment
     17 0000000000000000 l    d  .note.GNU-stack        0000000000000000 .note.GNU-stack
     18 00000000000045c0 g     F .text  000000000000025c cgpu_finalize
     19 0000000000002920 g     F .text  0000000000000052 cgpu_clear_set_pfn
     20 0000000000000000         *UND*  0000000000000000 os_kfree
     21 0000000000000000         *UND*  0000000000000000 os_spin_lock
     22 0000000000000000         *UND*  0000000000000000 os_writel
     23 0000000000000000         *UND*  0000000000000000 os_vma_ops
     24 0000000000000000         *UND*  0000000000000000 os_kmalloc
     25 0000000000000000         *UND*  0000000000000000 os_wait_event_interruptible_timeout
     26 0000000000003af0 g     F .text  0000000000000105 cgpu_km_unlocked_ioctl
     27 0000000000000000         *UND*  0000000000000000 os_ioremap_nocache
     28 0000000000000000         *UND*  0000000000000000 os_iounmap
     29 0000000000003c00 g     F .text  00000000000000cf cgpu_km_compat_ioctl
     30 0000000000000000         *UND*  0000000000000000 os_in_vma_range
     31 0000000000000000         *UND*  0000000000000000 os_get_tgid
     32 0000000000000000         *UND*  0000000000000000 os_memcpy
     33 0000000000000000         *UND*  0000000000000000 os_unmap_range
     34 0000000000000000         *UND*  0000000000000000 os_get_page_shift
     35 0000000000004d20 g     F .text  0000000000000037 group_get_policy
     36 0000000000000000         *UND*  0000000000000000 os_get_fops
     37 0000000000000000         *UND*  0000000000000000 __fentry__
     38 0000000000004e00 g     F .text  0000000000000086 inst_get_meminfo
     39 0000000000000000         *UND*  0000000000000000 os_kmalloc_array
     40 0000000000000000         *UND*  0000000000000000 os_kthread_run
     41 0000000000000000         *UND*  0000000000000000 os_spin_unlock
     42 0000000000000000         *UND*  0000000000000000 os_memset
     43 0000000000000000         *UND*  0000000000000000 os_ioremap_cache
     44 0000000000000000         *UND*  0000000000000000 os_filp_open
     45 0000000000000000         *UND*  0000000000000000 __stack_chk_fail
     46 0000000000004e90 g     F .text  0000000000001858 cgpu_ioctl
     47 0000000000000000         *UND*  0000000000000000 os_kthread_stop
     48 0000000000000000         *UND*  0000000000000000 put_spin_lock
     49 0000000000000000         *UND*  0000000000000000 os_get_system_info
     50 0000000000000000         *UND*  0000000000000000 os_cdev_put
     51 0000000000004d60 g     F .text  0000000000000057 group_set_max_inst
     52 0000000000000000         *UND*  0000000000000000 os_put_waitqueue_head
     53 0000000000000000         *UND*  0000000000000000 os_virt_to_phys
     54 00000000000028e0 g     F .text  000000000000003c check_permit
     55 0000000000004840 g     F .text  000000000000001b inst_get_total_mem
     56 0000000000000000         *UND*  0000000000000000 os_spin_lock_init
     57 0000000000000000         *UND*  0000000000000000 os_pr_debug
     58 0000000000004b70 g     F .text  0000000000000126 inst_get_node
     59 0000000000003cd0 g     F .text  0000000000000047 cgpu_km_poll
     60 0000000000003840 g     F .text  000000000000007f inst_vma_fault
     61 0000000000000000         *UND*  0000000000000000 os_get_vm_file
     62 0000000000000000         *UND*  0000000000000000 os_vm_insert_pfn
     63 0000000000000000         *UND*  0000000000000000 os_get_zeroed_page
     64 0000000000000000         *UND*  0000000000000000 os_get_inode
     65 0000000000000000         *UND*  0000000000000000 os_copy_to_user
     66 0000000000000000         *UND*  0000000000000000 os_file_inode
     67 0000000000004cf0 g     F .text  000000000000002f group_set_policy
     68 0000000000000000         *UND*  0000000000000000 os_cdev_get
     69 0000000000000000         *UND*  0000000000000000 get_rdev
     70 0000000000000000         *UND*  0000000000000000 os_set_pgoff
     71 0000000000000000         *UND*  0000000000000000 cgpu_km_vma_fault
     72 0000000000000000         *UND*  0000000000000000 os_filp_close
     73 00000000000038c0 g     F .text  0000000000000225 cgpu_km_mmap
     74 0000000000000000         *UND*  0000000000000000 os_follow_pfn
     75 0000000000000000         *UND*  0000000000000000 os_free_page
     76 0000000000000000         *UND*  0000000000000000 os_copy_from_user
     77 0000000000004a20 g     F .text  0000000000000145 inst_set_weight
     78 0000000000004dc0 g     F .text  0000000000000037 group_get_max_inst
     79 00000000000049f0 g     F .text  0000000000000024 inst_get_weight
     80 0000000000004860 g     F .text  0000000000000148 inst_set_total_mem
     81 0000000000004380 g     F .text  000000000000023e cgpu_initialize
     82 0000000000004ca0 g     F .text  000000000000004b inst_set_name
     83 0000000000000000         *UND*  0000000000000000 os_file_op
     84 0000000000002840 g     F .text  0000000000000097 get_system_info
     85 0000000000002690 g     F .text  00000000000001a3 inst_get_bios
     86 00000000000049b0 g     F .text  0000000000000037 inst_get_free_weight
     87 00000000000040d0 g     F .text  00000000000002a2 cgpu_km_close
     88 0000000000000000         *UND*  0000000000000000 os_kthread_should_stop
     89 0000000000000000         *UND*  0000000000000000 os_ireadcount_inc
     90 0000000000000000         *UND*  0000000000000000 os_get_minor
     91 0000000000000000         *UND*  0000000000000000 os_printf
     92 0000000000004820 g     F .text  0000000000000019 inst_get_minor
     93 0000000000000000         *UND*  0000000000000000 os_set_filp
     94 0000000000000000         *UND*  0000000000000000 get_spin_lock
     95 0000000000000000         *UND*  0000000000000000 os_init_waitqueue_head
     96 0000000000003d20 g     F .text  00000000000003a7 cgpu_km_open
     97 0000000000000000         *UND*  0000000000000000 os_minor
     98 0000000000000000         *UND*  0000000000000000 os_alloc_file_operations
     99 0000000000000000         *UND*  0000000000000000 os_get_device_id
    100 0000000000002640 g     F .text  000000000000004d _strstr
    View Code

    此外阿里提供内核态参数设置的接口是procfs,并不是sysfs。

    打开cgpu-km.c可以看到新驱动提供的文件接口:

    grep -n8 file_operations cgpu-km.c
    31-extern int cgpu_km_close(struct inode *inode, struct file *filp);
    32-extern int cgpu_initialize(void);
    33-extern void cgpu_finalize(void);
    34-extern int cgpu_km_procfs_init(int);
    35-extern int cgpu_km_procfs_deinit(void);
    36-
    37-int cgpu_major = 0;
    38-
    39:static struct file_operations cgpu_km_fops = {
    40-     .owner     = THIS_MODULE,
    41-     .poll      = cgpu_km_poll,
    42-     .unlocked_ioctl = cgpu_km_unlocked_ioctl,
    43-     .compat_ioctl = cgpu_km_compat_ioctl,
    44-     .mmap      = cgpu_km_mmap,
    45-     .open      = cgpu_km_open,
    46-     .release   = cgpu_km_close,
    47-};

    我们打开 drm的官方文档,可以看到GPU的驱动实现了大量的ioctl 操作。

    通过反汇编,dump 阿里的cgpu.o 可以看到很多ioctl的跳转

    objdump -s -d cgpu.o > cgpu.o.txt
    grep cgpu_ioctl cgpu.o.txt

    结果如下:

      1  0080 5f696f63 746c0000 00000000 00000000  _ioctl..........
      2 0000000000003af0 <cgpu_km_unlocked_ioctl>:
      3     3af0:       e8 00 00 00 00          callq  3af5 <cgpu_km_unlocked_ioctl+0x5>
      4     3b31:       e8 00 00 00 00          callq  3b36 <cgpu_km_unlocked_ioctl+0x46>
      5     3b4d:       74 41                   je     3b90 <cgpu_km_unlocked_ioctl+0xa0>
      6     3b62:       e8 00 00 00 00          callq  3b67 <cgpu_km_unlocked_ioctl+0x77>
      7     3b77:       75 77                   jne    3bf0 <cgpu_km_unlocked_ioctl+0x100>
      8     3b97:       75 d1                   jne    3b6a <cgpu_km_unlocked_ioctl+0x7a>
      9     3ba4:       74 2a                   je     3bd0 <cgpu_km_unlocked_ioctl+0xe0>
     10     3bae:       e8 00 00 00 00          callq  3bb3 <cgpu_km_unlocked_ioctl+0xc3>
     11     3bb6:       74 18                   je     3bd0 <cgpu_km_unlocked_ioctl+0xe0>
     12     3bc8:       75 85                   jne    3b4f <cgpu_km_unlocked_ioctl+0x5f>
     13     3be3:       0f 85 66 ff ff ff       jne    3b4f <cgpu_km_unlocked_ioctl+0x5f>
     14     3beb:       e9 7a ff ff ff          jmpq   3b6a <cgpu_km_unlocked_ioctl+0x7a>
     15     3bf0:       e8 00 00 00 00          callq  3bf5 <cgpu_km_unlocked_ioctl+0x105>
     16 0000000000003c00 <cgpu_km_compat_ioctl>:
     17     3c00:       e8 00 00 00 00          callq  3c05 <cgpu_km_compat_ioctl+0x5>
     18     3c3f:       e8 00 00 00 00          callq  3c44 <cgpu_km_compat_ioctl+0x44>
     19     3c58:       75 54                   jne    3cae <cgpu_km_compat_ioctl+0xae>
     20     3c61:       74 1d                   je     3c80 <cgpu_km_compat_ioctl+0x80>
     21     3c70:       75 58                   jne    3cca <cgpu_km_compat_ioctl+0xca>
     22     3c8b:       74 2b                   je     3cb8 <cgpu_km_compat_ioctl+0xb8>
     23     3c95:       e8 00 00 00 00          callq  3c9a <cgpu_km_compat_ioctl+0x9a>
     24     3c9d:       74 19                   je     3cb8 <cgpu_km_compat_ioctl+0xb8>
     25     3cac:       74 0a                   je     3cb8 <cgpu_km_compat_ioctl+0xb8>
     26     3cb0:       eb b1                   jmp    3c63 <cgpu_km_compat_ioctl+0x63>
     27     3cc8:       eb 99                   jmp    3c63 <cgpu_km_compat_ioctl+0x63>
     28     3cca:       e8 00 00 00 00          callq  3ccf <cgpu_km_compat_ioctl+0xcf>
     29 0000000000004e90 <cgpu_ioctl>:
     30 ]
     31     4e90:       e8 00 00 00 00          callq  4e95 <cgpu_ioctl+0x5>
     32     4ec5:       74 49                   je     4f10 <cgpu_ioctl+0x80>
     33     4ecd:       74 41                   je     4f10 <cgpu_ioctl+0x80>
     34     4ed7:       74 37                   je     4f10 <cgpu_ioctl+0x80>
     35     4ee1:       77 2d                   ja     4f10 <cgpu_ioctl+0x80>
     36     4ee8:       e8 00 00 00 00          callq  4eed <cgpu_ioctl+0x5d>
     37     4ef3:       0f 84 c2 0e 00 00       je     5dbb <cgpu_ioctl+0xf2b>
     38     4f02:       0f 84 f8 00 00 00       je     5000 <cgpu_ioctl+0x170>
     39     4f0c:       75 3d                   jne    4f4b <cgpu_ioctl+0xbb>
     40     4f22:       0f 85 c8 0f 00 00       jne    5ef0 <cgpu_ioctl+0x1060>
     41     4f49:       74 c5                   je     4f10 <cgpu_ioctl+0x80>
     42     4f51:       75 ed                   jne    4f40 <cgpu_ioctl+0xb0>
     43     4f58:       75 b6                   jne    4f10 <cgpu_ioctl+0x80>
     44     4f7a:       0f 84 c0 06 00 00       je     5640 <cgpu_ioctl+0x7b0>
     45     4f8c:       0f 84 cb 06 00 00       je     565d <cgpu_ioctl+0x7cd>
     46     4f94:       74 da                   je     4f70 <cgpu_ioctl+0xe0>
     47     4f98:       74 56                   je     4ff0 <cgpu_ioctl+0x160>
     48     4fa5:       75 c9                   jne    4f70 <cgpu_ioctl+0xe0>
     49     4fbc:       0f 8e 68 06 00 00       jle    562a <cgpu_ioctl+0x79a>
     50     4fd5:       0f 85 9c 06 00 00       jne    5677 <cgpu_ioctl+0x7e7>
     51     4fe9:       eb 85                   jmp    4f70 <cgpu_ioctl+0xe0>
     52     4ff6:       eb a2                   jmp    4f9a <cgpu_ioctl+0x10a>
     53     5004:       75 19                   jne    501f <cgpu_ioctl+0x18f>
     54     5006:       e9 aa 08 00 00          jmpq   58b5 <cgpu_ioctl+0xa25>
     55     5019:       0f 84 79 06 00 00       je     5698 <cgpu_ioctl+0x808>
     56     5025:       75 e9                   jne    5010 <cgpu_ioctl+0x180>
     57     5032:       eb 13                   jmp    5047 <cgpu_ioctl+0x1b7>
     58     5042:       74 0c                   je     5050 <cgpu_ioctl+0x1c0>
     59     504e:       75 e8                   jne    5038 <cgpu_ioctl+0x1a8>
     60     505d:       76 13                   jbe    5072 <cgpu_ioctl+0x1e2>
     61     5062:       e8 00 00 00 00          callq  5067 <cgpu_ioctl+0x1d7>
     62     506d:       e9 a3 fe ff ff          jmpq   4f15 <cgpu_ioctl+0x85>
     63     5098:       e8 00 00 00 00          callq  509d <cgpu_ioctl+0x20d>
     64     50a2:       e8 00 00 00 00          callq  50a7 <cgpu_ioctl+0x217>
     65     50b1:       0f 84 e9 07 00 00       je     58a0 <cgpu_ioctl+0xa10>
     66     50ba:       0f 84 75 05 00 00       je     5635 <cgpu_ioctl+0x7a5>
     67     50c5:       eb 1a                   jmp    50e1 <cgpu_ioctl+0x251>
     68     50db:       0f 84 f1 07 00 00       je     58d2 <cgpu_ioctl+0xa42>
     69     50e5:       75 e9                   jne    50d0 <cgpu_ioctl+0x240>
     70     50f6:       e8 00 00 00 00          callq  50fb <cgpu_ioctl+0x26b>
     71     5100:       e8 00 00 00 00          callq  5105 <cgpu_ioctl+0x275>
     72     5119:       e8 00 00 00 00          callq  511e <cgpu_ioctl+0x28e>
     73     5128:       eb 13                   jmp    513d <cgpu_ioctl+0x2ad>
     74     513b:       74 09                   je     5146 <cgpu_ioctl+0x2b6>
     75     5144:       75 ea                   jne    5130 <cgpu_ioctl+0x2a0>
     76     516e:       c6 05 00 00 00 00 00    movb   $0x0,0x0(%rip)        # 5175 <cgpu_ioctl+0x2e5>
     77     5189:       eb 11                   jmp    519c <cgpu_ioctl+0x30c>
     78     5193:       0f 84 0f 05 00 00       je     56a8 <cgpu_ioctl+0x818>
     79     51c0:       75 ce                   jne    5190 <cgpu_ioctl+0x300>
     80     51c8:       0f 84 3a 06 00 00       je     5808 <cgpu_ioctl+0x978>
     81     51d1:       0f 84 31 06 00 00       je     5808 <cgpu_ioctl+0x978>
     82     51dc:       eb 13                   jmp    51f1 <cgpu_ioctl+0x361>
     83     51eb:       0f 84 d1 06 00 00       je     58c2 <cgpu_ioctl+0xa32>
     84     51f5:       75 e9                   jne    51e0 <cgpu_ioctl+0x350>
     85     5207:       e8 00 00 00 00          callq  520c <cgpu_ioctl+0x37c>
     86     5229:       e8 00 00 00 00          callq  522e <cgpu_ioctl+0x39e>
     87     523c:       e8 00 00 00 00          callq  5241 <cgpu_ioctl+0x3b1>
     88     5274:       74 4d                   je     52c3 <cgpu_ioctl+0x433>
     89     52c1:       75 54                   jne    5317 <cgpu_ioctl+0x487>
     90     52d7:       75 3e                   jne    5317 <cgpu_ioctl+0x487>
     91     5315:       75 c9                   jne    52e0 <cgpu_ioctl+0x450>
     92     5326:       74 3e                   je     5366 <cgpu_ioctl+0x4d6>
     93     5364:       75 51                   jne    53b7 <cgpu_ioctl+0x527>
     94     5372:       75 43                   jne    53b7 <cgpu_ioctl+0x527>
     95     53b5:       75 c9                   jne    5380 <cgpu_ioctl+0x4f0>
     96     53bf:       74 4d                   je     540e <cgpu_ioctl+0x57e>
     97     540c:       75 51                   jne    545f <cgpu_ioctl+0x5cf>
     98     5422:       75 3b                   jne    545f <cgpu_ioctl+0x5cf>
     99     542b:       0f b6 15 00 00 00 00    movzbl 0x0(%rip),%edx        # 5432 <cgpu_ioctl+0x5a2>
    100     5435:       0f b6 05 00 00 00 00    movzbl 0x0(%rip),%eax        # 543c <cgpu_ioctl+0x5ac>
    101     544a:       0f b6 05 00 00 00 00    movzbl 0x0(%rip),%eax        # 5451 <cgpu_ioctl+0x5c1>
    102     5499:       0f 84 1f 02 00 00       je     56be <cgpu_ioctl+0x82e>                                                                                                                                                                                                                                  [39/1693]
    103     54ea:       0f 87 99 01 00 00       ja     5689 <cgpu_ioctl+0x7f9>
    104     550c:       75 7e                   jne    558c <cgpu_ioctl+0x6fc>
    105     5524:       75 66                   jne    558c <cgpu_ioctl+0x6fc>
    106     5529:       74 35                   je     5560 <cgpu_ioctl+0x6d0>
    107     555e:       75 d0                   jne    5530 <cgpu_ioctl+0x6a0>
    108     5586:       0f 88 50 03 00 00       js     58dc <cgpu_ioctl+0xa4c>
    109     55ce:       0f 84 fd 00 00 00       je     56d1 <cgpu_ioctl+0x841>
    110     55e0:       0f 84 36 02 00 00       je     581c <cgpu_ioctl+0x98c>
    111     55e9:       0f 84 d6 07 00 00       je     5dc5 <cgpu_ioctl+0xf35>
    112     55f5:       eb 18                   jmp    560f <cgpu_ioctl+0x77f>
    113     5609:       0f 84 03 02 00 00       je     5812 <cgpu_ioctl+0x982>
    114     5615:       75 e9                   jne    5600 <cgpu_ioctl+0x770>
    115     5620:       e8 00 00 00 00          callq  5625 <cgpu_ioctl+0x795>
    116     5625:       e9 35 fa ff ff          jmpq   505f <cgpu_ioctl+0x1cf>
    117     5630:       e9 3b f9 ff ff          jmpq   4f70 <cgpu_ioctl+0xe0>
    118     5637:       e9 ab fa ff ff          jmpq   50e7 <cgpu_ioctl+0x257>
    119     5646:       75 17                   jne    565f <cgpu_ioctl+0x7cf>
    120     5653:       e8 00 00 00 00          callq  5658 <cgpu_ioctl+0x7c8>
    121     5658:       e9 d1 fb ff ff          jmpq   522e <cgpu_ioctl+0x39e>
    122     5668:       e8 00 00 00 00          callq  566d <cgpu_ioctl+0x7dd>
    123     5672:       e9 9e f8 ff ff          jmpq   4f15 <cgpu_ioctl+0x85>
    124     5684:       e9 e7 f8 ff ff          jmpq   4f70 <cgpu_ioctl+0xe0>
    125     5693:       e9 f4 fe ff ff          jmpq   558c <cgpu_ioctl+0x6fc>
    126     56a3:       e9 85 f9 ff ff          jmpq   502d <cgpu_ioctl+0x19d>
    127     56ab:       74 08                   je     56b5 <cgpu_ioctl+0x825>
    128     56b9:       e9 07 fb ff ff          jmpq   51c5 <cgpu_ioctl+0x335>
    129     56cc:       e9 3d fe ff ff          jmpq   550e <cgpu_ioctl+0x67e>
    130     577d:       75 91                   jne    5710 <cgpu_ioctl+0x880>
    131     57fe:       e9 5c f8 ff ff          jmpq   505f <cgpu_ioctl+0x1cf>
    132     580d:       e9 e8 f9 ff ff          jmpq   51fa <cgpu_ioctl+0x36a>
    133     5817:       e9 01 fe ff ff          jmpq   561d <cgpu_ioctl+0x78d>
    134     5822:       0f 85 be fd ff ff       jne    55e6 <cgpu_ioctl+0x756>
    135     5833:       0f 85 ad fd ff ff       jne    55e6 <cgpu_ioctl+0x756>
    136     583f:       0f 85 a1 fd ff ff       jne    55e6 <cgpu_ioctl+0x756>
    137     584b:       0f 85 95 fd ff ff       jne    55e6 <cgpu_ioctl+0x756>
    138     5853:       0f 85 8d fd ff ff       jne    55e6 <cgpu_ioctl+0x756>
    139     585f:       0f 85 81 fd ff ff       jne    55e6 <cgpu_ioctl+0x756>
    140     5872:       75 07                   jne    587b <cgpu_ioctl+0x9eb>
    141     588f:       0f 87 9c 09 00 00       ja     6231 <cgpu_ioctl+0x13a1>
    142     589c:       eb df                   jmp    587d <cgpu_ioctl+0x9ed>
    143     58b0:       e9 41 f8 ff ff          jmpq   50f6 <cgpu_ioctl+0x266>
    144     58bd:       e9 6b f7 ff ff          jmpq   502d <cgpu_ioctl+0x19d>
    145     58cd:       e9 28 f9 ff ff          jmpq   51fa <cgpu_ioctl+0x36a>
    146     58d7:       e9 0b f8 ff ff          jmpq   50e7 <cgpu_ioctl+0x257>
    147     58f4:       0f 8e 92 fc ff ff       jle    558c <cgpu_ioctl+0x6fc>
    148     5904:       0f 84 82 fc ff ff       je     558c <cgpu_ioctl+0x6fc>
    149     591c:       0f 88 6a fc ff ff       js     558c <cgpu_ioctl+0x6fc>
    150     592c:       e8 00 00 00 00          callq  5931 <cgpu_ioctl+0xaa1>
    151     593b:       0f 84 36 08 00 00       je     6177 <cgpu_ioctl+0x12e7>
    152     5977:       75 ea                   jne    5963 <cgpu_ioctl+0xad3>
    153     59ee:       e8 00 00 00 00          callq  59f3 <cgpu_ioctl+0xb63>
    154     5a10:       0f 87 ef 03 00 00       ja     5e05 <cgpu_ioctl+0xf75>
    155     5a1c:       0f 87 13 04 00 00       ja     5e35 <cgpu_ioctl+0xfa5>
    156     5a26:       0f 87 39 04 00 00       ja     5e65 <cgpu_ioctl+0xfd5>
    157     5aab:       0f 87 1e 03 00 00       ja     5dcf <cgpu_ioctl+0xf3f>
    158     5ab8:       0f 87 d7 03 00 00       ja     5e95 <cgpu_ioctl+0x1005>
    159     5adf:       0f 87 94 05 00 00       ja     6079 <cgpu_ioctl+0x11e9>
    160     5af0:       0f 87 d9 02 00 00       ja     5dcf <cgpu_ioctl+0xf3f>
    161     5b04:       0f 87 4f 05 00 00       ja     6059 <cgpu_ioctl+0x11c9>
    162     5b1c:       0f 84 dd 04 00 00       je     5fff <cgpu_ioctl+0x116f>
    163     5b46:       0f 84 1a 04 00 00       je     5f66 <cgpu_ioctl+0x10d6>
    164     5b69:       e8 00 00 00 00          callq  5b6e <cgpu_ioctl+0xcde>
    165     5b98:       0f 88 9b 03 00 00       js     5f39 <cgpu_ioctl+0x10a9>
    166     5bc7:       75 7e                   jne    5c47 <cgpu_ioctl+0xdb7>
    167     5c41:       0f 84 5b 04 00 00       je     60a2 <cgpu_ioctl+0x1212>
    168     5c64:       73 4c                   jae    5cb2 <cgpu_ioctl+0xe22>
    169     5c9d:       72 eb                   jb     5c8a <cgpu_ioctl+0xdfa>
    170     5cda:       0f 84 39 02 00 00       je     5f19 <cgpu_ioctl+0x1089>
    171     5ce7:       e8 00 00 00 00          callq  5cec <cgpu_ioctl+0xe5c>
    172     5cf4:       0f 85 92 f8 ff ff       jne    558c <cgpu_ioctl+0x6fc>
    173     5d13:       0f 87 dc 01 00 00       ja     5ef5 <cgpu_ioctl+0x1065>
    174     5d19:       73 51                   jae    5d6c <cgpu_ioctl+0xedc>
    175     5d3a:       0f 85 ea 01 00 00       jne    5f2a <cgpu_ioctl+0x109a>
    176     5d43:       eb 1e                   jmp    5d63 <cgpu_ioctl+0xed3>
    177     5d5d:       0f 85 c7 01 00 00       jne    5f2a <cgpu_ioctl+0x109a>
    178     5d6a:       77 d9                   ja     5d45 <cgpu_ioctl+0xeb5>
    179     5d7f:       0f 84 07 f8 ff ff       je     558c <cgpu_ioctl+0x6fc>
    180     5db4:       75 cf                   jne    5d85 <cgpu_ioctl+0xef5>
    181     5db6:       e9 d1 f7 ff ff          jmpq   558c <cgpu_ioctl+0x6fc>
    182     5dc0:       e9 50 f1 ff ff          jmpq   4f15 <cgpu_ioctl+0x85>
    183     5dca:       e9 4e f8 ff ff          jmpq   561d <cgpu_ioctl+0x78d>
    184     5e00:       e9 42 fe ff ff          jmpq   5c47 <cgpu_ioctl+0xdb7>
    185     5e30:       e9 64 fc ff ff          jmpq   5a99 <cgpu_ioctl+0xc09>
    186     5e60:       e9 34 fc ff ff          jmpq   5a99 <cgpu_ioctl+0xc09>
    187     5e90:       e9 04 fc ff ff          jmpq   5a99 <cgpu_ioctl+0xc09>
    188     5eb4:       0f 84 04 fc ff ff       je     5abe <cgpu_ioctl+0xc2e>
    189     5ee1:       e9 61 fd ff ff          jmpq   5c47 <cgpu_ioctl+0xdb7>
    190     5ef0:       e8 00 00 00 00          callq  5ef5 <cgpu_ioctl+0x1065>
    191     5f08:       e8 00 00 00 00          callq  5f0d <cgpu_ioctl+0x107d>
    192     5f14:       e9 56 fe ff ff          jmpq   5d6f <cgpu_ioctl+0xedf>
    193     5f25:       e9 b6 fd ff ff          jmpq   5ce0 <cgpu_ioctl+0xe50>
    194     5f34:       e9 53 f6 ff ff          jmpq   558c <cgpu_ioctl+0x6fc>
    195     5f5b:       0f 85 e6 fc ff ff       jne    5c47 <cgpu_ioctl+0xdb7>
    196     5f61:       e9 63 fc ff ff          jmpq   5bc9 <cgpu_ioctl+0xd39>
    197     5f94:       0f 85 ad fc ff ff       jne    5c47 <cgpu_ioctl+0xdb7>
    198     5fb5:       0f 85 8c fc ff ff       jne    5c47 <cgpu_ioctl+0xdb7>
    199     5fd9:       0f 85 68 fc ff ff       jne    5c47 <cgpu_ioctl+0xdb7>
    200     5ff5:       e8 00 00 00 00          callq  5ffa <cgpu_ioctl+0x116a>
    201     5ffa:       e9 6f fb ff ff          jmpq   5b6e <cgpu_ioctl+0xcde>
    202     6036:       0f 85 0b fc ff ff       jne    5c47 <cgpu_ioctl+0xdb7>
    203     6054:       e9 e5 fa ff ff          jmpq   5b3e <cgpu_ioctl+0xcae>
    204     606e:       0f 85 4d fe ff ff       jne    5ec1 <cgpu_ioctl+0x1031>
    205     6074:       e9 91 fa ff ff          jmpq   5b0a <cgpu_ioctl+0xc7a>
    206     6097:       0f 85 1d fe ff ff       jne    5eba <cgpu_ioctl+0x102a>
    207     609d:       e9 43 fa ff ff          jmpq   5ae5 <cgpu_ioctl+0xc55>
    208     6108:       0f 84 50 01 00 00       je     625e <cgpu_ioctl+0x13ce>
    209     6149:       0f 87 a7 fc ff ff       ja     5df6 <cgpu_ioctl+0xf66>
    210     6153:       0f 87 b8 00 00 00       ja     6211 <cgpu_ioctl+0x1381>
    211     616a:       74 1a                   je     6186 <cgpu_ioctl+0x12f6>
    212     6172:       e9 d0 fa ff ff          jmpq   5c47 <cgpu_ioctl+0xdb7>
    213     6181:       e9 06 f4 ff ff          jmpq   558c <cgpu_ioctl+0x6fc>
    214     61b7:       0f 83 3a 02 00 00       jae    63f7 <cgpu_ioctl+0x1567>
    215     620f:       eb 9f                   jmp    61b0 <cgpu_ioctl+0x1320>
    216     6226:       0f 85 40 ff ff ff       jne    616c <cgpu_ioctl+0x12dc>
    217     622c:       e9 28 ff ff ff          jmpq   6159 <cgpu_ioctl+0x12c9>
    218     6259:       e9 88 f3 ff ff          jmpq   55e6 <cgpu_ioctl+0x756>
    219     62c7:       75 21                   jne    62ea <cgpu_ioctl+0x145a>
    220     62d1:       0f 84 ed 01 00 00       je     64c4 <cgpu_ioctl+0x1634>
    221     6317:       0f 84 a4 03 00 00       je     66c1 <cgpu_ioctl+0x1831>
    222     6320:       0f 84 54 03 00 00       je     667a <cgpu_ioctl+0x17ea>
    223     6349:       0f 85 70 ff ff ff       jne    62bf <cgpu_ioctl+0x142f>
    224     639e:       77 bd                   ja     635d <cgpu_ioctl+0x14cd>
    225     63f2:       e9 c8 fe ff ff          jmpq   62bf <cgpu_ioctl+0x142f>
    226     6437:       0f 83 21 fe ff ff       jae    625e <cgpu_ioctl+0x13ce>
    227     645b:       0f 87 95 f9 ff ff       ja     5df6 <cgpu_ioctl+0xf66>
    228     6465:       0f 87 5d 02 00 00       ja     66c8 <cgpu_ioctl+0x1838>
    229     6478:       0f 85 ee fc ff ff       jne    616c <cgpu_ioctl+0x12dc>
    230     64bf:       e9 65 ff ff ff          jmpq   6429 <cgpu_ioctl+0x1599>
    231     6516:       0f 84 9f 00 00 00       je     65bb <cgpu_ioctl+0x172b>
    232     6572:       74 3a                   je     65ae <cgpu_ioctl+0x171e>
    233     65b6:       e9 54 ff ff ff          jmpq   650f <cgpu_ioctl+0x167f>
    234     662f:       0f 84 12 f6 ff ff       je     5c47 <cgpu_ioctl+0xdb7>
    235     663d:       0f 84 04 f6 ff ff       je     5c47 <cgpu_ioctl+0xdb7>
    236     664d:       0f 84 f4 f5 ff ff       je     5c47 <cgpu_ioctl+0xdb7>
    237     6675:       e9 cd f5 ff ff          jmpq   5c47 <cgpu_ioctl+0xdb7>
    238     667e:       0f 85 a2 fc ff ff       jne    6326 <cgpu_ioctl+0x1496>
    239     66bc:       e9 fe fb ff ff          jmpq   62bf <cgpu_ioctl+0x142f>
    240     66c3:       e9 f7 fb ff ff          jmpq   62bf <cgpu_ioctl+0x142f>
    241     66dd:       0f 84 88 fd ff ff       je     646b <cgpu_ioctl+0x15db>
    242     66e3:       e9 84 fa ff ff          jmpq   616c <cgpu_ioctl+0x12dc>
    View Code

    此外从"os-interface.c"的源码看:

    void *os_init_waitqueue_head(void)
    {
        wait_queue_head_t *sched_wq = kmalloc(sizeof(wait_queue_head_t), GFP_KERNEL);
    
        if(sched_wq)
            init_waitqueue_head(sched_wq);
        return (void*)sched_wq;
    }
    
    void os_put_waitqueue_head(void *sched_wq)
    {
        if(sched_wq)
            kfree(sched_wq);
    }
    
    bool os_kthread_should_stop(void)
    {
      return kthread_should_stop();
    }
    
    void *os_kthread_run(CGPU_THREAD_FN sched_thread_fn, void *data, const char *name)
    {
    
      return (void *)kthread_run((CGPU_THREAD_FN)sched_thread_fn, data, name);
    }
    
    void os_kthread_stop(void *sch)
    {
       struct task_struct *sched_ts = (struct task_struct *)sch;
       if(sched_ts)
           kthread_stop(sched_ts);
    }

    通过内核线程和queue实现的cGPU调度算法。

     ----------------------------------------------------------------------------------

    Step by Step:  

    github:

    https://github.com/torvalds/linux/

    doc:

    linux-kernel-labs device_drivers  

    sphinx-samples/writing_usb_driver   

    Tutorial on Linux Device Driver Programming Embedded Systems 

    1. scaffold/skeleton   

    首先创建一个基本的Device driver, 网上教程或者Example一堆。

    Writing device drivers in Linux: A brief tutorial   (PDF version)

    mkdir GPUDriver  && cd GPUDriver
    
    cat <<EOF | tee nothing.c
    #include <linux/module.h>
    
    MODULE_LICENSE("Dual BSD/GPL");
    EOF
    
    cat <<EOF | tee Makefile
    obj-m := nothing.o
    EOF
    
    RELEASE=`uname -r |cut  -d "-" -f 1`    
    # sudo apt-get update && sudo apt-get install -y linux-source-$RELEASE
    sudo apt-get update && sudo apt-get install -y linux-source
    
    # like DKMS install driver  https://github.com/IntelRealSense/librealsense/issues/4586#
    # https://askubuntu.com/questions/554624/how-to-resolve-the-lib-modules-3-13-0-27-generic-build-no-such-file-or-direct
    make -C  /usr/src/linux-headers-$(uname -r) M=`pwd` modules 
    sudo insmod nothing.ko
    
    lsmod |grep nothing 
    # sudo modprobe -r nothing 
    sudo rmmod nothing
    lsmod |grep nothing 
    rm *

    2. scaffold/skeleton- print hello info

     1 rm *
     2 
     3 MODULE=hello
     4 cat <<EOF | tee ${MODULE}.c
     5 #include <linux/init.h>
     6 #include <linux/module.h>
     7 #include <linux/kernel.h>
     8 
     9 MODULE_LICENSE("Dual BSD/GPL");
    10 
    11 static int ${MODULE}_init(void) {
    12   printk("<1> Hello world!
    ");
    13   return 0;
    14 }
    15 
    16 static void ${MODULE}_exit(void) {
    17   printk("<1> Bye, cruel world
    ");
    18 }
    19 
    20 module_init(${MODULE}_init);
    21 module_exit(${MODULE}_exit);
    22 EOF
    23 
    24 cat <<EOF | tee Makefile
    25 obj-m := ${MODULE}.o
    26 EOF
    27 
    28 make -C /usr/src/linux-headers-$(uname -r) M=`pwd` modules 
    29 sudo insmod ${MODULE}.ko
    30 lsmod |grep ${MODULE}
    31 sudo rmmod ${MODULE}
    32 lsmod |grep ${MODULE}
    33 
    34 dmesg |grep world 
    35 cat /var/log/syslog |grep world 
    View Code

    3. scaffold/skeleton- warm up:memory example

    device driver

      1 rm *
      2 MODULE=memory
      3 
      4 # https://stephane.lesimple.fr/blog/kernel-2-6-18-linuxconfig-h-problem/
      5 # https://lzw.me/a/linux-config-no-such-file-or-directory.html
      6 # https://bugzilla.redhat.com/show_bug.cgi?id=212463
      7 # https://blog.csdn.net/qq_35277038/article/details/80498210
      8 # https://www.programmersought.com/article/92814489864/
      9 
     10 cat <<EOF | tee ${MODULE}.c
     11 /* Necessary includes for device drivers */
     12 #include <linux/init.h>
     13 #include <linux/config.h>
     14 #include <linux/module.h>
     15 #include <linux/kernel.h> /* printk() */
     16 #include <linux/slab.h> /* kmalloc() */
     17 #include <linux/fs.h> /* everything... */
     18 #include <linux/errno.h> /* error codes */
     19 #include <linux/types.h> /* size_t */
     20 #include <linux/proc_fs.h>
     21 #include <linux/fcntl.h> /* O_ACCMODE */
     22 #include <asm/switch_to.h> /* cli(), *_flags */
     23 #include <asm/uaccess.h> /* copy_from/to_user */
     24 #include <linux/uaccess.h>
     25 
     26 MODULE_LICENSE("Dual BSD/GPL");
     27 
     28 /* Declaration of ${MODULE}.c functions */
     29 int ${MODULE}_open(struct inode *inode, struct file *filp);
     30 int ${MODULE}_release(struct inode *inode, struct file *filp);
     31 ssize_t ${MODULE}_read(struct file *filp, char *buf, size_t count, loff_t *f_pos);
     32 ssize_t ${MODULE}_write(struct file *filp, const char *buf, size_t count, loff_t *f_pos);
     33 void ${MODULE}_exit(void);
     34 int ${MODULE}_init(void);
     35 
     36 /* Structure that declares the usual file */
     37 /* access functions */
     38 
     39 struct file_operations ${MODULE}_fops = {
     40   read: ${MODULE}_read,
     41   write: ${MODULE}_write,
     42   open: ${MODULE}_open,
     43   release: ${MODULE}_release
     44 };
     45 
     46 /* Declaration of the init and exit functions */
     47 module_init(${MODULE}_init);
     48 module_exit(${MODULE}_exit);
     49 
     50 /* Global variables of the driver */
     51 /* Major number */
     52 int ${MODULE}_major = 60;
     53 /* Buffer to store data */
     54 char *${MODULE}_buffer;
     55 
     56 int ${MODULE}_init(void) {
     57   int result;
     58 
     59   /* Registering device */
     60   result = register_chrdev(${MODULE}_major, "${MODULE}", &${MODULE}_fops);
     61   if (result < 0) {
     62     printk(
     63       "<1>${MODULE}: cannot obtain major number %d
    ", ${MODULE}_major);
     64     return result;
     65   }
     66 
     67   /* Allocating ${MODULE} for the buffer */
     68   ${MODULE}_buffer = kmalloc(1, GFP_KERNEL);
     69   if (!${MODULE}_buffer) {
     70     result = -ENOMEM;
     71     goto fail;
     72   }
     73   memset(${MODULE}_buffer, 0, 1);
     74 
     75   printk("<1>Inserting ${MODULE} module
    ");
     76   return 0;
     77 
     78   fail:
     79     ${MODULE}_exit();
     80     return result;
     81 }
     82 
     83 
     84 void ${MODULE}_exit(void) {
     85   /* Freeing the major number */
     86   unregister_chrdev(${MODULE}_major, "${MODULE}");
     87 
     88   /* Freeing buffer ${MODULE} */
     89   if (${MODULE}_buffer) {
     90     kfree(${MODULE}_buffer);
     91   }
     92 
     93   printk("<1>Removing ${MODULE} module
    ");
     94 }
     95 
     96 int ${MODULE}_open(struct inode *inode, struct file *filp) {
     97   /* Success */
     98   return 0;
     99 }
    100 
    101 int ${MODULE}_release(struct inode *inode, struct file *filp) {
    102   /* Success */
    103   return 0;
    104 }
    105 
    106 ssize_t ${MODULE}_read(struct file *filp, char *buf,
    107                     size_t count, loff_t *f_pos) {
    108   /* Transfering data to user space */
    109   copy_to_user(buf,${MODULE}_buffer,1);
    110 
    111   /* Changing reading position as best suits */
    112   if (*f_pos == 0) {
    113     *f_pos+=1;
    114     return 1;
    115   } else {
    116     return 0;
    117   }
    118 }
    119 
    120 ssize_t ${MODULE}_write( struct file *filp, const char *buf,
    121                       size_t count, loff_t *f_pos) {
    122   const char *tmp;
    123 
    124   tmp=buf+count-1;
    125   copy_from_user(${MODULE}_buffer,tmp,1);
    126   return 1;
    127 }
    128 EOF
    129 
    130 cat <<EOF | tee Makefile
    131 obj-m := ${MODULE}.o
    132 EOF
    133 
    134 INPATH=/usr/src/linux-headers-$(uname -r)/include
    135 cat <<EOF | sudo tee ${INPATH}/linux/config.h
    136 /* WorkAround for linux/config.h */
    137 #ifndef _LINUX_CONFIG_H
    138 #define _LINUX_CONFIG_H
    139 /* This file is no longer in use and kept only for backward compatibility.
    140  * autoconf.h is now included via -imacros on the commandline
    141  * https://www.linuxquestions.org/questions/linux-kernel-70/removal-of-include-linux-config-h-file-in-2-6-19-kernel-506363
    142  */
    143 #include <generated/autoconf.h>
    144 #endif
    145 EOF
    146 
    147 # /usr/src/linux-headers-$(uname -r)/include/linux/config.h
    148 make -C /usr/src/linux-headers-$(uname -r) M=`pwd` modules 
    149 sudo insmod ${MODULE}.ko
    150 lsmod |grep ${MODULE}
    151 cat /proc/modules |grep ${MODULE}
    152 sudo mknod /dev/memory c 60 0
    153 sudo chmod 666 /dev/memory
    154 echo -n abcdef | sudo tee /dev/memory
    155 cat /dev/memory 
    156 
    157 sudo rmmod ${MODULE}
    158 lsmod |grep ${MODULE}
    View Code

    check: 

    # c code:
    # static char *name = "world";        ///< An example LKM argument -- default value is "world"
    # module_param(name, charp, S_IRUGO); ///< Param desc. charp = char ptr, S_IRUGO can be read/not changed
    # MODULE_PARM_DESC(name, "The name to display in /var/log/kern.log");  ///< parameter description
    
    # sudo insmod hello.ko name=Derek
    # cat  /sys/module/memory/name 
    
    cat /proc/devices |grep memory
    ls /sys/module/memory/ 

    user app

     1 cat <<EOF | tee test.c
     2 #include <stdio.h>
     3 #include <unistd.h>
     4 
     5 int main() {
     6   unsigned char byte,dummy;
     7   FILE * DEV;
     8 
     9   /* Opening the device ${MODULE}*/
    10   DEV=fopen("/dev/${MODULE}","w");
    11 
    12   /* We remove the buffer from the file i/o */
    13   /* setvbuf(DEV,&dummy,_IONBF,1); */
    14   /* Initializing the variable to one */
    15   byte=1;
    16 
    17   /* We make an infinite loop */
    18   while (1) {
    19     /* Writing to the parallel port */
    20     /* to turn on a LED */
    21     printf("Write value is %d
    ",byte);
    22     fwrite(&byte,1,1,DEV);
    23     sleep(1);
    24     fread(&dummy,1,1,DEV);
    25     printf("Read value is %d
    ",dummy);
    26     /* Updating the byte value */
    27     byte<<=1;
    28     if (byte >= 10)
    29         break;
    30   }
    31   fclose(DEV);
    32 }
    33 EOF
    34 
    35 gcc -o test test.c
    36 test
    View Code

    3. scaffold/skeleton- warm up:procfs example

    MODULE=driver
    cat <<EOF | tee ${MODULE}.c
    /***************************************************************************//**
    *  file       driver.c
    *
    *  details    Simple Linux device driver (procfs)
    *
    *  author     EmbeTronicX
    *
    * *******************************************************************************/
    #include <linux/kernel.h>
    #include <linux/init.h>
    #include <linux/module.h>
    #include <linux/kdev_t.h>
    #include <linux/fs.h>
    #include <linux/cdev.h>
    #include <linux/device.h>
    #include<linux/slab.h>                 //kmalloc()
    #include<linux/uaccess.h>              //copy_to/from_user()
    #include <linux/ioctl.h>
    #include<linux/proc_fs.h>
     
    #define WR_VALUE _IOW('a','a',int32_t*)
    #define RD_VALUE _IOR('a','b',int32_t*)
     
    int32_t value = 0;
    char etx_array[20]="try_proc_array
    ";
    static int len = 1;
     
     
    dev_t dev = 0;
    static struct class *dev_class;
    static struct cdev etx_cdev;
     
    /*
    ** Function Prototypes
    */
    static int      __init etx_driver_init(void);
    static void     __exit etx_driver_exit(void);
     
    /*************** Driver Functions **********************/
    static int      etx_open(struct inode *inode, struct file *file);
    static int      etx_release(struct inode *inode, struct file *file);
    static ssize_t  etx_read(struct file *filp, char __user *buf, size_t len,loff_t * off);
    static ssize_t  etx_write(struct file *filp, const char *buf, size_t len, loff_t * off);
    static long     etx_ioctl(struct file *file, unsigned int cmd, unsigned long arg);
     
    /***************** Procfs Functions *******************/
    static int      open_proc(struct inode *inode, struct file *file);
    static int      release_proc(struct inode *inode, struct file *file);
    static ssize_t  read_proc(struct file *filp, char __user *buffer, size_t length,loff_t * offset);
    static ssize_t  write_proc(struct file *filp, const char *buff, size_t len, loff_t * off);
     
    /*
    ** File operation sturcture
    */
    static struct file_operations fops =
    {
            .owner          = THIS_MODULE,
            .read           = etx_read,
            .write          = etx_write,
            .open           = etx_open,
            .unlocked_ioctl = etx_ioctl,
            .release        = etx_release,
    };
     
    /*
    ** procfs operation sturcture
    */
    static struct file_operations proc_fops = {
            .open = open_proc,
            .read = read_proc,
            .write = write_proc,
            .release = release_proc
    };
     
    /*
    ** This fuction will be called when we open the procfs file
    */
    static int open_proc(struct inode *inode, struct file *file)
    {
        printk(KERN_INFO "proc file opend.....	");
        return 0;
    }
     
    /*
    ** This fuction will be called when we close the procfs file
    */
    static int release_proc(struct inode *inode, struct file *file)
    {
        printk(KERN_INFO "proc file released.....
    ");
        return 0;
    }
     
    /*
    ** This fuction will be called when we read the procfs file
    */
    static ssize_t read_proc(struct file *filp, char __user *buffer, size_t length,loff_t * offset)
    {
        printk(KERN_INFO "proc file read.....
    ");
        if(len)
            len=0;
        else{
            len=1;
            return 0;
        }
        copy_to_user(buffer,etx_array,20);
     
        return length;;
    }
     
    /*
    ** This fuction will be called when we write the procfs file
    */
    static ssize_t write_proc(struct file *filp, const char *buff, size_t len, loff_t * off)
    {
        printk(KERN_INFO "proc file wrote.....
    ");
        copy_from_user(etx_array,buff,len);
        return len;
    }
     
    /*
    ** This fuction will be called when we open the Device file
    */
    static int etx_open(struct inode *inode, struct file *file)
    {
            printk(KERN_INFO "Device File Opened...!!!
    ");
            return 0;
    }
     
    /*
    ** This fuction will be called when we close the Device file
    */
    static int etx_release(struct inode *inode, struct file *file)
    {
            printk(KERN_INFO "Device File Closed...!!!
    ");
            return 0;
    }
     
    /*
    ** This fuction will be called when we read the Device file
    */
    static ssize_t etx_read(struct file *filp, char __user *buf, size_t len, loff_t *off)
    {
            printk(KERN_INFO "Readfunction
    ");
            return 0;
    }
     
    /*
    ** This fuction will be called when we write the Device file
    */
    static ssize_t etx_write(struct file *filp, const char __user *buf, size_t len, loff_t *off)
    {
            printk(KERN_INFO "Write Function
    ");
            return 0;
    }
     
    /*
    ** This fuction will be called when we write IOCTL on the Device file
    */
    static long etx_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
    {
             switch(cmd) {
                    case WR_VALUE:
                            copy_from_user(&value ,(int32_t*) arg, sizeof(value));
                            printk(KERN_INFO "Value = %d
    ", value);
                            break;
                    case RD_VALUE:
                            copy_to_user((int32_t*) arg, &value, sizeof(value));
                            break;
            }
            return 0;
    }
     
    /*
    ** Module Init function
    */
    static int __init etx_driver_init(void)
    {
            /*Allocating Major number*/
            if((alloc_chrdev_region(&dev, 0, 1, "etx_Dev")) <0){
                    printk(KERN_INFO "Cannot allocate major number
    ");
                    return -1;
            }
            printk(KERN_INFO "Major = %d Minor = %d 
    ",MAJOR(dev), MINOR(dev));
     
            /*Creating cdev structure*/
            cdev_init(&etx_cdev,&fops);
     
            /*Adding character device to the system*/
            if((cdev_add(&etx_cdev,dev,1)) < 0){
                printk(KERN_INFO "Cannot add the device to the system
    ");
                goto r_class;
            }
     
            /*Creating struct class*/
            if((dev_class = class_create(THIS_MODULE,"etx_class")) == NULL){
                printk(KERN_INFO "Cannot create the struct class
    ");
                goto r_class;
            }
     
            /*Creating device*/
            if((device_create(dev_class,NULL,dev,NULL,"etx_device")) == NULL){
                printk(KERN_INFO "Cannot create the Device 1
    ");
                goto r_device;
            }
     
            /*Creating Proc entry*/
            proc_create("etx_proc",0666,NULL,&proc_fops);
     
            printk(KERN_INFO "Device Driver Insert...Done!!!
    ");
            return 0;
     
    r_device:
            class_destroy(dev_class);
    r_class:
            unregister_chrdev_region(dev,1);
            return -1;
    }
     
    /*
    ** Module exit function
    */
    static void __exit etx_driver_exit(void)
    {
            remove_proc_entry("etx_proc",NULL);
            device_destroy(dev_class,dev);
            class_destroy(dev_class);
            cdev_del(&etx_cdev);
            unregister_chrdev_region(dev, 1);
            printk(KERN_INFO "Device Driver Remove...Done!!!
    ");
    }
     
    module_init(etx_driver_init);
    module_exit(etx_driver_exit);
     
    MODULE_LICENSE("GPL");
    MODULE_AUTHOR("EmbeTronicX <embetronicx@gmail.com>");
    MODULE_DESCRIPTION("Simple Linux device driver (procfs)");
    MODULE_VERSION("1.6");
    EOF
    
    
    cat <<EOF |  sed -e "s/\	/	/" | tee Makefile
    obj-m += ${MODULE}.o
     
    KDIR = /lib/modules/$(shell uname -r)/build
     
    all:
    	make -C $(KDIR) M=$(shell pwd) modules
     
    clean:
    	make -C $(KDIR) M=$(shell pwd) clean
    EOF
    
    
    sudo insmod ${MODULE}.ko
    lsmod |grep ${MODULE}
    dmesg |tail
    ls /proc/etx_proc
    cat /proc/etx_proc
    echo "device driver proc" > /proc/etx_proc
    cat /proc/etx_proc
    sudo rmmod ${MODULE}
    lsmod |grep ${MODULE}

    3. scaffold/skeleton- warm up:open user space dev example

    for open user space file,  call filp_open, there are several example in kernel source。 details:oreilly linux device drivers  open and release

    for GPU ioctl, we can call vfs_ioctl (linux/fs.h),  kernel source code ioctl.c/vfs_ioctl, overlayfs also call vfs_ioctl. (more friendly to read https://code.woboq.org/linux/linux/fs/ioctl.c.html

    see more latencytop.c: 70 59433 4897 i915_irq_wait drm_ioctl vfs_ioctl do_vfs_ioctl sys_ioctl

    we can also call unlocked_ioctl and compat_ioctl dirctly

    A.我们先创建一个新的device设备

    这个设备支持ioctl,通过以下脚本创建这个设备,该脚本会加载一个驱动,并生成一个/dev/etx_device

    MODULE=driver
    tee ${MODULE}.c >/dev/null <<EOF
    /***************************************************************************//**
    *  file       driver.c
    *
    *  details    Simple Linux device driver (Real Linux Device Driver)
    *
    *  author     EmbeTronicX
    *
    * *******************************************************************************/
    #include <linux/kernel.h>
    #include <linux/init.h>
    #include <linux/module.h>
    #include <linux/kdev_t.h>
    #include <linux/fs.h>
    #include <linux/cdev.h>
    #include <linux/device.h>
    #include<linux/slab.h>                 //kmalloc()
    #include<linux/uaccess.h>              //copy_to/from_user()
     
    #define WR_VALUE _IOW('a','a',int32_t*)
    #define RD_VALUE _IOR('a','b',int32_t*) 
    #define mem_size        1024           //Memory Size
     
    int32_t value = 0;
    dev_t dev = 0;
    static struct class *dev_class;
    static struct cdev etx_cdev;
    uint8_t *kernel_buffer;
     
    /*
    ** Function Prototypes
    */
    static int      __init etx_driver_init(void);
    static void     __exit etx_driver_exit(void);
    static int      etx_open(struct inode *inode, struct file *file);
    static int      etx_release(struct inode *inode, struct file *file);
    static ssize_t  etx_read(struct file *filp, char __user *buf, size_t len,loff_t * off);
    static ssize_t  etx_write(struct file *filp, const char *buf, size_t len, loff_t * off);
    static long     etx_ioctl(struct file *file, unsigned int cmd, unsigned long arg);
     
     
    /*
    ** File Operations structure
    */
    static struct file_operations fops =
    {
            .owner          = THIS_MODULE,
            .read           = etx_read,
            .write          = etx_write,
            .open           = etx_open,
            .unlocked_ioctl = etx_ioctl,
            .release        = etx_release,
    };
     
    /*
    ** This fuction will be called when we open the Device file
    */
    static int etx_open(struct inode *inode, struct file *file)
    {
            /*Creating Physical memory*/
            if((kernel_buffer = kmalloc(mem_size , GFP_KERNEL)) == 0){
                printk(KERN_INFO "Cannot allocate memory in kernel
    ");
                return -1;
            }
            printk(KERN_INFO "Device File Opened...!!!
    ");
            return 0;
    }
     
    /*
    ** This fuction will be called when we close the Device file
    */
    static int etx_release(struct inode *inode, struct file *file)
    {
            kfree(kernel_buffer);
            printk(KERN_INFO "Device File Closed...!!!
    ");
            return 0;
    }
     
    /*
    ** This fuction will be called when we read the Device file
    */
    static ssize_t etx_read(struct file *filp, char __user *buf, size_t len, loff_t *off)
    {
            //Copy the data from the kernel space to the user-space
            copy_to_user(buf, kernel_buffer, mem_size);
            printk(KERN_INFO "Data Read : Done!
    ");
            return mem_size;
    }
     
    /*
    ** This fuction will be called when we write the Device file
    */
    static ssize_t etx_write(struct file *filp, const char __user *buf, size_t len, loff_t *off)
    {
            //Copy the data to kernel space from the user-space
            copy_from_user(kernel_buffer, buf, len);
            printk(KERN_INFO "Data Write : Done!
    ");
            return len;
    }
    
    /*
    ** This fuction will be called when we write IOCTL on the Device file
    */
    static long etx_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
    {
             switch(cmd) {
                    case WR_VALUE:
                            copy_from_user(&value ,(int32_t*) arg, sizeof(value));
                            sprintf(kernel_buffer, "%s %d, %d, %d, %ld
    ","This is a ioctl input. PID, TGID, cmd & value:", current->pid, current->tgid, cmd, arg);
    
                            printk(KERN_INFO "Value = %d
    ", value);
                            break;
                    case RD_VALUE:
                            copy_to_user((int32_t*) arg, &value, sizeof(value));
                            break;
            }
            return 0;
    }
    
    /*
    ** Module Init function
    */
    static int __init etx_driver_init(void)
    {
            /*Allocating Major number*/
            if((alloc_chrdev_region(&dev, 0, 1, "etx_Dev")) <0){
                    printk(KERN_INFO "Cannot allocate major number
    ");
                    return -1;
            }
            printk(KERN_INFO "Major = %d Minor = %d 
    ",MAJOR(dev), MINOR(dev));
     
            /*Creating cdev structure*/
            cdev_init(&etx_cdev,&fops);
     
            /*Adding character device to the system*/
            if((cdev_add(&etx_cdev,dev,1)) < 0){
                printk(KERN_INFO "Cannot add the device to the system
    ");
                goto r_class;
            }
     
            /*Creating struct class*/
            if((dev_class = class_create(THIS_MODULE,"etx_class")) == NULL){
                printk(KERN_INFO "Cannot create the struct class
    ");
                goto r_class;
            }
     
            /*Creating device*/
            if((device_create(dev_class,NULL,dev,NULL,"etx_device")) == NULL){
                printk(KERN_INFO "Cannot create the Device 1
    ");
                goto r_device;
            }
            printk(KERN_INFO "Device Driver Insert...Done!!!
    ");
            return 0;
     
    r_device:
            class_destroy(dev_class);
    r_class:
            unregister_chrdev_region(dev,1);
            return -1;
    }
     
    /*
    ** Module exit function
    */
    static void __exit etx_driver_exit(void)
    {
            device_destroy(dev_class,dev);
            class_destroy(dev_class);
            cdev_del(&etx_cdev);
            unregister_chrdev_region(dev, 1);
            printk(KERN_INFO "Device Driver Remove...Done!!!
    ");
    }
     
    module_init(etx_driver_init);
    module_exit(etx_driver_exit);
     
    MODULE_LICENSE("GPL");
    MODULE_AUTHOR("EmbeTronicX <embetronicx@gmail.com>");
    MODULE_DESCRIPTION("Simple Linux device driver (Real Linux Device Driver)");
    MODULE_VERSION("1.4");
    EOF
    
    make clean
    make
    sudo insmod ${MODULE}.ko
    lsmod | grep ${MODULE}
    lsof | grep ${MODULE}
    # sudo rm /dev/etx_device
    # sudo rmmod ${MODULE}.ko
    
    
    APP=test_app
    tee ${APP}.c >/dev/null <<EOF
    /***************************************************************************//**
    *  file       test_app.c
    *
    *  details    Userspace application to test the Device driver
    *
    *  author     EmbeTronicX
    *
    * *******************************************************************************/
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <sys/types.h>
    #include <sys/stat.h>
    #include <fcntl.h>
    #include <unistd.h>
    #include<sys/ioctl.h>
     
    int8_t write_buf[1024];
    int8_t read_buf[1024];
    #define WR_VALUE _IOW('a','a',int32_t*)
    #define RD_VALUE _IOR('a','b',int32_t*) 
    
    int main()
    {
            int fd;
        int32_t value, number;
            char option;
            printf("*********************************
    ");
            printf("*******WWW.EmbeTronicX.com*******
    ");
     
            fd = open("/dev/etx_device", O_RDWR);
            if(fd < 0) {
                    printf("Cannot open device file...
    ");
                    return 0;
            }
     
            while(1) {
                    printf("****Please Enter the Option******
    ");
                    printf("        1. Write               
    ");
                    printf("        2. Read                 
    ");
                    printf("        3. IOCTL Write          
    ");
                    printf("        4. IOCTL Read           
    ");
                    printf("        5. Exit                 
    ");
                    printf("*********************************
    ");
                    scanf(" %c", &option);
                    printf("Current PID=%d, Your Option = %c
    ", getpid(), option);
                    
                    switch(option) {
                            case '1':
                                    printf("Enter the string to write into driver :");
                                    scanf("  %[^	
    ]s", write_buf);
                                    printf("Data Writing ...");
                                    write(fd, write_buf, strlen(write_buf)+1);
                                    printf("Done!
    ");
                                    break;
                            case '2':
                                    printf("Data Reading ...");
                                    read(fd, read_buf, 1024);
                                    printf("Done!
    
    ");
                                    printf("Data = %s
    
    ", read_buf);
                                    break;
                            case '3':
                                    printf("Enter the ioctl Value to send
    ");
                                    scanf("%d",&number);
                                    printf("Writing Value to Driver
    ");
                                    ioctl(fd, WR_VALUE, (int32_t*) &number);
                                    printf("Done!
    ");
                                    break;
                            case '4':
                                    printf("Reading ioctl Value from Driver
    ");
                                    ioctl(fd, RD_VALUE, (int32_t*) &value);
                                    printf("Value is %d
    ", value);
                                    read(fd, read_buf, 1024);
                                    printf("Done!
    
    ");
                                    printf("Data = %s
    
    ", read_buf);
                                    break;
                            case '5':
                                    close(fd);
                                    exit(1);
                                    break;
                            default:
                                    printf("Enter Valid option = %c
    ",option);
                                    break;
                    }
            }
            close(fd);
    }
    EOF
    
    gcc -o ${APP} ${APP}.c
    sudo ./${APP}
    View Code

    通过运行`sudo ./${APP}` 我肯可以先往这个设备中写一个字符串,然后读一下。

    A.我们先创建一个新的device设备

    这个设备用来打开/dev/etx_device,并截获(Intercepte)ioctl操作。

    通过以下脚本创建这个设别,该脚本会加载一个驱动,并生成一个 /dev/top_intercepte设备

    mkdir intercepte && cd intercepte
    MODULE=intercepte
    
    cat <<EOF |  sed -e "s/\	/	/" | tee Makefile
    obj-m += ${MODULE}.o
     
    KDIR = /lib/modules/$(shell uname -r)/build
     
    all:
    	make -C $(KDIR) M=$(shell pwd) modules
     
    clean:
    	make -C $(KDIR) M=$(shell pwd) clean
    EOF
    
    SUBDEV=etx_device
    tee ${MODULE}.c >/dev/null <<EOF
    /***************************************************************************//**
    *  file       ${MODULE}.c
    *
    *  details    Simple Linux device driver (Real Linux Device Driver)
    *
    *  author     EmbeTronicX
    *
    * *******************************************************************************/
    #include <linux/kernel.h>
    #include <linux/init.h>
    #include <linux/module.h>
    #include <linux/kdev_t.h>
    #include <linux/fs.h>
    #include <linux/cdev.h>
    #include <linux/device.h>
    #include<linux/slab.h>                 //kmalloc()
    #include<linux/uaccess.h>              //copy_to/from_user()
     
    #define WR_VALUE _IOW('a','a',int32_t*)
    #define RD_VALUE _IOR('a','b',int32_t*) 
    #define mem_size        1024           //Memory Size
     
    int32_t value = 0;
    dev_t dev = 0;
    static struct class *dev_class;
    static struct cdev etx_cdev;
    uint8_t *kernel_buffer;
    struct file *file=NULL;
     
    /*
    ** Function Prototypes
    */
    static int      __init etx_driver_init(void);
    static void     __exit etx_driver_exit(void);
    static int      etx_open(struct inode *inode, struct file *file);
    static int      etx_release(struct inode *inode, struct file *file);
    static ssize_t  etx_read(struct file *filp, char __user *buf, size_t len,loff_t * off);
    static ssize_t  etx_write(struct file *filp, const char *buf, size_t len, loff_t * off);
    static long     etx_ioctl(struct file *file, unsigned int cmd, unsigned long arg);
     
     
    /*
    ** File Operations structure
    */
    static struct file_operations fops =
    {
            .owner          = THIS_MODULE,
            .read           = etx_read,
            .write          = etx_write,
            .open           = etx_open,
            .unlocked_ioctl = etx_ioctl,
            .release        = etx_release,
    };
     
    /*
    ** This fuction will be called when we open the Device file
    */
    static int etx_open(struct inode *inode, struct file *file)
    {
            /*Creating Physical memory*/
            if((kernel_buffer = kmalloc(mem_size , GFP_KERNEL)) == 0){
                printk(KERN_INFO "Cannot allocate memory in kernel
    ");
                return -1;
            }
            printk(KERN_INFO "Device File Opened...!!!
    ");
            return 0;
    }
     
    /*
    ** This fuction will be called when we close the Device file
    */
    static int etx_release(struct inode *inode, struct file *file)
    {
            kfree(kernel_buffer);
            printk(KERN_INFO "Device File Closed...!!!
    ");
            return 0;
    }
     
    /*
    ** This fuction will be called when we read the Device file
    */
    static ssize_t etx_read(struct file *filp, char __user *buf, size_t len, loff_t *off)
    {
            //Copy the data from the kernel space to the user-space
            copy_to_user(buf, kernel_buffer, mem_size);
            printk(KERN_INFO "Data Read : Done!
    ");
            return mem_size;
    }
     
    /*
    ** This fuction will be called when we write the Device file
    */
    static ssize_t etx_write(struct file *filp, const char __user *buf, size_t len, loff_t *off)
    {
            //Copy the data to kernel space from the user-space
            copy_from_user(kernel_buffer, buf, len);
            printk(KERN_INFO "Data Write : Done!
    ");
            return len;
    }
    
    /*
    ** This fuction will be called when we write IOCTL on the Device file
    */
    static long etx_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
    {
            long rc = 0;
            switch(cmd) {
                    case WR_VALUE:
                            copy_from_user(&value ,(int32_t*) arg, sizeof(value));
                            sprintf(kernel_buffer, "%s %d, %d, %d, %ld
    ","This is a ioctl input. PID, TGID, cmd & value:", current->pid, current->tgid, cmd, arg);
                            rc=file->f_op->unlocked_ioctl(file,cmd,arg); 
                            printk(KERN_INFO "Value = %d
    ", value);
                            break;
                    case RD_VALUE:
                            copy_to_user((int32_t*) arg, &value, sizeof(value));
                            break;
            }
            return rc;
    }
    
    /*
    ** Module Init function
    */
    static int __init etx_driver_init(void)
    {
            /*Allocating Major number*/
            if((alloc_chrdev_region(&dev, 0, 1, "etx_TopDev")) <0){
                    printk(KERN_INFO "Cannot allocate major number
    ");
                    return -1;
            }
            printk(KERN_INFO "Major = %d Minor = %d 
    ",MAJOR(dev), MINOR(dev));
     
            /*Creating cdev structure*/
            cdev_init(&etx_cdev,&fops);
     
            /*Adding character device to the system*/
            if((cdev_add(&etx_cdev,dev,1)) < 0){
                printk(KERN_INFO "Cannot add the device to the system
    ");
                goto r_class;
            }
     
            /*Creating struct class*/
            if((dev_class = class_create(THIS_MODULE,"topetx_class")) == NULL){
                printk(KERN_INFO "Cannot create the struct class
    ");
                goto r_class;
            }
     
            /*Creating device*/
            if((device_create(dev_class,NULL,dev,NULL,"top_${MODULE}")) == NULL){
                printk(KERN_INFO "Cannot create the Device 1
    ");
                goto r_device;
            }
            /*Creating device*/
            file=filp_open("/dev/${SUBDEV}",O_RDWR,0);
            if(IS_ERR(file)) {
                    printk(KERN_INFO "Cannot Open the Device /dev/${SUBDEV}
    ");
                    goto fail0;
            }
            printk(KERN_INFO "Device Driver Insert...Done!!!
    ");
            return 0; 
    fail0:
            filp_close(file,NULL);
            printk("load failed
    ");
    r_device:
            class_destroy(dev_class);
    r_class:
            unregister_chrdev_region(dev,1);
            return -1;
    }
     
    /*
    ** Module exit function
    */
    static void __exit etx_driver_exit(void)
    {
            filp_close(file,NULL);
            device_destroy(dev_class,dev);
            class_destroy(dev_class);
            cdev_del(&etx_cdev);
            unregister_chrdev_region(dev, 1);
            printk(KERN_INFO "Device Driver Remove...Done!!!
    ");
    }
     
    module_init(etx_driver_init);
    module_exit(etx_driver_exit);
     
    MODULE_LICENSE("GPL");
    MODULE_AUTHOR("EmbeTronicX <embetronicx@gmail.com>");
    MODULE_DESCRIPTION("Simple Linux device driver (Real Linux Device Driver)");
    MODULE_VERSION("1.4");
    EOF
    
    make clean
    make
    sudo insmod ${MODULE}.ko
    lsmod | grep ${MODULE}
    lsof | grep ${MODULE}
    # sudo rm /dev/etx_device
    # sudo rmmod ${MODULE}.ko
    
    
    APP=toptest_app
    tee ${TOPAPP}.c >/dev/null <<EOF
    /***************************************************************************//**
    *  file       ${TOPAPP}.c
    *
    *  details    Userspace application to test the Device driver
    *
    *  author     EmbeTronicX
    *
    * *******************************************************************************/
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <sys/types.h>
    #include <sys/stat.h>
    #include <fcntl.h>
    #include <unistd.h>
    #include<sys/ioctl.h>
     
    int8_t write_buf[1024];
    int8_t read_buf[1024];
    #define WR_VALUE _IOW('a','a',int32_t*)
    #define RD_VALUE _IOR('a','b',int32_t*) 
    
    int main()
    {
            int fd;
        int32_t value, number;
            char option;
            printf("*********************************
    ");
            printf("*******WWW.EmbeTronicX.com*******
    ");
     
            fd = open("/dev/top_${MODULE}", O_RDWR);
            if(fd < 0) {
                    printf("Cannot open device file...
    ");
                    return 0;
            }
     
            while(1) {
                    printf("****Please Enter the Option******
    ");
                    printf("        1. Write                
    ");
                    printf("        2. Read                 
    ");
                    printf("        3. IOCTL Write          
    ");
                    printf("        4. IOCTL Read           
    ");
                    printf("        5. Exit                 
    ");
                    printf("*********************************
    ");
                    scanf(" %c", &option);
                    printf("Current PID=%d, Your Option = %c
    ", getpid(), option);
                    
                    switch(option) {
                            case '1':
                                    printf("Enter the string to write into driver :");
                                    scanf("  %[^	
    ]s", write_buf);
                                    printf("Data Writing ...");
                                    write(fd, write_buf, strlen(write_buf)+1);
                                    printf("Done!
    ");
                                    break;
                            case '2':
                                    printf("Data Reading ...");
                                    read(fd, read_buf, 1024);
                                    printf("Done!
    
    ");
                                    printf("Data = %s
    
    ", read_buf);
                                    break;
                            case '3':
                                    printf("Enter the ioctl Value to send
    ");
                                    scanf("%d",&number);
                                    printf("Writing Value to Driver
    ");
                                    ioctl(fd, WR_VALUE, (int32_t*) &number);
                                    printf("Done!
    ");
                                    break;
                            case '4':
                                    printf("Reading ioctl Value from Driver
    ");
                                    ioctl(fd, RD_VALUE, (int32_t*) &value);
                                    printf("Value is %d
    ", value);
                                    read(fd, read_buf, 1024);
                                    printf("Done!
    
    ");
                                    printf("Data = %s
    
    ", read_buf);
                                    break;
                            case '5':
                                    close(fd);
                                    exit(1);
                                    break;
                            default:
                                    printf("Enter Valid option = %c
    ",option);
                                    break;
                    }
            }
            close(fd);
    }
    EOF
    
    gcc -o ${TOPAPP} ${TOPAPP}.c
    sudo ./${TOPAPP}

    通过运行`sudo ./${TOPAPP}` 我肯可以先往这个设备中执行命令3进行一个ioctl写的命令,然后通过`sudo ./${APP}`的命令3进行一个ioctl读的命令读一下, 看看输出结果有没有发生变化。

    REF:   

    scaffold

    Linux Device Drivers, 3rd Edition by Jonathan Corbet, Alessandro Rubini, Greg Kroah-Ha (oreilly)

    BuildYourOwnKernel

    kernel thread

    kthreads/kthreads.pdf  

    Kernel threads  (introduce mechanism

    SysPlay's BlogKernel Threads Continued print  all tasks info(include process id), his many blog, CATEGORIES cover many domains: 

     1 cat <<EOF | tee tasks.c
     2 #include <linux/kernel.h>
     3 #include <linux/module.h>
     4 #include <linux/sched/signal.h>
     5 
     6 char buffer[256];
     7 char * get_task_state(long state)
     8 {
     9     switch (state) {
    10         case TASK_RUNNING:
    11             return "TASK_RUNNING";
    12         case TASK_INTERRUPTIBLE:
    13             return "TASK_INTERRUPTIBLE";
    14         case TASK_UNINTERRUPTIBLE:
    15             return "TASK_UNINTERRUPTIBLE";
    16         case __TASK_STOPPED:
    17             return "__TASK_STOPPED";
    18         case __TASK_TRACED:
    19             return "__TASK_TRACED";
    20         default:
    21         {
    22             sprintf(buffer, "Unknown Type:%ld
    ", state);
    23             return buffer;
    24         }
    25     }
    26 }
    27 
    28 static int test_tasks_init(void)
    29 {
    30     struct task_struct *task_list;
    31     unsigned int process_count = 0;
    32     pr_info("%s: In init
    ", __func__);
    33     for_each_process(task_list) {
    34         pr_info("Process: %s	 PID:[%d]	 State:%s
    ", 
    35                 task_list->comm, task_list->pid,
    36                 get_task_state(task_list->state));
    37         process_count++;    
    38     }
    39     pr_info("Number of processes:%u
    ", process_count);
    40     return 0;
    41 }
    42 
    43 static void test_tasks_exit(void)
    44 {
    45     pr_info("%s: In exit
    ", __func__);
    46 }
    47 
    48 MODULE_LICENSE("GPL");
    49 module_init(test_tasks_init);
    50 module_exit(test_tasks_exit);
    51 EOF
    52 
    53 MODULE=tasks
    54 cat <<EOF | tee Makefile
    55 obj-m := ${MODULE}.o
    56 EOF
    57 
    58 make -C /usr/src/linux-headers-$(uname -r) M=`pwd` modules 
    59 sudo insmod ${MODULE}.ko
    60 lsmod |grep ${MODULE}
    61 sudo rmmod ${MODULE}
    62 lsmod |grep ${MODULE}
    63 dmesg |tail
    View Code

    linux-kernel Creation of kernel threads(Learning linux-kernel eBook (PDF)),

    Chapters

    it show inserting the .ko

     1 MODULE=kern_thread
     2 cat <<EOF | tee ${MODULE}.c
     3 #include <linux/module.h>
     4 #include <linux/kernel.h>
     5 #include <linux/init.h>
     6 #include <linux/kthread.h>
     7 #include <linux/sched.h>
     8 
     9 #define AUTHOR        "Nachiket Kulkarni"
    10 #define DESCRIPTION    "Simple module that demonstrates creation of 2 kernel threads"
    11 
    12 static int kthread_func(void *arg)
    13 {
    14 /* Every kthread has a struct task_struct associated with it which is it's identifier.
    15 * Whenever a thread is schedule for execution, the kernel sets "current" pointer to 
    16 * it's struct task_struct.
    17 * current->comm is the name of the command that caused creation of this thread
    18 * current->pid is the process of currently executing thread 
    19 */
    20     printk(KERN_INFO "I am thread: %s[PID = %d] [TGID = %d]
    ", current->comm, current->pid, current->tgid);
    21     return 0;
    22 }
    23 
    24 static int __init init_func(void)
    25 {
    26     struct task_struct *ts1;
    27     struct task_struct *ts2;
    28     int err;
    29 
    30     printk(KERN_INFO "Starting 2 threads
    ");
    31 
    32 /*struct task_struct *kthread_create(int (*threadfn)(void *data), void *data, 
    33  *                         const char *namefmt, ...);
    34  * This function creates a kernel thread and starts the thread.
    35  */
    36     ts1 = kthread_run(kthread_func, NULL, "thread-1");
    37     if (IS_ERR(ts1)) {
    38         printk(KERN_INFO "ERROR: Cannot create thread ts1
    ");
    39         err = PTR_ERR(ts1);
    40         ts1 = NULL;
    41         return err;
    42     }
    43 
    44     ts1 = kthread_run(kthread_func, NULL, "thread-1");
    45     if (IS_ERR(ts1)) {
    46         printk(KERN_INFO "ERROR: Cannot create thread ts1
    ");
    47         err = PTR_ERR(ts1);
    48         ts1 = NULL;
    49         return err;
    50     }
    51 
    52     printk(KERN_INFO "I am thread: %s[PID = %d] [TGID = %d]
    ", current->comm, current->pid, current->tgid);
    53     return 0;
    54 }
    55 
    56 static void __exit exit_func(void)
    57 {
    58     printk(KERN_INFO "Exiting the module
    ");
    59 }
    60 
    61 module_init(init_func);
    62 module_exit(exit_func);
    63 
    64 MODULE_AUTHOR(AUTHOR);
    65 // MODULE_DESCRIPTION(MODULE_AUTHOR); /* not pass */
    66 MODULE_LICENSE("GPL");
    67 EOF
    68 
    69 # ls /lib/modules/4.15.0-101-generic/build/include/linux/
    70 cat <<EOF |  sed -e "s/\	/	/" | tee Makefile
    71 obj-m += ${MODULE}.o
    72 
    73 all:
    74 	make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules
    75 clean:
    76 	make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean
    77 EOF
    78 
    79 make -C /usr/src/linux-headers-$(uname -r) M=`pwd` modules 
    80 sudo insmod ${MODULE}.ko
    81 lsmod |grep ${MODULE}
    82 sudo rmmod ${MODULE}
    83 lsmod |grep ${MODULE}
    84 dmesg |tail
    View Code

    and print 

    [1373814.205950] Starting 2 threads
    [1373814.206296] I am thread: thread-1[PID = 15204] [TGID = 15204]
    [1373814.206695] I am thread: insmod[PID = 15203] [TGID = 15203]
    [1373814.206722] I am thread: thread-1[PID = 15205] [TGID = 15205]

    Kernel threads (man ) made easy

    Kernel thread to print the running tasks on the CPU every 500ms  

    MODULE=kthread
    cat <<EOF | tee ${MODULE}.c
    #include <linux/kernel.h>
    #include <linux/module.h>
    #include <linux/sched/signal.h>
    #include <linux/kthread.h>
    #include <linux/delay.h>
    
    struct task_struct *print_thread;
    char buffer[256];
    char * get_task_state(long state)
    {
        switch (state) {
            case TASK_RUNNING:
                return "TASK_RUNNING";
            case TASK_INTERRUPTIBLE:
                return "TASK_INTERRUPTIBLE";
            case TASK_UNINTERRUPTIBLE:
                return "TASK_UNINTERRUPTIBLE";
            case __TASK_STOPPED:
                return "__TASK_STOPPED";
            case __TASK_TRACED:
                return "__TASK_TRACED";
            case TASK_IDLE:
                return "(TASK_UNINTERRUPTIBLE | TASK_NOLOAD)";
            case TASK_KILLABLE:
                return "(TASK_WAKEKILL | TASK_UNINTERRUPTIBLE)";
            case TASK_STOPPED:
                return "(TASK_WAKEKILL | __TASK_STOPPED)";
            case TASK_TRACED:
                return "(TASK_WAKEKILL | __TASK_TRACED)";
            default:
            {
                sprintf(buffer, "Unknown Type:%ld", state);
                return buffer;
            }
        }
    }
    
    static int print_running_thread(void *data)
    {
             while(!kthread_should_stop()) {
                     struct task_struct *task_list;
                     for_each_process(task_list) {
                             if (task_list->state == TASK_RUNNING)
                                     pr_info("Process: %s	 PID:[%d]	 State:%s	 CPU:%d
    ",
                                                     task_list->comm, task_list->pid,
                                                     get_task_state(task_list->state), task_list->cpu);
                     }
                     msleep(500);
             }
             return 0;
    }
    
    MODULE_LICENSE("GPL");
    static int test_tasks_init(void)
    {
        pr_info("%s: In init
    ", __func__);
        print_thread = kthread_run(print_running_thread, NULL,
                                    "print_running_cpu");
        return 0;
    }
    
    static void test_tasks_exit(void)
    {
        pr_info("%s: In exit
    ", __func__);
        kthread_stop(print_thread);
    }
    
    module_init(test_tasks_init);
    module_exit(test_tasks_exit);
    EOF
    
    # ls /lib/modules/4.15.0-101-generic/build/include/linux/
    cat <<EOF |  sed -e "s/\	/	/" | tee Makefile
    obj-m += ${MODULE}.o
    
    all:
    	make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules
    clean:
    	make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean
    EOF
    
    sudo insmod ${MODULE}.ko
    lsmod |grep ${MODULE}
    sudo rmmod ${MODULE}
    lsmod |grep ${MODULE}
    dmesg |tail
    View Code

    Kernel Thread Creation : 1   (schedule with wake_up_process)

    MODULE=threads
    cat <<EOF | tee ${MODULE}.c
    #include <linux/module.h>
    #include <linux/kernel.h>
    #include <linux/kthread.h>  // for threads
    #include <linux/sched.h>  // for task_struct
    #include <linux/time.h>   // for using jiffies 
    #include <linux/timer.h>
    
    static struct task_struct *thread1;
    
    
    static int thread_fn(void *data) {
    
    unsigned long j0,j1;
    int delay = 60*HZ;
    
    printk(KERN_INFO "In thread1");
    j0 = jiffies;
    j1 = j0 + delay;
    
    while (time_before(jiffies, j1)){
            schedule();
            printk(KERN_INFO "jiffies:%ld, I am thread: %s[PID = %d] [TGID = %d]
    ", j0, current->comm, current->pid, current->tgid);
    }
    return 0;
    }
    
    int thread_init (void) {
       
        char  our_thread[8]="thread1";
        printk(KERN_INFO "in init");
        thread1 = kthread_create(thread_fn,NULL,our_thread);
        if((thread1))
            {
            printk(KERN_INFO "in if");
            wake_up_process(thread1);
            }
    
        return 0;
    }
    
    
    
    void thread_cleanup(void) {
     int ret;
     ret = kthread_stop(thread1);
     if(!ret)
      printk(KERN_INFO "Thread stopped");
    
    }
    MODULE_LICENSE("GPL");   
    module_init(thread_init);
    module_exit(thread_cleanup);
    EOF
    
    # ls /lib/modules/4.15.0-101-generic/build/include/linux/
    cat <<EOF |  sed -e "s/\	/	/" | tee Makefile
    ifneq ($(KERNELRELEASE),)
    	obj-m := threads.o
    else
    
    KERNELDIR ?= /lib/modules/$(shell uname -r)/build
    
    PWD := $(shell pwd)
    
    default:
    	$(MAKE) -C $(KERNELDIR) M=$(PWD) modules 
    endif
    EOF
    
    sudo insmod ${MODULE}.ko
    lsmod |grep ${MODULE}
    sudo rmmod ${MODULE}
    lsmod |grep ${MODULE}
    dmesg |tail
    View Code

    Linux Device Driver Tutorial Part 19 – Kernel Thread    

    MODULE=driver
    cat <<EOF | tee ${MODULE}.c
    /***************************************************************************//**
    *  file       driver.c
    *
    *  details    Simple Linux device driver (Kernel Thread)
    *
    *  author     EmbeTronicX
    *
    * *******************************************************************************/
    #include <linux/kernel.h>
    #include <linux/init.h>
    #include <linux/module.h>
    #include <linux/kdev_t.h>
    #include <linux/fs.h>
    #include <linux/cdev.h>
    #include <linux/device.h>
    #include<linux/slab.h>                 //kmalloc()
    #include<linux/uaccess.h>              //copy_to/from_user()
    #include <linux/kthread.h>             //kernel threads
    #include <linux/sched.h>               //task_struct 
    #include <linux/delay.h>
     
     
    dev_t dev = 0;
    static struct class *dev_class;
    static struct cdev etx_cdev;
     
    static int __init etx_driver_init(void);
    static void __exit etx_driver_exit(void);
     
    static struct task_struct *etx_thread;
     
    /*
    ** Function Prototypes
    */
    /*************** Driver Fuctions **********************/
    static int etx_open(struct inode *inode, struct file *file);
    static int etx_release(struct inode *inode, struct file *file);
    static ssize_t etx_read(struct file *filp, 
                    char __user *buf, size_t len,loff_t * off);
    static ssize_t etx_write(struct file *filp, 
                    const char *buf, size_t len, loff_t * off);
     /******************************************************/
     
    int thread_function(void *pv);
     
    /*
    ** Thread
    */
    int thread_function(void *pv)
    {
        int i=0;
        while(!kthread_should_stop()) {
            printk(KERN_INFO "In EmbeTronicX Thread Function %d
    ", i++);
            msleep(1000);
        }
        return 0;
    }
     
    /*
    ** File operation sturcture
    */ 
    static struct file_operations fops =
    {
            .owner          = THIS_MODULE,
            .read           = etx_read,
            .write          = etx_write,
            .open           = etx_open,
            .release        = etx_release,
    };
     
    /*
    ** This fuction will be called when we open the Device file
    */  
    static int etx_open(struct inode *inode, struct file *file)
    {
            printk(KERN_INFO "Device File Opened...!!!
    ");
            return 0;
    }
     
    /*
    ** This fuction will be called when we close the Device file
    */   
    static int etx_release(struct inode *inode, struct file *file)
    {
            printk(KERN_INFO "Device File Closed...!!!
    ");
            return 0;
    }
     
    /*
    ** This fuction will be called when we read the Device file
    */
    static ssize_t etx_read(struct file *filp, 
                    char __user *buf, size_t len, loff_t *off)
    {
            printk(KERN_INFO "Read function
    ");
     
            return 0;
    }
     
    /*
    ** This fuction will be called when we write the Device file
    */
    static ssize_t etx_write(struct file *filp, 
                    const char __user *buf, size_t len, loff_t *off)
    {
            printk(KERN_INFO "Write Function
    ");
            return len;
    }
     
    /*
    ** Module Init function
    */  
    static int __init etx_driver_init(void)
    {
            /*Allocating Major number*/
            if((alloc_chrdev_region(&dev, 0, 1, "etx_Dev")) <0){
                    printk(KERN_INFO "Cannot allocate major number
    ");
                    return -1;
            }
            printk(KERN_INFO "Major = %d Minor = %d 
    ",MAJOR(dev), MINOR(dev));
     
            /*Creating cdev structure*/
            cdev_init(&etx_cdev,&fops);
     
            /*Adding character device to the system*/
            if((cdev_add(&etx_cdev,dev,1)) < 0){
                printk(KERN_INFO "Cannot add the device to the system
    ");
                goto r_class;
            }
     
            /*Creating struct class*/
            if((dev_class = class_create(THIS_MODULE,"etx_class")) == NULL){
                printk(KERN_INFO "Cannot create the struct class
    ");
                goto r_class;
            }
     
            /*Creating device*/
            if((device_create(dev_class,NULL,dev,NULL,"etx_device")) == NULL){
                printk(KERN_INFO "Cannot create the Device 
    ");
                goto r_device;
            }
     
            etx_thread = kthread_create(thread_function,NULL,"eTx Thread");
            if(etx_thread) {
                wake_up_process(etx_thread);
            } else {
                printk(KERN_ERR "Cannot create kthread
    ");
                goto r_device;
            }
    #if 0
            /* You can use this method to create and run the thread */
            etx_thread = kthread_run(thread_function,NULL,"eTx Thread");
            if(etx_thread) {
                printk(KERN_ERR "Kthread Created Successfully...
    ");
            } else {
                printk(KERN_ERR "Cannot create kthread
    ");
                 goto r_device;
            }
    #endif
            printk(KERN_INFO "Device Driver Insert...Done!!!
    ");
            return 0;
     
     
    r_device:
            class_destroy(dev_class);
    r_class:
            unregister_chrdev_region(dev,1);
            cdev_del(&etx_cdev);
            return -1;
    }
     
    /*
    ** Module exit function
    */  
    static void __exit etx_driver_exit(void)
    {
            kthread_stop(etx_thread);
            device_destroy(dev_class,dev);
            class_destroy(dev_class);
            cdev_del(&etx_cdev);
            unregister_chrdev_region(dev, 1);
            printk(KERN_INFO "Device Driver Remove...Done!!
    ");
    }
     
    module_init(etx_driver_init);
    module_exit(etx_driver_exit);
     
    MODULE_LICENSE("GPL");
    MODULE_AUTHOR("EmbeTronicX <embetronicx@gmail.com>");
    MODULE_DESCRIPTION("A simple device driver - Kernel Thread");
    MODULE_VERSION("1.14");
    EOF
    
    
    cat <<EOF |  sed -e "s/\	/	/" | tee Makefile
    obj-m += ${MODULE}.o
     
    KDIR = /lib/modules/$(shell uname -r)/build
     
    all:
    	make -C $(KDIR) M=$(shell pwd) modules
     
    clean:
    	make -C $(KDIR) M=$(shell pwd) clean
    EOF
    
    
    sudo insmod ${MODULE}.ko
    lsmod |grep ${MODULE}
    dmesg |tail
    sudo rmmod ${MODULE}
    lsmod |grep ${MODULE}

     Githu Linux-Kernel-Examples, stackoverflow Creating Kernel Thread using kernel_thread

    ioctl 

    Linux内核态文件读写相关函数API  (vfs_write 和 vfs_read 例子, same 文章来在cnblog

    Linux kernel filp_open解析   

    linux内核态文件操作filp_open/filp_close/vfs_read/vfs_write   

    内核层读写应用层文件,使用filp_open函数——完美   

    内核层读写应用层文件,使用filp_open函数 (vfs_write 和 vfs_read)

    Read/write files within a Linux kernel module  (也推荐 vfs_write 和 vfs_read)

    You should be aware that you should avoid file I/O from within Linux kernel when possible. The main idea is to go "one level deeper" and call VFS level functions instead of the syscall handler directly

    Writing to a file from the Kernel  (snippet)

    linux kernel access file : filp_open – example   

    Reading Files From The Linux Kernel Space (Module/Driver) (Fedora 14)    

    Linux Device Driver, kernel thread can't open file? (内核线程打开文件)

    目前网上ioctl的例子如下, 编译会有问题,一个是 _struct task_struct_  不兼容的问题(不要引用asm/uascess.h),一个是ioctl 不再支持的问题, 一个是asm/linkage.h不存在的问题。

    # filp_open/sys_open  https://developer.aliyun.com/article/449701
    cat << EOF | tee chardev.h
    #include <linux/ioctl.h>
    #define BAO_IOCTL 't'
    #define IOCTL_READ  _IOR(BAO_IOCTL, 0, int)
    #define IOCTL_WRITE  _IOW(BAO_IOCTL, 1, int)
    #define BAO_IOCTL_MAXNR 1
    EOF
    
    
    # issue:  error: dereferencing pointer to incomplete type 'struct task_struct'
    # REF: https://lkml.org/lkml/2017/10/24/827
    # REF: #include <linux/sched.h>, http://biancheng.dnbcw.net/linux/351783.html 
    # REF: #include <linux/uaccess.h>, https://lkml.org/lkml/2017/9/30/189
    # https://medium.com/@avenger.v14/hi-when-building-the-enhanced-example-with-character-device-i-encountered-a-build-error-for-55079354f704
    # REF: http://dannysun-unknown.blogspot.com/2018/01/error-dereferencing-pointer-to.html
    MODULE=test
    UFILE=memory
    # sudo tee ${MODULE}.c >/dev/null <<EOF
    cat <<EOF | tee ${MODULE}.c
    #include <linux/kernel.h>
    #include <linux/sched.h>
    #include <linux/module.h>
    #include <linux/stat.h>
    #include <linux/fs.h>  /* unlocked_ioctl and compat_ioctl, vfs_read, vfs_write */
    #include <asm/unistd.h>
    #include <asm/uaccess.h>
    #include <linux/types.h>
    #include <linux/ioctl.h> /* vfs_ioctl */
    #include "chardev.h"
    
    MODULE_LICENSE("GPL");
    //#define __KERNEL_SYSCALLS__
    
    static char buf1[20];
    static char buf2[20];
    struct file *file=NULL;
    
    static int __init testmod_init(void)
    {
        mm_segment_t old_fs;
        ssize_t result;
        ssize_t ret;
    
        sprintf(buf1,"%s","baoqunmin");
        file=filp_open("${UFILE}",O_RDWR,0);
        if(IS_ERR(file)) goto fail0;
    
        old_fs=get_fs();
        set_fs(get_ds());
    
        ret=file->f_op->write(file,buf1,sizeof(buf1),&file->f_pos);
        result=file->f_op->read(file,buf2,sizeof(buf2),&file->f_pos);
    
        if(result>=0){buf2[19]='
    ';printk("buf2-->%s
    ",buf2);}
        else printk("failed
    ");
    
        result=file->f_op->unlocked_ioctl(file,IOCTL_READ,1); /* unsupport ioctl any more,  hardcode arg=1 */
        result=file->f_op->read(file,buf2,sizeof(buf2),&file->f_pos);
        set_fs(old_fs);
           filp_close(file,NULL);
    
         printk("file loaded
    ");
            return 0;
        fail0:{filp_close(file,NULL);printk("load failed
    ");}
    
        return 1;
    }
    
    static void __exit testmod_cleanup(void)
    {
    
        printk("module exit.................................
    ");
    }
    
    module_init(testmod_init);
    module_exit(testmod_cleanup);
    EOF
    
    
    # issue: asm/linkage.h: No such file or directory
    # REF:  https://cnasolution.com/questions/130765/gcc-error-asm-linkage-h-no-such-file-or-directory
    # REF: 3 ways work around https://github.com/dtaht/sch_cake/issues/110
    cat << EOF | sed -e "s/\	/	/" | tee Makefile
    CC=gcc
    INCPATH=/lib/modules/$(shell uname -r)/build/include
    ARCHPATH=/usr/src/linux-headers-${REL%-*}/arch/x86/include
    MODCFLAGS := -Wall -DMODULE -D__KERNEL__ -DLINUX -I$(INCPATH) -I$(ARCHPATH)
    
    test.o :test.c
    	$(CC) $(MODCFLAGS) -c test.c
    	echo insmod test.o to turn it on
    	echo rmmod test to turn it off
    	echo
    EOF
    
    
    cat <<EOF |  sed -e "s/\	/	/" | tee Makefile
    obj-m += ${MODULE}.o
     
    KDIR = /lib/modules/$(shell uname -r)/build
     
    all:
    	make -C $(KDIR) M=$(shell pwd) modules
     
    clean:
    	make -C $(KDIR) M=$(shell pwd) clean
    EOF
    View Code

    kernel doc

    The Linux kernel  

    ascii to string

     1 # https://stackoverflow.com/questions/5724761/ascii-hex-convert-in-bash/5725125
     2 # echo $'a	b'
     3 # cat <<< "abcdefga" | xxd  -c 4 
     4 # cat <<< "abcdefga" | xxd -ps -c 4 
     5 # echo Aa | od -t x1
     6 # echo -n Aa | hexdump -e '/1 "%02x"'
     7 # printf '30122722722320545' | iconv -f ebcdic-uk -t ascii | od -An -vtu1
     8 # https://www.xspdf.com/resolution/51314853.html
     9 # https://www.tecmint.com/convert-files-to-utf-8-encoding-in-linux/
    10 # https://www.geeksforgeeks.org/iconv-command-in-linux-with-examples/
    11 # https://www.howtoforge.com/linux-cat-command/
    12 # https://stackoverflow.com/questions/5724761/ascii-hex-convert-in-bash/5725125
    13 # https://stackoverflow.com/questions/525872/echo-tab-characters-in-bash-script
    14 # https://www.linuxquestions.org/questions/linux-newbie-8/tab-in-bash-script-242400/
    View Code
  • 相关阅读:
    未在本地计算机上注册“microsoft.ACE.oledb.12.0”提供程序 解决方法
    未能从程序集“System.ServiceModel, Version=3.0.0.0问题解决
    HTML5斯诺克桌球俱乐部【译】
    MVC调试时查看生成的sql语句
    小问题 小技巧 :创建虚拟目录并将IIS里面.net配置版本设为2.0
    网页调用服务程序
    WatiN——Web自动化测试(三)【弹出窗口处理】
    WatiN——Web自动化测试(二)
    小问题 小技巧 :网站路径问题
    小问题 小技巧:敲回车默认提交
  • 原文地址:https://www.cnblogs.com/shaohef/p/13616687.html
Copyright © 2020-2023  润新知