接下来我们来尝试写一个测试程序,该测试程序首先在用户空间创建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
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)
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)
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
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.
use "make tools/help"
or "cd tools; make help"
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
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模块的编译工作。
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
4.2可复制系统当前配置文件sudo apt-get install linux-source
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一致(后缀面不同)。
make
五、efd-us 与efd-lkm功能联调 5.1 启动执行efd-us
新建一个终端窗口执行用户空间应用程序efd-us,如下:
5.2 安装efd-lkm.ko./efd-us
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与源文件文件名不一致。
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)