限于作者能力水平,本文可能存在谬误,因此而给读者带来的损失,作者不做任何承诺。
本文分析基于 linux-4.14.132 上游代码。
运行环境为:
Ubuntu 16.04.4 LTS + QEMU + Arm vexpress-a9
rootfs 基于 ubuntu-base-16.04-core-armhf.tar.gz 制作。
交叉编译了 perf 后,将 perf 放到 rootfs 的 /usr/bin/perf 路径,用QEMU启动系统后,执行 perf ,系统提示 No such file or directory 。通过 ls 命令发现文件 /usr/bin/perf 确实存在,所以,No such file or directory信息提示的并不是文件 /usr/bin/perf 不存在,而应该是提示 perf 执行过程中,需要加载的其它文件不存在。由于 perf 程序是通过动态链接方式编译的,所以 perf 执行过程中,需加载相关的解释器程序(动态链接器)和动态链接库。事情到了这里,我们通常可能会怀疑是编译链接过程出了差错。用file命令,对比了不能正常运行的 perf 程序,和能正常运行的 make 程序的基本执行信息:
$ file /usr/bin/perf
/usr/bin/perf: ELF 32-bit LSB executable, ARM, EABI5 version 1 (SYSV), dynamically linked, interpreter /lib/ld-linux.so.3, for GNU/Linux 3.2.0, BuildID[sha1]=b33d1b50a0a7636b8f5c48cc111e0c816c691fc9, not stripped
$ file /usr/bin/make
/usr/bin/make: ELF 32-bit LSB executable, ARM, EABI5 version 1 (SYSV), dynamically linked, interpreter /lib/ld-linux-armhf.so.3, for GNU/Linux 2.6.32, BuildID[sha1]=0b89bb2e75e4a812e398a069ad88628f6f535dd5, stripped
通过上面两条信息,我们看到关键差异在于,perf 和 make 指向的解释器程序(即动态链接器)不同,分别是/lib/ld-linux.so.3和/lib/ld-linux-armhf.so.3。由于根文件系统基于ubuntu-base-16.04-core-armhf.tar.gz构建 ,该基础包包含的解释器程序是/lib/ld-linux-armhf.so.3,而不是/lib/ld-linux.so.3,导致系统在执行 perf 程序时,找不到需要的解释器程序/lib/ld-linux.so.3,从而输出 No such file or directory 提示信息。在将编译器由arm-linux-gnueabi-gcc(通过 apt 安装的用来编译内核的编译器),更换为arm-linux-gnueabihf-gcc后,运行如下命令序列
cd tools # 切换到内核代码 tools 目录
make perf_clean # 清空 perf 目录下旧的编译文件,或 make clean 清空 tools 的所有子目录
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- perf # 编译 perf
重新编译 perf 后,我们看到程序可以正常运行了:
$ perf
usage: perf [--version] [--help] [OPTIONS] COMMAND [ARGS]
...
$ file `which perf`
/usr/bin/perf: ELF 32-bit LSB executable, ARM, EABI5 version 1 (SYSV), dynamically linked, interpreter /lib/ld-linux-armhf.so.3, for GNU/Linux 2.6.16, BuildID[sha1]=fa482c30f51a3b34231c9a6011681a7bb43679bd, not stripped
问题已经解决了,但我还是从代码层面进一步分析了出错的原因。我们是从 bash 启动 perf 程序的,而 bash 启动程序的过程是通过系统调用fork() + execve() 发起的。在我们的场景下,报错代码路径执行的简要流程如下:
sys_execve()
do_execve(getname(filename), argv, envp)
...
/* 打开程序文件 /usr/bin/perf */
file = do_open_execat(fd, filename, flags);
...
/* 执行程序 /usr/bin/perf */
exec_binprm(bprm)
...
load_elf_binary() /* 执行 ELF 格式程序 */
...
/* 读取ELF文件的所有phdr */
elf_phdata = load_elf_phdrs(&loc->elf_ex, bprm->file);
...
for (i = 0; i < loc->elf_ex.e_phnum; i++) {
if (elf_ppnt->p_type == PT_INTERP) { /* 解释器程序路径, 如 /lib/ld-linux-armhf.so.3 */
...
/* 如果找不到解释器程序,将返回错误码 ENOENT,对应的错误信息为 No such file or directory */
interpreter = open_exec(elf_interpreter);
retval = PTR_ERR(interpreter);
if (IS_ERR(interpreter))
goto out_free_interp;
}
...
}
当然,在 sys_execve() 系统调用的代码路径中,并非只有一处返回 ENOENT错误码,在这里是通过 ftrace 来确认在内核代码哪一处返回ENOENT的。以下是确认上述调用路径的操作步骤:
# cd /sys/kernel/debug/tracing
# echo 0 > tracing_on
# echo > trace
# echo function_graph > current_tracer
# echo do_execveat_common > set_graph_function
# echo 4 > max_graph_depth
# echo 1 > tracing_on
# perf
# echo 0 > tracing_on
# cat trace
cat trace 命令用来查看 ftrace 追踪记录,我们截取 perf 执行过程追踪部分如下:
# tracer: function_graph
#
# CPU DURATION FUNCTION CALLS
# | | | | | | |
2) | do_execveat_common() {
2) | unshare_files() {
2) 2.917 us | unshare_fd();
2) + 20.791 us | }
2) + 12.500 us | kmem_cache_alloc_trace();
2) | prepare_bprm_creds() {
2) 3.250 us | mutex_lock_interruptible();
2) | prepare_exec_creds() {
2) + 17.834 us | prepare_creds();
2) + 46.042 us | }
2) + 89.666 us | }
2) 2.875 us | _raw_spin_lock();
2) | do_open_execat() {
2) | do_filp_open() {
2) ! 125.542 us | path_openat();
2) 3.500 us | restore_nameidata();
2) ! 174.833 us | }
2) 2.792 us | __fsnotify_parent();
2) 3.625 us | fsnotify();
2) ! 233.875 us | }
2) | sched_exec() {
2) 5.709 us | _raw_spin_lock_irqsave();
2) + 30.708 us | select_task_rq_fair();
2) 3.917 us | _raw_spin_unlock_irqrestore();
2) ! 105.833 us | }
2) | mm_alloc() {
2) 4.375 us | kmem_cache_alloc();
2) | mm_init() {
2) 1.541 us | __init_rwsem();
2) + 68.000 us | pgd_alloc();
2) ! 105.708 us | }
2) ! 141.500 us | }
2) 5.000 us | kmem_cache_alloc();
2) 3.125 us | down_write_killable();
2) 2.458 us | vm_get_page_prot();
2) | insert_vm_struct() {
2) | cap_vm_enough_memory() {
2) 3.291 us | cap_capable();
2) + 17.708 us | }
2) 4.292 us | __vm_enough_memory();
2) | vma_link() {
2) 1.042 us | __vma_link_list();
2) 5.083 us | __vma_link_rb();
2) 0.833 us | __vma_link_file();
2) + 26.875 us | }
2) ! 494.417 us | }
2) 1.084 us | up_write();
2) | count.constprop.4() {
2) + 13.625 us | get_user_arg_ptr();
2) 1.416 us | _cond_resched();
2) 9.334 us | get_user_arg_ptr();
2) + 63.291 us | }
2) | count.constprop.4() {
2) | get_user_arg_ptr() {
2) + 25.542 us | do_page_fault();
2) + 68.500 us | }
2) 1.083 us | _cond_resched();
2) 9.083 us | get_user_arg_ptr();
2) 0.958 us | _cond_resched();
2) 8.916 us | get_user_arg_ptr();
2) 0.958 us | _cond_resched();
2) 8.916 us | get_user_arg_ptr();
2) 0.917 us | _cond_resched();
2) 8.750 us | get_user_arg_ptr();
2) 0.959 us | _cond_resched();
2) 8.833 us | get_user_arg_ptr();
2) 0.959 us | _cond_resched();
2) 8.875 us | get_user_arg_ptr();
2) 0.916 us | _cond_resched();
2) 8.792 us | get_user_arg_ptr();
2) 0.916 us | _cond_resched();
2) 8.792 us | get_user_arg_ptr();
2) 0.917 us | _cond_resched();
2) + 35.958 us | get_user_arg_ptr();
2) + 28.083 us | _cond_resched();
2) + 19.833 us | get_user_arg_ptr();
2) 1.917 us | _cond_resched();
2) + 43.042 us | get_user_arg_ptr();
2) 1.875 us | _cond_resched();
2) + 16.500 us | get_user_arg_ptr();
2) 1.708 us | _cond_resched();
2) + 16.208 us | get_user_arg_ptr();
2) 2.334 us | _cond_resched();
2) + 18.333 us | get_user_arg_ptr();
2) ! 850.917 us | }
2) | prepare_binprm() {
2) 3.416 us | mnt_may_suid();
2) | cap_bprm_set_creds() {
2) 1.500 us | mnt_may_suid();
2) + 11.542 us | get_vfs_caps_from_disk();
2) + 46.334 us | }
2) | kernel_read() {
2) + 64.667 us | vfs_read();
2) + 81.792 us | }
2) ! 174.084 us | }
2) | copy_strings_kernel() {
2) | copy_strings() {
2) + 16.916 us | get_user_arg_ptr();
2) 2.667 us | _cond_resched();
2) ! 120.792 us | get_user_pages_remote();
2) 2.625 us | acct_arg_size();
2) 1.458 us | flush_cache_page();
2) 1.625 us | flush_kernel_dcache_page();
2) 1.375 us | put_arg_page();
2) ! 413.833 us | }
2) ! 423.500 us | }
2) | copy_strings() {
2) 9.583 us | get_user_arg_ptr();
2) 1.375 us | _cond_resched();
2) | get_user_pages_remote() {
2) + 13.209 us | __get_user_pages();
2) + 20.666 us | }
2) 0.875 us | acct_arg_size();
2) 0.792 us | flush_cache_page();
2) 9.292 us | get_user_arg_ptr();
2) | do_page_fault() {
2) 1.458 us | down_read_trylock();
2) 2.875 us | find_vma();
2) + 12.875 us | handle_mm_fault();
2) 0.959 us | up_read();
2) + 46.542 us | }
2) 1.333 us | _cond_resched();
2) 9.125 us | get_user_arg_ptr();
2) 1.292 us | _cond_resched();
2) 9.083 us | get_user_arg_ptr();
2) 1.250 us | _cond_resched();
2) 9.083 us | get_user_arg_ptr();
2) 1.250 us | _cond_resched();
2) 9.084 us | get_user_arg_ptr();
2) | do_page_fault() {
2) 1.125 us | down_read_trylock();
2) 2.334 us | find_vma();
2) 9.042 us | handle_mm_fault();
2) 0.875 us | up_read();
2) + 42.917 us | }
2) 1.375 us | _cond_resched();
2) 9.167 us | get_user_arg_ptr();
2) 1.250 us | _cond_resched();
2) 9.042 us | get_user_arg_ptr();
2) | do_page_fault() {
2) 1.167 us | down_read_trylock();
2) 2.292 us | find_vma();
2) 8.750 us | handle_mm_fault();
2) 0.833 us | up_read();
2) + 43.375 us | }
2) ==========> |
2) | gic_handle_irq() {
2) ! 151.958 us | __handle_domain_irq();
2) ! 170.791 us | }
2) <========== |
2) 1.625 us | _cond_resched();
2) + 10.167 us | get_user_arg_ptr();
2) 1.625 us | _cond_resched();
2) + 17.500 us | get_user_arg_ptr();
2) 2.292 us | _cond_resched();
2) + 16.625 us | get_user_arg_ptr();
2) 2.250 us | _cond_resched();
2) + 16.542 us | get_user_arg_ptr();
2) 2.208 us | _cond_resched();
2) + 16.500 us | get_user_arg_ptr();
2) 2.250 us | _cond_resched();
2) + 16.541 us | get_user_arg_ptr();
2) | do_page_fault() {
2) 3.042 us | down_read_trylock();
2) 5.209 us | find_vma();
2) + 22.166 us | handle_mm_fault();
2) 1.583 us | up_read();
2) + 92.500 us | }
2) 2.458 us | _cond_resched();
2) 2.584 us | flush_kernel_dcache_page();
2) 2.459 us | put_arg_page();
2) # 2887.709 us | }
2) | copy_strings() {
2) + 25.167 us | get_user_arg_ptr();
2) 1.334 us | _cond_resched();
2) | get_user_pages_remote() {
2) + 16.250 us | __get_user_pages();
2) + 24.042 us | }
2) 0.791 us | acct_arg_size();
2) 0.792 us | flush_cache_page();
2) 1.291 us | flush_kernel_dcache_page();
2) 1.042 us | put_arg_page();
2) ! 147.708 us | }
2) | would_dump() {
2) | inode_permission() {
2) 2.916 us | __inode_permission();
2) 9.792 us | }
2) + 17.042 us | }
2) 2.000 us | task_active_pid_ns();
2) 1.792 us | __task_pid_nr_ns();
2) | search_binary_handler() {
2) 1.417 us | _raw_read_lock();
2) 1.500 us | try_module_get();
2) 1.458 us | load_script();
2) 0.833 us | _raw_read_lock();
2) 0.833 us | module_put();
2) 0.833 us | try_module_get();
2) | load_elf_binary() {
2) 3.000 us | kmem_cache_alloc_trace();
2) 1.250 us | elf_check_arch();
2) + 42.041 us | load_elf_phdrs();
2) 2.916 us | __kmalloc();
2) + 13.625 us | kernel_read();
2) + 59.125 us | open_exec();
2) 3.417 us | kfree();
2) 1.458 us | kfree();
2) 1.333 us | kfree();
2) ! 192.750 us | }
2) 1.042 us | _raw_read_lock();
2) 0.791 us | module_put();
2) ! 257.292 us | }
2) 1.208 us | acct_arg_size();
通过查看上面的追踪记录,同时结合内核代码,我们可以判定,load_elf_binary() 调用终止于如下代码路径:
load_elf_binary()
loc = kmalloc(sizeof(*loc), GFP_KERNEL);
...
elf_phdata = load_elf_phdrs(&loc->elf_ex, bprm->file);
...
for (i = 0; i < loc->elf_ex.e_phnum; i++) {
if (elf_ppnt->p_type == PT_INTERP) {
elf_interpreter = kmalloc(elf_ppnt->p_filesz, GFP_KERNEL);
...
kernel_read(bprm->file, elf_interpreter, elf_ppnt->p_filesz, &pos);
interpreter = open_exec(elf_interpreter); /* 找不到动态链接器文件,返回 ENOENT */
retval = PTR_ERR(interpreter);
if (IS_ERR(interpreter))
goto out_free_interp; /* 出错了,从这里退出了 load_elf_binary() 函数 */
/* 如果没有出错,would_dump() 会出现在追踪记录中 */
would_dump(bprm, interpreter);
...
}
}
...
out_free_interp: /* 我们的场景中,出错后跳转到这里 */
kfree(elf_interpreter);
out_free_ph:
kfree(elf_phdata);
goto out;
ftrace 的配置和使用方法在这里不作展开,读者可自行查阅相关资料。
在确认文件/usr/bin/perf存在的情形下,仍然提示No such file or directory ,确实让人有些困惑,但我们相信,事出必有因,逐步分析,抽丝剥茧,真相总会浮出水面,而在这个过程中,我们终有所得。
现实中,存在更多文件存在,但不能运行的情景,如程序路径不对、远程传送拷贝导致程序不完整、运行为其它架构编译的程序等等。
我们经常看到类似如下的编译器
arm-none-eabi-gcc
arm-linux-gnueabi-gcc
arm-linux-gnueabihf-gcc
让人不知所措,很是迷茫。下面一个简单的小结,可以帮助我们理解它们。
交叉编译工具链的命名规则为:arch[-vendor][-os][-(gnu)eabi]
arch: 体系架构,如ARM,MIPS
vendor: 工具链提供商
os: 目标操作系统,如 linux
eabi: 嵌入式应用二进制接口(Embedded Application Binary Interface)
根据对操作系统的支持与否,ARM GCC可分为支持和不支持操作系统,如arm-none-eabi,这个是没有操作系统的,自然不可能支持那些跟操作系统关系密切的函数,比如fork(2),它使用的是newlib这个专用于嵌入式系统的C库。而arm-none-linux-eabi,是用于Linux的,使用glibc。
总的来说,我对ruby还比较陌生,我正在为我正在创建的对象编写一些rspec测试用例。许多测试用例都非常基础,我只是想确保正确填充和返回值。我想知道是否有办法使用循环结构来执行此操作。不必为我要测试的每个方法都设置一个assertEquals。例如:describeitem,"TestingtheItem"doit"willhaveanullvaluetostart"doitem=Item.new#HereIcoulddotheitem.name.shouldbe_nil#thenIcoulddoitem.category.shouldbe_nilendend但我想要一些方法来使用
我有一个Ruby程序,它使用rubyzip压缩XML文件的目录树。gem。我的问题是文件开始变得很重,我想提高压缩级别,因为压缩时间不是问题。我在rubyzipdocumentation中找不到一种为创建的ZIP文件指定压缩级别的方法。有人知道如何更改此设置吗?是否有另一个允许指定压缩级别的Ruby库? 最佳答案 这是我通过查看rubyzip内部创建的代码。level=Zlib::BEST_COMPRESSIONZip::ZipOutputStream.open(zip_file)do|zip|Dir.glob("**/*")d
我试图在一个项目中使用rake,如果我把所有东西都放到Rakefile中,它会很大并且很难读取/找到东西,所以我试着将每个命名空间放在lib/rake中它自己的文件中,我添加了这个到我的rake文件的顶部:Dir['#{File.dirname(__FILE__)}/lib/rake/*.rake'].map{|f|requiref}它加载文件没问题,但没有任务。我现在只有一个.rake文件作为测试,名为“servers.rake”,它看起来像这样:namespace:serverdotask:testdoputs"test"endend所以当我运行rakeserver:testid时
我的目标是转换表单输入,例如“100兆字节”或“1GB”,并将其转换为我可以存储在数据库中的文件大小(以千字节为单位)。目前,我有这个:defquota_convert@regex=/([0-9]+)(.*)s/@sizes=%w{kilobytemegabytegigabyte}m=self.quota.match(@regex)if@sizes.include?m[2]eval("self.quota=#{m[1]}.#{m[2]}")endend这有效,但前提是输入是倍数(“gigabytes”,而不是“gigabyte”)并且由于使用了eval看起来疯狂不安全。所以,功能正常,
Rails2.3可以选择随时使用RouteSet#add_configuration_file添加更多路由。是否可以在Rails3项目中做同样的事情? 最佳答案 在config/application.rb中:config.paths.config.routes在Rails3.2(也可能是Rails3.1)中,使用:config.paths["config/routes"] 关于ruby-on-rails-Rails3中的多个路由文件,我们在StackOverflow上找到一个类似的问题
我在使用omniauth/openid时遇到了一些麻烦。在尝试进行身份验证时,我在日志中发现了这一点:OpenID::FetchingError:Errorfetchinghttps://www.google.com/accounts/o8/.well-known/host-meta?hd=profiles.google.com%2Fmy_username:undefinedmethod`io'fornil:NilClass重要的是undefinedmethodio'fornil:NilClass来自openid/fetchers.rb,在下面的代码片段中:moduleNetclass
对于具有离线功能的智能手机应用程序,我正在为Xml文件创建单向文本同步。我希望我的服务器将增量/差异(例如GNU差异补丁)发送到目标设备。这是计划:Time=0Server:hasversion_1ofXmlfile(~800kiB)Client:hasversion_1ofXmlfile(~800kiB)Time=1Server:hasversion_1andversion_2ofXmlfile(each~800kiB)computesdeltaoftheseversions(=patch)(~10kiB)sendspatchtoClient(~10kiBtransferred)Cl
我正在寻找执行以下操作的正确语法(在Perl、Shell或Ruby中):#variabletoaccessthedatalinesappendedasafileEND_OF_SCRIPT_MARKERrawdatastartshereanditcontinues. 最佳答案 Perl用__DATA__做这个:#!/usr/bin/perlusestrict;usewarnings;while(){print;}__DATA__Texttoprintgoeshere 关于ruby-如何将脚
使用带有Rails插件的vim,您可以创建一个迁移文件,然后一次性打开该文件吗?textmate也可以这样吗? 最佳答案 你可以使用rails.vim然后做类似的事情::Rgeneratemigratonadd_foo_to_bar插件将打开迁移生成的文件,这正是您想要的。我不能代表textmate。 关于ruby-使用VimRails,您可以创建一个新的迁移文件并一次性打开它吗?,我们在StackOverflow上找到一个类似的问题: https://sta
在选择我想要运行操作的频率时,唯一的选项是“每天”、“每小时”和“每10分钟”。谢谢!我想为我的Rails3.1应用程序运行调度程序。 最佳答案 这不是一个优雅的解决方案,但您可以安排它每天运行,并在实际开始工作之前检查日期是否为当月的第一天。 关于ruby-如何每月在Heroku运行一次Scheduler插件?,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.com/questions/8692687/