Ant-Veil

Caspar Blog

当系统调用不存在……

| Comments

最近在一个项目上卡了几天,整理出来主要碰到的问题,以备自己查阅。

具体的问题抽象出来就是这么一种情况:

有一堆分层的目录,里面全是系统调用的相关测试代码+Makefile,结构大致如下:

├── wait4
│   ├── Makefile
│   ├── wait401.c
│   └── wait402.c
├── waitid
│   ├── waitid01
│   │   ├── Makefile
│   │   └── waitid01.c
│   ├── waitid02
│   │   ├── Makefile
│   │   └── waitid02.c
├── waitpid
│   │   ├── Makefile
│   │   └── waitpid_err_test.c
.....

现在要把这堆系统调用的测试代码移植到各硬件平台和各发行版上,因此要考虑内核版本、glibc 版本等等问题。比如说 getcpu 这个东东只在 2.6.19 及以后版本中存在并且只有 x86_64 and i386 架构才可用。而编译这堆系统调用使用的 Makefile 大致如下:

MAKEFILES_FOR_TESTCASES = $(shell find testcases -name Makefile)
TESTCASES_BY_MAKE = $(addsuffix /test,$(dir $(MAKEFILES_FOR_TESTCASES)))

$(TESTCASES_BY_MAKE):
$(MAKE) -C $(dir $@) test

因此可能的解决方案有如下几种:

1. 修改 Makefile,禁止编译会出错的测试用例

FILTER_OUT_CASES = testcase1 testcase2 testcase3
ALL_MAKEFILES = $(shell find testcases -name Makefile)
FILTER_OUT_MAKEFILES = $(wildcard $(foreach filename,$(FILTER_OUT_CASES),testcases/$(filename)/Makefile))
MAKEFILES_FOR_TESTCASES = $(filter-out $(FILTER_OUT_MAKEFILES),$(ALL_MAKEFILES))
TESTCASES_BY_MAKE = $(addsuffix /test,$(dir $(MAKEFILES_FOR_TESTCASES)))

$(TESTCASES_BY_MAKE):
$(MAKE) -C $(dir $@) test

代码很平淡无奇,之所以贴出这段代码是为了温习一下 Makefile 中 wildcard,filter-out 和 foreach 的用法 :-)

2.修改 c 代码,使用预处理判断是否存在

这种办法在ltp上应用十分广泛,ltp 中自动从 configure 文件中生成 include/config.h,生成的 config.h 包含了一系列预处理,例如:

/* Define to 1 if you have the  header file. */
#undef HAVE_SYS_SIGNALFD_H

/* Define to 1 if you have the header file. */
#define HAVE_SYS_STAT_H 1

当内核版本不同导致系统调用不存在时,可以设置 undef 预处理;如果系统调用存在,则 define 一下。

接下去可以修改源代码,在可能会在不同内核版本上出现分歧的代码前后加上:

#ifdef HAVE_SYS_SIGNALFD_H
...
#else
int main()
{
  printf("syscall not exists in this platform\");
  return 1;
}
#endif

同时 ltp 还在 C 代码中判断内核版本,内核版本在中有声明。

如果不使用 configure 文件,可以自己写一个 shell 脚本来生成,例如:

#!/bin/sh

CONFIG_PATH=./include/config.h
KVER=`uname -r | cut -d'-' -f 1`
KMAJVER=`echo $KVER | cut -d'.' -f 1-2`
KMINVER=`echo $KVER | cut -d'.' -f 3`

if [ "$KMAJVER" = "2.6" ] && [ $KMINVER -ge 19 ];
then
echo '#define HAVE_SYS_EPOLL_H 1' >> $CONFIG_PATH
else
echo '#ifdef HAVE_SYS_EPOLL_H' >> $CONFIG_PATH
echo '# undef HAVE_SYS_EPOLL_H' >> $CONFIG_PATH
echo '#endif' >> $CONFIG_PATH
fi

Comments