当系统调用不存在……

2010年4月7日 | 分类: Programming | 标签: , ,
最近在一个项目上卡了几天,整理出来主要碰到的问题,以备自己查阅。

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

有一堆分层的目录,里面全是系统调用的相关测试代码+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大致如下:

1
2
3
4
5
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,禁止编译会出错的测试用例

1
2
3
4
5
6
7
8
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包含了一系列预处理,例如:

1
2
3
4
5
/* Define to 1 if you have the <sys/signalfd.h> header file. */
#undef HAVE_SYS_SIGNALFD_H
 
/* Define to 1 if you have the <sys/stat.h> header file. */
#define HAVE_SYS_STAT_H 1

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

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

1
2
3
4
5
6
7
8
9
#ifdef HAVE_SYS_SIGNALFD_H
...
#else
int main()
{
  printf("syscall not exists in this platform\");
  return 1;
}
#endif

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

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#!/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

目前还没有任何评论.