一起来感受下eventfd的魅力(二、kernel通过eventfd向user空间发送消息事件通知)

一起来感受下eventfd的魅力(二、kernel通过eventfd向user空间发送消息事件通知),第1张

写在前面

        接下来我们来尝试写一个测试程序,该测试程序首先在用户空间创建eventfd对象,然后再通过kernel模块程序更新eventfd的计数器以实现向用户控件程序发送事件消息通知。


        *本篇基于Ubunt 18.04调试和验证*


一、用户空间程序-efd-us 1.1 源码

efd-us.c

#include 
#include 
#include 
#include      //Definition of uint64_t
#include 

int efd; //Eventfd file descriptor
uint64_t eftd_ctr;

int retval;     //for select()
fd_set rfds;        //for select()

int s;

int main() {


    //Create eventfd
    efd = eventfd(0,0);
    if (efd == -1){
        printf("\nUnable to create eventfd! Exiting...\n");
        exit(EXIT_FAILURE);
    }

    printf("\nefd=%d pid=%d",efd,getpid());

    //Watch efd
    FD_ZERO(&rfds);
    FD_SET(efd, &rfds);

    printf("\nNow waiting on select()...");
    fflush(stdout);

    retval = select(efd+1, &rfds, NULL, NULL, NULL);

    if (retval == -1){
        printf("\nselect() error. Exiting...");
        exit(EXIT_FAILURE);
    } else if (retval > 0) {
        printf("\nselect() says data is available now. Exiting...");
        printf("\nreturned from select(), now executing read()...");
        s = read(efd, &eftd_ctr, sizeof(uint64_t));
        if (s != sizeof(uint64_t)){
            printf("\neventfd read error. Exiting...");
        } else {
            printf("\nReturned from read(), value read = %lld",eftd_ctr);
        }
    } else if (retval == 0) {
        printf("\nselect() says that no data was available");
    }

    printf("\nClosing eventfd. Exiting...");
    close(efd);
    printf("\n");
    exit(EXIT_SUCCESS);
}
1.2 编译方法
gcc efd-us.c -o efd-us

1.3 代码解读

//创建eventfd文件描述符

efd = eventfd(0,0);

//监听efd

retval = select(efd+1, &rfds, NULL, NULL, NULL);

//读取efd计数器内容

s = read(efd, &eftd_ctr, sizeof(uint64_t));


二、内核模块代码 - efd-lkm

2.1 efd-lkm.c源码

#include 
#include 
#include 
#include 
#include 
#include 
#include 

//Received from userspace. Process ID and eventfd's File descriptor are enough to uniquely identify an eventfd object.
int pid;
int efd;

//Resolved references...
struct task_struct * userspace_task = NULL; //...to userspace program's task struct
struct file * efd_file = NULL;          //...to eventfd's file struct
struct eventfd_ctx * efd_ctx = NULL;        //...and finally to eventfd context

//Increment Counter by 1
static uint64_t plus_one = 1;

int init_module(void) {
    printk(KERN_ALERT "~~~Received from userspace: pid=%d efd=%d\n",pid,efd);

    userspace_task = pid_task(find_vpid(pid), PIDTYPE_PID);
    printk(KERN_ALERT "~~~Resolved pointer to the userspace program's task struct: %p\n",userspace_task);

    printk(KERN_ALERT "~~~Resolved pointer to the userspace program's files struct: %p\n",userspace_task->files);

    rcu_read_lock();
    efd_file = fcheck_files(userspace_task->files, efd);
    rcu_read_unlock();
    printk(KERN_ALERT "~~~Resolved pointer to the userspace program's eventfd's file struct: %p\n",efd_file);


    efd_ctx = eventfd_ctx_fileget(efd_file);
    if (!efd_ctx) {
        printk(KERN_ALERT "~~~eventfd_ctx_fileget() Jhol, Bye.\n");
        return -1;
    }
    printk(KERN_ALERT "~~~Resolved pointer to the userspace program's eventfd's context: %p\n",efd_ctx);

    eventfd_signal(efd_ctx, plus_one);

    printk(KERN_ALERT "~~~Incremented userspace program's eventfd's counter by 1\n");

    eventfd_ctx_put(efd_ctx);

    return 0;
}


