从用户空间,我们可以使用cpuset来实际隔离我们系统中的特定内核,并只执行一个特定的进程到该内核。
我试图用一个内核模块做同样的事情。 所以我想要模块在一个孤立的核心执行。 换句话说: 如何在内核模块中使用cpuset
? *
在我的内核模块中使用linux / cpuset.h不起作用。 所以,我有一个这样的模块:
#include <linux/module.h> #include <linux/cpuset.h> ... #ifdef CONFIG_CPUSETS printk(KERN_INFO, "cpusets is enabled!"); #endif cpuset_init(); // this function is declared in cpuset.h ...
当试图加载这个模块,我得到(在dmesg
)下面的消息cpusets is enabled!
。 但是我也收到消息Unknown symbol cpu_init (err 0)
。
同样,我尝试使用sched_setaffinity
从linux/sched.h
为了移动所有正在运行的procceses到一个特定的核心,然后运行我的模块到一个孤立的核心。 我得到了同样的错误消息: Unknown symbol sched_setaffinity (err 0)
。 我想我得到了“未知符号”,因为这些函数在内核中没有EXPORT_SYMBOL
。 所以我去了,试图调用sys_sched_setaffinity
系统调用 (基于这个问题 ),但又得到了这个消息: Unknown symbol sys_sched_setaffinity (err 0)
!
此外,我不寻找一个解决scheme,使用isolcpus
,这是在启动时设置的。 我想只是加载模块,然后隔离发生。
所以我想要模块在一个孤立的核心执行。
和
实际上隔离了我们系统中的一个特定的核心,并且只执行一个特定的进程到这个核
这是一个使用内核3.16在Debian框中编译和测试的工作源代码。 我将介绍如何首先加载和卸载以及传递的参数的含义。
所有的来源可以在这里找到github …
https://github.com/harryjackson/doc/tree/master/linux/kernel/toy/toy
构建和加载模块…
make insmod toy param_cpu_id=2
卸载模块使用
rmmod toy
我不使用modprobe,因为它需要一些配置等。我们传递给toy
内核模块的参数是我们要隔离的CPU。 被调用的设备操作都不会运行,除非它们在该CPU上执行。
一旦模块被加载,你可以在这里找到它
/dev/toy
简单的操作就像
cat /dev/toy
创建内核模块捕获并产生一些输出的事件。 你可以看到使用dmesg
的输出。
源代码…
#include <linux/module.h> #include <linux/fs.h> #include <linux/miscdevice.h> MODULE_LICENSE("GPL"); MODULE_AUTHOR("Harry"); MODULE_DESCRIPTION("toy kernel module"); MODULE_VERSION("0.1"); #define DEVICE_NAME "toy" #define CLASS_NAME "toy" static int param_cpu_id; module_param(param_cpu_id , int, (S_IRUSR | S_IRGRP | S_IROTH)); MODULE_PARM_DESC(param_cpu_id, "CPU ID that operations run on"); //static void bar(void *arg); //static void foo(void *cpu); static int toy_open( struct inode *inodep, struct file *fp); static ssize_t toy_read( struct file *fp , char *buffer, size_t len, loff_t * offset); static ssize_t toy_write( struct file *fp , const char *buffer, size_t len, loff_t *); static int toy_release(struct inode *inodep, struct file *fp); static struct file_operations toy_fops = { .owner = THIS_MODULE, .open = toy_open, .read = toy_read, .write = toy_write, .release = toy_release, }; static struct miscdevice toy_device = { .minor = MISC_DYNAMIC_MINOR, .name = "toy", .fops = &toy_fops }; //static int CPU_IDS[64] = {0}; static int toy_open(struct inode *inodep, struct file *filep) { int this_cpu = get_cpu(); printk(KERN_INFO "open: called on CPU:%d\n", this_cpu); if(this_cpu == param_cpu_id) { printk(KERN_INFO "open: is on requested CPU: %d\n", smp_processor_id()); } else { printk(KERN_INFO "open: not on requested CPU:%d\n", smp_processor_id()); } put_cpu(); return 0; } static ssize_t toy_read(struct file *filep, char *buffer, size_t len, loff_t *offset){ int this_cpu = get_cpu(); printk(KERN_INFO "read: called on CPU:%d\n", this_cpu); if(this_cpu == param_cpu_id) { printk(KERN_INFO "read: is on requested CPU: %d\n", smp_processor_id()); } else { printk(KERN_INFO "read: not on requested CPU:%d\n", smp_processor_id()); } put_cpu(); return 0; } static ssize_t toy_write(struct file *filep, const char *buffer, size_t len, loff_t *offset){ int this_cpu = get_cpu(); printk(KERN_INFO "write called on CPU:%d\n", this_cpu); if(this_cpu == param_cpu_id) { printk(KERN_INFO "write: is on requested CPU: %d\n", smp_processor_id()); } else { printk(KERN_INFO "write: not on requested CPU:%d\n", smp_processor_id()); } put_cpu(); return 0; } static int toy_release(struct inode *inodep, struct file *filep){ int this_cpu = get_cpu(); printk(KERN_INFO "release called on CPU:%d\n", this_cpu); if(this_cpu == param_cpu_id) { printk(KERN_INFO "release: is on requested CPU: %d\n", smp_processor_id()); } else { printk(KERN_INFO "release: not on requested CPU:%d\n", smp_processor_id()); } put_cpu(); return 0; } static int __init toy_init(void) { int cpu_id; if(param_cpu_id < 0 || param_cpu_id > 4) { printk(KERN_INFO "toy: unable to load module without cpu parameter\n"); return -1; } printk(KERN_INFO "toy: loading to device driver, param_cpu_id: %d\n", param_cpu_id); //preempt_disable(); // See notes below cpu_id = get_cpu(); printk(KERN_INFO "toy init called and running on CPU: %d\n", cpu_id); misc_register(&toy_device); //preempt_enable(); // See notes below put_cpu(); //smp_call_function_single(1,foo,(void *)(uintptr_t) 1,1); return 0; } static void __exit toy_exit(void) { misc_deregister(&toy_device); printk(KERN_INFO "toy exit called\n"); } module_init(toy_init); module_exit(toy_exit);
上面的代码包含了你所要求的两种方法,即隔离CPU和在隔离内核上运行的init
。
在初始化get_cpu
禁用抢占,即任何内容之后,它不会被内核抢占,并将运行在一个核心。 请注意,这是使用3.16内核,你的里程可能会有所不同,取决于你的内核版本,但我认为这些API已经很长一段时间
这是Makefile …
obj-m += toy.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
笔记。 get_cpu
在linux/smp.h
声明为
#define get_cpu() ({ preempt_disable(); smp_processor_id(); }) #define put_cpu() preempt_enable()
所以在调用preempt_disable
之前,实际上并不需要调用get_cpu
。 get_cpu调用是下列调用序列的一个包装。
preempt_count_inc(); barrier();
和put_cpu是真的这样做…
barrier(); if (unlikely(preempt_count_dec_and_test())) { __preempt_schedule(); }
你可以像上面那样使用幻想。 几乎所有这些都来自以下来源。
Google for … smp_call_function_single
Linux内核开发,由罗伯特·爱书。
http://derekmolloy.ie/writing-a-linux-kernel-module-part-2-a-character-device/
你指出你的问题:
我想我得到了“未知符号”,因为这些函数在内核中没有EXPORT_SYMBOL
我认为这是你的问题的关键点。 我看到你正在包含文件linux/cpuset.h
,它定义了方法: cpuset_init
等等。 但是,在编译和使用nm
命令时,我们可以看到指示器指示我们这个函数不可用:
编译:
root@hectorvp-pc:/home/hectorvp/cpuset/cpuset_try# make make -C /lib/modules/3.19.0-31-generic/build M=/home/hectorvp/cpuset/cpuset_try modules make[1]: Entering directory '/usr/src/linux-headers-3.19.0-31-generic' CC [M] /home/hectorvp/cpuset/cpuset_try/cpuset_try.o Building modules, stage 2. MODPOST 1 modules WARNING: "cpuset_init" [/home/hectorvp/cpuset/cpuset_try/cpuset_try.ko] undefined! CC /home/hectorvp/cpuset/cpuset_try/cpuset_try.mod.o LD [M] /home/hectorvp/cpuset/cpuset_try/cpuset_try.ko make[1]: Leaving directory '/usr/src/linux-headers-3.19.0-31-generic'
看到WARNING: "cupset_init" [...] undefined!
。 并使用nm
:
root@hectorvp-pc:/home/hectorvp/cpuset/cpuset_try# nm cpuset_try.ko 0000000000000030 T cleanup_module U cpuset_init U __fentry__ 0000000000000000 T init_module 000000000000002f r __module_depends U printk 0000000000000000 D __this_module 0000000000000000 r __UNIQUE_ID_license0 000000000000000c r __UNIQUE_ID_srcversion1 0000000000000038 r __UNIQUE_ID_vermagic0 0000000000000000 r ____versions
(注: U
代表“未定义”)
不过 ,我一直在探索内核的符号如下:
root@hectorvp-pc:/home/hectorvp/cpuset/cpuset_try# cat /proc/kallsyms | grep cpuset_init ffffffff8110dc40 T cpuset_init_current_mems_allowed ffffffff81d722ae T cpuset_init ffffffff81d72342 T cpuset_init_smp
我看到它已经被导出,但是它在/lib/modules/$(uname -r)/build/modulee.symvers
。 所以你是对的。
经过进一步的调查,我发现它实际上定义在:
http://lxr.free-electrons.com/source/kernel/cpuset.c#L2101
这是您需要调用的函数,因为它在内核空间中可用。 因此你不需要访问用户空间。
在这个问题的第二个答案中报告了我发现使模块能够调用这个符号的工作。 注意,你不需要包含linux/cpuset.h
:
#include <linux/module.h> #include <linux/kernel.h> #include <linux/init.h> //#include <linux/cpuset.h> #include <linux/kallsyms.h> int init_module(void) { static void (*cpuset_init_p)(void); cpuset_init_p = (void*) kallsyms_lookup_name("cpuset_init"); printk(KERN_INFO "Starting ...\n"); #ifdef CONFIG_CPUSETS printk(KERN_INFO "cpusets is enabled!"); #endif (*cpuset_init_p)(); /* * A non 0 return means init_module failed; module can't be loaded. */ return 0; } void cleanup_module(void) { printk(KERN_INFO "Ending ...\n"); } MODULE_LICENSE("GPL");
我编译成功,并与insmod
安装。 贝娄是我在dmesg
得到的输出:
[ 1713.738925] Starting ... [ 1713.738929] cpusets is enabled! [ 1713.738943] kernel tried to execute NX-protected page - exploit attempt? (uid: 0) [ 1713.739042] BUG: unable to handle kernel paging request at ffffffff81d7237b [ 1713.739074] IP: [<ffffffff81d7237b>] cpuset_init+0x0/0x94 [ 1713.739102] PGD 1c16067 PUD 1c17063 PMD 30bc74063 PTE 8000000001d72163 [ 1713.739136] Oops: 0011 [#1] SMP [ 1713.739153] modulees linked in: cpuset_try(OE+) xt_conntrack ipt_MASQUERADE nf_nat_masquerade_ipv4 iptable_nat nf_conntrack_ipv4 nf_defrag_ipv4 nf_nat_ipv4 xt_addrtype iptable_filter ip_tables x_tables nf_nat nf_conntrack br_netfilter bridge stp llc pci_stub vboxpci(OE) vboxnetadp(OE) vboxnetflt(OE) vboxdrv(OE) aufs binfmt_misc cfg80211 nls_iso8859_1 snd_hda_codec_hdmi snd_hda_codec_realtek intel_rapl snd_hda_codec_generic iosf_mbi snd_hda_intel x86_pkg_temp_thermal intel_powerclamp snd_hda_controller snd_hda_codec snd_hwdep coretemp kvm_intel amdkfd kvm snd_pcm snd_seq_midi snd_seq_midi_event amd_iommu_v2 snd_rawmidi radeon snd_seq crct10dif_pclmul crc32_pclmul snd_seq_device aesni_intel ttm aes_x86_64 drm_kms_helper drm snd_timer i2c_algo_bit dcdbas mei_me lrw gf128mul mei snd glue_helper ablk_helper [ 1713.739533] cryptd soundcore shpchp lpc_ich serio_raw 8250_fintek mac_hid video parport_pc ppdev lp parport autofs4 hid_generic usbhid hid e1000e ahci psmouse ptp libahci pps_core [ 1713.739628] CPU: 2 PID: 24679 Comm: insmod Tainted: G OE 3.19.0-56-generic #62-Ubuntu [ 1713.739663] Hardware name: Dell Inc. OptiPlex 9020/0PC5F7, BIOS A03 09/17/2013 [ 1713.739693] task: ffff8800d29f09d0 ti: ffff88009177c000 task.ti: ffff88009177c000 [ 1713.739723] RIP: 0010:[<ffffffff81d7237b>] [<ffffffff81d7237b>] cpuset_init+0x0/0x94 [ 1713.739757] RSP: 0018:ffff88009177fd10 EFLAGS: 00010292 [ 1713.739779] RAX: 0000000000000013 RBX: ffffffff81c1a080 RCX: 0000000000000013 [ 1713.739808] RDX: 000000000000c928 RSI: 0000000000000246 RDI: 0000000000000246 [ 1713.739836] RBP: ffff88009177fd18 R08: 000000000000000a R09: 00000000000003db [ 1713.739865] R10: 0000000000000092 R11: 00000000000003db R12: ffff8800ad1aaee0 [ 1713.739893] R13: 0000000000000000 R14: ffffffffc0947000 R15: ffff88009177fef8 [ 1713.739923] FS: 00007fbf45be8700(0000) GS:ffff88031dd00000(0000) knlGS:0000000000000000 [ 1713.739955] CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 [ 1713.739979] CR2: ffffffff81d7237b CR3: 00000000a3733000 CR4: 00000000001407e0 [ 1713.740007] Stack: [ 1713.740016] ffffffffc094703e ffff88009177fd98 ffffffff81002148 0000000000000001 [ 1713.740052] 0000000000000001 ffff8802479de200 0000000000000001 ffff88009177fd78 [ 1713.740087] ffffffff811d79e9 ffffffff810fb058 0000000000000018 ffffffffc0949000 [ 1713.740122] Call Trace: [ 1713.740137] [<ffffffffc094703e>] ? init_module+0x3e/0x50 [cpuset_try] [ 1713.740175] [<ffffffff81002148>] do_one_initcall+0xd8/0x210 [ 1713.740190] [<ffffffff811d79e9>] ? kmem_cache_alloc_trace+0x189/0x200 [ 1713.740207] [<ffffffff810fb058>] ? load_module+0x15b8/0x1d00 [ 1713.740222] [<ffffffff810fb092>] load_module+0x15f2/0x1d00 [ 1713.740236] [<ffffffff810f6850>] ? store_uevent+0x40/0x40 [ 1713.740250] [<ffffffff810fb916>] SyS_finit_module+0x86/0xb0 [ 1713.740265] [<ffffffff817ce10d>] system_call_fastpath+0x16/0x1b [ 1713.740280] Code: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 0c 53 58 31 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 <00> 00 00 00 00 1c 00 00 00 c0 92 2c 7d c0 92 2c 7d a0 fc 69 ee [ 1713.740398] RIP [<ffffffff81d7237b>] cpuset_init+0x0/0x94 [ 1713.740413] RSP <ffff88009177fd10> [ 1713.740421] CR2: ffffffff81d7237b [ 1713.746177] ---[ end trace 25614103c0658b94 ]---
尽管有错误,我会说我已经回答了你最初的问题:
如何从内核模块中使用cpuset? *
可能不是最优雅的方式,因为我不是一个专家。 你需要从这里继续。
问候
你有尝试work_struct吗?
struct workqueue_attrs { cpumask_var_t cpumask; /* allowed CPUs */ }
首先应该通过cpu隔离(例如cpu 0x1)
setenv bootargs isolcpus=\"0x1"\
和下一个
struct lkm_sample { struct work_struct lkm_work_struct; struct workqueue_struct *lkm_wq_struct; ... }; static struct lkm_sample lkm_smpl; static void work(struct work_struct *work) { struct lkm_sample *tmp = container_of(work, struct lkm_sample, lkm_work_struct); .... return; } static int __init lkm_init(void) { //see: https://lwn.net/Articles/540999/ lkm_smpl.lkm_wq_struct = create_singlethread_workqueue("you_wq_name"); INIT_WORK(&lkm_smpl.lkm_wq_struct, work); }
如果你想启动(运行__init)在隔离的cpu上lkm:
setenv bootargs isolcpus = \“0x1”\
lsmod helper_module.ko与
call_usermodehelper_setup struct subprocess_info * call_usermodehelper_setup(char * path,char ** argv,/ * taskset 0x00000001 helper_application * / char ** envp,gfp_t gfp_mask,int(* init)(struct subprocess_info * info,struct cred * new),void(*清理)(struct subprocess_info * info),void * data); 使用helper内核模块,它应该通过taskset运行用户空间程序(helper_application),mask应该来自isolcpus。 Helper模块应该仅运行__init函数()并返回-1,因为只有一个任务:在独立的cpu上运行用户空间应用程序。
用户空间助手应用程序接下来应该只是:lsmod为goal_module.ko,goal_module应该在同一个独立的cpu上启动。
使用workqueue继续在隔离的cpu上运行隔离模块。