5.1.5 如何使用系统调用
如图5.2所示,用户应用可以通过两种方式使用系统调用。第一种方式是通过C库函数,包括系统调用在C库中的封装函数和其他普通函数。
图5.2 使用系统调用的两种方式 |
第二种方式是使用_syscall宏。2.6.18版本之前的内核,在include/asm-i386/unistd.h文件中定义有7个_syscall宏,分别是:
- _syscall0(type,name)
- _syscall1(type,name,type1,arg1)
- _syscall2(type,name,type1,arg1,type2,arg2)
- _syscall3(type,name,type1,arg1,type2,arg2,type3,arg3)
- _syscall4(type,name,type1,arg1,type2,arg2,type3,
arg3,type4,arg4) - _syscall5(type,name,type1,arg1,type2,arg2,type3,
arg3,type4,arg4,type5,arg5) - _syscall6(type,name,type1,arg1,type2,arg2,type3,
arg3,type4,arg4,type5,arg5,type6,arg6)
其中,type表示所生成系统调用的返回值类型,name表示该系统调用的名称,typeN、argN分别表示第N个参数的类型和名称,它们的数目和_syscall后面的数字一样大。这些宏的作用是创建名为name的函数,_syscall后面跟的数字指明了该函数的参数的个数。
比如sysinfo系统调用用于获取系统总体统计信息,使用_syscall宏定义为:
- _syscall1(int, sysinfo, struct sysinfo *, info);
展开后的形式为:
- int sysinfo(struct sysinfo * info)
- {
- long __res;
- __asm__ volatile("int $0x80" : "=a" (__res) : "0" (116),"b" ((long)(info)));
- do {
- if ((unsigned long)(__res) >= (unsigned long)(-(128 + 1))) {
- errno = -(__res);
- __res = -1;
- }
- return (int) (__res);
- } while (0);
- }
可以看出,_syscall1(int, sysinfo, struct sysinfo *, info)展开成一个名为sysinfo的函数,原参数int就是函数的返回类型,原参数struct sysinfo *和info分别构成新函数的参数。
在程序文件里使用_syscall宏定义需要的系统调用,就可以在接下来的代码中通过系统调用名称直接调用该系统调用。下面是一个使用sysinfo系统调用的实例。
代码清单5.1 sysinfo系统调用使用实例
- 00 #include <stdio.h>
- 01 #include <stdlib.h>
- 02 #include <errno.h>
- 03 #include <linux/unistd.h>
- 04 #include <linux/kernel.h> /* for struct sysinfo */
- 05
- 06 _syscall1(int, sysinfo, struct sysinfo *, info);
- 07
- 08 int main(void)
- 09 {
- 10 struct sysinfo s_info;
- 11 int error;
- 12
- 13 error = sysinfo(&s_info);
- 14 printf("code error = %d\n", error);
- 15 printf("Uptime = %lds\nLoad: 1 min %lu / 5 min %lu / 15 min %lu\n"
- 16 "RAM: total %lu / free %lu / shared %lu\n"
- 17 "Memory in buffers = %lu\nSwap: total %lu / free %lu\n"
- 18 "Number of processes = %d\n",
- 19 s_info.uptime, s_info.loads[0],
- 20 s_info.loads[1], s_info.loads[2],
- 21 s_info.totalram, s_info.freeram,
- 22 s_info.sharedram, s_info.bufferram,
- 23 s_info.totalswap, s_info.freeswap,
- 24 s_info.procs);
- 25 exit(EXIT_SUCCESS);
- 26 }
但是自2.6.19版本开始,_syscall宏被废除,我们需要使用syscall函数,通过指定系统调用号和一组参数来调用系统调用。
syscall函数原型为:
- int syscall(int number, ...);
其中number是系统调用号,number后面应顺序接上该系统调用的所有参数。下面是gettid系统调用的调用实例。
代码清单5.2 gettid系统调用使用实例
- 00 #include <unistd.h>
- 01 #include <sys/syscall.h>
- 02 #include <sys/types.h>
- 03
- 04 #define __NR_gettid 224
- 05
- 06 int main(int argc, char *argv[])
- 07 {
- 08 pid_t tid;
- 09
- 10 tid = syscall(__NR_gettid);
- 11 }
大部分系统调用都包括了一个SYS_符号常量来指定自己到系统调用号的映射,因此上面第10行可重写为:
- tid = syscall(SYS_gettid);