void cleanup_module(void) {
    printk(KERN_ALERT "~~~Module Exiting...\n");
}  

MODULE_LICENSE("GPL");
module_param(pid, int, 0);
module_param(efd, int, 0);
2.2 代码解读

//init_module是默认的模块的入口,如果你想指定其他的函数作为模块的入口就需要module_init函数来指定。


init_module()是真正的入口,module_init是宏,如果在模块中使用,最终还是要转换到init_module()上。


如果不是在模块中使用,module_init可以说没有什么作用。


init_module(void)

//pid_task( ) 函数获取任务的任务描述符信息

userspace_task = pid_task(find_vpid(pid), PIDTYPE_PID);

//根据files_struct结构中的filelist和给定的fd获取file结构体

efd_file = fcheck_files(userspace_task->files, efd);

//(Acquires a reference to the internal eventfd context.)获取对内部eventfd上下文的引用,并返回该引用的指针。


efd_ctx = eventfd_ctx_fileget(efd_file);

//eventfd计数器增1(不一定增1,这个plus_one可以为任何数字)

eventfd_signal(efd_ctx, plus_one);

//(Releases a reference to the internal eventfd context)释放对内部eventfd上下文的引用

eventfd_ctx_put(efd_ctx);


三、内核常用编译命令 3.1 清理命令

  clean          - Remove most generated files but keep the config and
                    enough build support to build external modules
  mrproper      - Remove all generated files + config + various backup files
  distclean      - mrproper + remove editor backup and patch files

3.2 配置命令

  config      - Update current config utilising a line-oriented program
  nconfig         - Update current config utilising a ncurses menu based program
  menuconfig      - Update current config utilising a menu based program
  xconfig      - Update current config utilising a Qt based front-end
  gconfig      - Update current config utilising a GTK+ based front-end
  oldconfig      - Update current config utilising a provided .config as base
  localmodconfig  - Update current config disabling modules not loaded
  localyesconfig  - Update current config converting local mods to core
  defconfig      - New config with default from ARCH supplied defconfig
  savedefconfig   - Save current config as ./defconfig (minimal config)
  allnoconfig      - New config where all options are answered with no
  allyesconfig      - New config where all options are accepted with yes
  allmodconfig      - New config selecting modules when possible
  alldefconfig    - New config with all symbols set to default
  randconfig      - New config with random answer to all options
  listnewconfig   - List new options
  olddefconfig      - Same as oldconfig but sets new symbols to their
                    default value without prompting
  kvmconfig      - Enable additional options for kvm guest kernel support
  xenconfig       - Enable additional options for xen dom0 and guest kernel support
  tinyconfig      - Configure the tiniest possible kernel
  testconfig      - Run Kconfig unit tests (requires python3 and pytest)

3.3 镜像、模块等kernel编译命令

  all          - Build all targets marked with [*]
* vmlinux      - Build the bare kernel
* modules      - Build all modules
  modules_install - Install all modules to INSTALL_MOD_PATH (default: /)
  dir/            - Build all files in dir and below
  dir/file.[ois]  - Build specified target only
  dir/file.ll     - Build the LLVM assembly file
                    (requires compiler support for LLVM assembly generation)
  dir/file.lst    - Build specified mixed source/assembly target only
                    (requires a recent binutils and recent build (System.map))
  dir/file.ko     - Build module including final link
  modules_prepare - Set up for building external modules
  tags/TAGS      - Generate tags file for editors
  cscope      - Generate cscope index
  gtags           - Generate GNU GLOBAL index
  kernelrelease      - Output the release version string (use with make -s)
  kernelversion      - Output the version stored in Makefile (use with make -s)
  image_name      - Output the image name (use with make -s)
  headers_install - Install sanitised kernel headers to INSTALL_HDR_PATH
                    (default: ./usr)

3.4 编译Static analysers

  checkstack      - Generate a list of stack hogs
  namespacecheck  - Name space analysis on compiled kernel
  versioncheck    - Sanity check on version.h usage
  includecheck    - Check for duplicate included header files
  export_report   - List the usages of all exported symbols
  headerdep       - Detect inclusion cycles in headers
  coccicheck      - Check with Coccinelle

3.5 编译Tools

  nsdeps          - Generate missing symbol namespace dependencies

3.6 编译Kernel自测试文件

  kselftest       - Build and run kernel selftest (run as root)
                    Build, install, and boot kernel before
                    running kselftest on it
  kselftest-clean - Remove all generated kselftest files
  kselftest-merge - Merge all the config dependencies of kselftest to existing
                    .config.

3.7 Userspace tools targets

  use "make tools/help"
  or  "cd tools; make help"

3.8 Kernel 打包命令

  rpm-pkg             - Build both source and binary RPM kernel packages
  binrpm-pkg          - Build only the binary kernel RPM package
  deb-pkg             - Build both source and binary deb kernel packages
  bindeb-pkg          - Build only the binary kernel deb package
  snap-pkg            - Build only the binary kernel snap package (will connect to external hosts)
  tar-pkg             - Build the kernel as an uncompressed tarball
  targz-pkg           - Build the kernel as a gzip compressed tarball
  tarbz2-pkg          - Build the kernel as a bzip2 compressed tarball
  tarxz-pkg           - Build the kernel as a xz compressed tarball
  perf-tar-src-pkg    - Build perf-5.4.174.tar source tarball
  perf-targz-src-pkg  - Build perf-5.4.174.tar.gz source tarball
  perf-tarbz2-src-pkg - Build perf-5.4.174.tar.bz2 source tarball
  perf-tarxz-src-pkg  - Build perf-5.4.174.tar.xz source tarball

3.9 Documentation targets

 Linux kernel internal documentation in different formats from ReST:
  htmldocs        - HTML
  latexdocs       - LaTeX
  pdfdocs         - PDF
  epubdocs        - EPUB
  xmldocs         - XML
  linkcheckdocs   - check for broken external links (will connect to external hosts)
  refcheckdocs    - check for references to non-existing files under Documentation
  cleandocs       - clean all generated files

  make SPHINXDIRS="s1 s2" [target] Generate only docs of folder s1, s2
  valid values for SPHINXDIRS are: 

  make SPHINX_CONF={conf-file} [target] use *additional* sphinx-build
  configuration. This is e.g. useful to build with nit-picking config.

  Default location for the generated documents is Documentation/output

3.10 Architecture specific targets (x86)

* bzImage      - Compressed kernel image (arch/x86/boot/bzImage)
  install      - Install kernel using
                  (your) ~/bin/installkernel or
                  (distribution) /sbin/installkernel or
                  install to $(INSTALL_PATH) and run lilo
  fdimage      - Create 1.4MB boot floppy image (arch/x86/boot/fdimage)
  fdimage144   - Create 1.4MB boot floppy image (arch/x86/boot/fdimage)
  fdimage288   - Create 2.8MB boot floppy image (arch/x86/boot/fdimage)
  isoimage     - Create a boot CD-ROM image (arch/x86/boot/image.iso)
                  bzdisk/fdimage*/isoimage also accept:
                  FDARGS="..."  arguments for the booted kernel
                  FDINITRD=file initrd for the booted kernel

  i386_defconfig           - Build for i386
  x86_64_defconfig         - Build for x86_64

  make V=0|1 [targets] 0 => quiet build (default), 1 => verbose build
  make V=2   [targets] 2 => give reason for rebuild of target
  make O=dir [targets] Locate all output files in "dir", including .config
  make C=1   [targets] Check re-compiled c source with $CHECK (sparse by default)
  make C=2   [targets] Force check of all c source with $CHECK
  make RECORDMCOUNT_WARN=1 [targets] Warn about ignored mcount sections
  make W=n   [targets] Enable extra build checks, n=1,2,3 where
        1: warnings which may be relevant and do not occur too often
        2: warnings which occur quite often but may still be relevant
        3: more obscure warnings, can most likely be ignored
        Multiple levels can be combined with W=12 or W=123

Execute "make" or "make all" to build all targets marked with [*]



四、编译内核模块代码-efd-lkm

efd-lkm源码部分和编译相关的准备工作如前面所描述的,接下来我们讨论自定义efd-lkm模块的编译工作。


4.1 下载内核代码

sudo apt-cache search linux-source

执行结果:

linux-source - Linux kernel source with Ubuntu patches
linux-source-4.15.0 - Linux kernel source for version 4.15.0 with Ubuntu patches
linux-source-4.18.0 - Linux kernel source for version 4.18.0 with Ubuntu patches
linux-source-5.0.0 - Linux kernel source for version 5.0.0 with Ubuntu patches
linux-source-5.3.0 - Linux kernel source for version 5.3.0 with Ubuntu patche

uname -r

执行结果:
5.4.0-84-generic

sudo apt-get install linux-source

4.2可复制系统当前配置文件
bill@bill-VirtualBox:/boot$ sudo cp config-5.4.0-84-generic ../usr/src/linux-headers-5.4.0-84-generic/.config

4.1 编写Makefile

Makefile

# Makefile once again.
# This conditional selects whether we are being included from the
# kernel Makefile or not.

# efd-lkm.o即我们定义的需要编译“内核模块”, obj-y 编译进内核,obj-n 不编译。


obj-m := efd-lkm.o CROSS_COMPILE = CC = gcc # Assume the source tree is where the running kernel was built # You should set KERNELDIR in the environment if it's elsewhere # 声明kernel源码路径 KERNELDIR ?= /usr/src/linux-headers-$(shell uname -r) # The current directory is passed to sub-makes as argument # 生命当前Makefiles所在路径 PWD := $(shell pwd) all: modules modules: $(MAKE) -C $(KERNELDIR) M=$(PWD) modules clean: rm -rf *.o *~ core .depend *.symvers .*.cmd *.ko *.mod.c .tmp_versions $(TARGET)

 注意!Makefile中的“obj-m := efd-lkm.o"中的efd-lmk.o需要与efd-lmk.c一致(后缀面不同)。


4.2 编译efd-lkm

make


五、efd-us 与efd-lkm功能联调 5.1 启动执行efd-us

新建一个终端窗口执行用户空间应用程序efd-us,如下:

./efd-us

 5.2 安装efd-lkm.ko

sudo insmod efd-lkm.ko pid-=2467 efd=3

命令解释:

语法:insmod [-fkmpsvxX][-o <模块名称>][模块文件][符号名称 = 符号值]

efd-lkm.ko//模块文件

pid=12467//pid为符号名称,12467位符号值

efd=3//同上

再新建一个终端窗口安装前面完成编译的内核模块efd-lkm.ko,如下:

5.3 测试结果


六、FAQ 5.1 执行“apt-get source linux-image-$(uname -r)”,提示"E:您必须在sources.list中指定源代码(deb-src)URI"

解决方案:在软件和更新(ubuntu)勾选“源代码”

5.2 *** No rule to make target '/home/bill/01_custom/01_efd_lkm/lkm.o', needed by '__build'.  Stop.

问题分析及解决方案:Makefile中“obj-m := xxx.o"的xxx与源文件文件名不一致。


欢迎分享,转载请注明来源:内存溢出

原文地址: https://outofmemory.cn/langs/563367.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-04-02
下一篇 2022-04-02

发表评论

登录后才能评论

评论列表(0条)

保存