系统调用的封装例程

Posted kaluotee

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了系统调用的封装例程相关的知识,希望对你有一定的参考价值。



原文 http://blog.chinaunix.net/uid-27033491-id-3249801.html

1,什么是封装例程?
        系统调用是内核实现的,内核向用户态提供服务,内核向用户态提供系统调用的接口是一个软中断。
也就是int 0x80.如果用户直接调用系统调用会很麻烦。下面就举个直接调用系统调用的例子。用汇编实现的。
  1. # hello.s ----> intel汇编的注释用的; 而ATT用的#
  2. # display a string "Hello, world."

  3. .section .rodata
  4. msg:
  5. .ascii "Hello, world.\\n"

  6. .section .text
  7. .globl _start
  8. _start:

  9. movl $2, %eax #调用fork系统调用
  10. int $0x80


  11. movl $4, %eax # system call 系统调用号(sys_write)
  12. movl $1, %ebx # file descriptor 参数一:文件描述符(stdout)
  13. movl $msg, %ecx # string address 参数二:要显示的字符串
  14. movl $14, %edx # string length 参数三:字符串长度
  15. int $0x80 # 调用内核功能

  16. movl $1, %eax # 系统调用号(sys_exit)
  17. movl $0, %ebx # 参数一:退出代码
  18. int $0x80 # 调用内核功能
这就需要对系统调用进行封装,便于用户调用,比如说fork(),getpid(),这些函数都是libc库中的函数
都对系统调用进行了封装。而且都是遵循POSIX标准的。
---------------------------------------------------------------------------------------------
2,什么是POSIX?
      POSIX是一套操作系统标准,它是随着UNIX标准化而诞生的。linux是遵循POSIX标准的操作系统,如果
linux遵循POSIX标准的话,UNIX上的应用程序就可以顺利移植到linux上来了。形象点说也就是linux和UNIX
很多函数名字及其参数都定义的是一样的。
---------------------------------------------------------------------------------------------
3,旧方式封装(淘汰了),__syscall0()  ~ __syscall6()
     这几个宏实际上是利用了系统调用参数的个数不能超个6个来进行分类的。为了内核的安全考虑。从内核2.6.18开始该宏就从头文件中删除了,其实完全也可以自己加入到 指定的头文件中去。该宏被syscall()取代了,syscall()是glibc中的函数。而上面的_syscall()宏是不依赖于库的。
  1. #define __syscall_return(type, res) \\
  2. do \\
  3. if ((unsigned long)(res) >= (unsigned long)(-MAX_ERRNO)) \\
  4. res = -1; \\
  5. \\
  6. return (type) (res); \\
  7. while (0)

  8. #define _syscall0(type,name) \\
  9. type name(void) \\
  10. \\
  11. long __res; \\
  12. __asm__ volatile ("int $0x80" \\
  13. : "=a" (__res) \\
  14. : "0" (__NR_##name)); \\
  15. __syscall_return(type,__res); \\

  16. #define _syscall1(type,name,type1,arg1) \\
  17. type name(type1 arg1) \\
  18. \\
  19. long __res; \\
  20. __asm__ volatile ("push %%ebx ; movl %2,%%ebx ; int $0x80 ; pop %%ebx" \\
  21. : "=a" (__res) \\
  22. : "0" (__NR_##name),"ri" ((long)(arg1)) : "memory"); \\
  23. __syscall_return(type,__res); \\

  24. #define _syscall2(type,name,type1,arg1,type2,arg2) \\
  25. type name(type1 arg1,type2 arg2) \\
  26. \\
  27. long __res; \\
  28. __asm__ volatile ("push %%ebx ; movl %2,%%ebx ; int $0x80 ; pop %%ebx" \\
  29. : "=a" (__res) \\
  30. : "0" (__NR_##name),"ri" ((long)(arg1)),"c" ((long)(arg2)) \\
  31. : "memory"); \\
  32. __syscall_return(type,__res); \\

  33. #define _syscall3(type,name,type1,arg1,type2,arg2,type3,arg3) \\
  34. type name(type1 arg1,type2 arg2,type3 arg3) \\
  35. \\
  36. long __res; \\
  37. __asm__ volatile ("push %%ebx ; movl %2,%%ebx ; int $0x80 ; pop %%ebx" \\
  38. : "=a" (__res) \\
  39. : "0" (__NR_##name),"ri" ((long)(arg1)),"c" ((long)(arg2)), \\
  40. "d" ((long)(arg3)) : "memory"); \\
  41. __syscall_return(type,__res); \\

  42. #define _syscall4(type,name,type1,arg1,type2,arg2,type3,arg3,type4,arg4) \\
  43. type name (type1 arg1, type2 arg2, type3 arg3, type4 arg4) \\
  44. \\
  45. long __res; \\
  46. __asm__ volatile ("push %%ebx ; movl %2,%%ebx ; int $0x80 ; pop %%ebx" \\
  47. : "=a" (__res) \\
  48. : "0" (__NR_##name),"ri" ((long)(arg1)),"c" ((long)(arg2)), \\
  49. "d" ((long)(arg3)),"S" ((long)(arg4)) : "memory"); \\
  50. __syscall_return(type,__res); \\

  51. #define _syscall5(type,name,type1,arg1,type2,arg2,type3,arg3,type4,arg4, \\
  52. type5,arg5) \\
  53. type name (type1 arg1,type2 arg2,type3 arg3,type4 arg4,type5 arg5) \\
  54. \\
  55. long __res; \\
  56. __asm__ volatile ("push %%ebx ; movl %2,%%ebx ; movl %1,%%eax ; " \\
  57. "int $0x80 ; pop %%ebx" \\
  58. : "=a" (__res) \\
  59. : "i" (__NR_##name),"ri" ((long)(arg1)),"c" ((long)(arg2)), \\
  60. "d" ((long)(arg3)),"S" ((long)(arg4)),"D" ((long)(arg5)) \\
  61. : "memory"); \\
  62. __syscall_return(type,__res); \\

  63. #define _syscall6(type,name,type1,arg1,type2,arg2,type3,arg3,type4,arg4, \\
  64. type5,arg5,type6,arg6) \\
  65. type name (type1 arg1,type2 arg2,type3 arg3,type4 arg4,type5 arg5,type6 arg6) \\
  66. \\
  67. long __res; \\
  68. struct long __a1; long __a6; __s = (long)arg1, (long)arg6 ; \\
  69. __asm__ volatile ("push %%ebp ; push %%ebx ; movl 4(%2),%%ebp ; " \\
  70. "movl 0(%2),%%ebx ; movl %1,%%eax ; int $0x80 ; " \\
  71. "pop %%ebx ; pop %%ebp" \\
  72. : "=a" (__res) \\
  73. : "i" (__NR_##name),"0" ((long)(&__s)),"c" ((long)(arg2)), \\
  74. "d" ((long)(arg3)),"S" ((long)(arg4)),"D" ((long)(arg5)) \\
  75. : "memory"); \\
  76. __syscall_return(type,__res); \\
---------------------------------------------------------------------------------------------
4,新方式封装。(现在的封装方式)
现在对系统调用的封装都是用的syscall函数,该函数是不定参数,很好用,可以调用所有的系统调用。

  1.   1 #include <linux/unistd.h>
  2.   2 #include <syscall.h>
  3.   3 #include <sys/types.h>
  4.   4 #include <stdio.h>
  5.   6
  6.   7
  7.   8 int main(void)
  8.   9
  9.  10 long pid1;
  10.  11 pid1 = syscall(SYS_getpid); //getpid
  11.  12 printf("pid = %ld\\n",pid1);
  12.  13 return 0;
  13.  14
---------------------------------------------------------------------------------------------
5,问题:我自己添加了一个系统调用,想在用户态下测试下,代码如下:

  1.   1 #include <linux/unistd.h>
  2.   2 #include <syscall.h>
  3.   3 #include <sys/types.h>
  4.   4 #include <stdio.h>
  5.   5
  6.   6
  7.   7 int main(void)
  8.   8
  9.   9 long n = 0;
  10.  10 n = syscall(SYS_mysyscall,190);
  11.  11 return 0;
  12.  12
该程序会提示“SYS_mysyscall”没有定义。
解决办法:
1,/usr/include/bits/syscall.h文件中添加:#define SYS_mysyscall __NR_mysyscall
2,/usr/include/i386-linux-gnu/asm/unistd_32文件末尾添加:
      #define __NR_mysyscall          345
      这样,你添加的系统调用和系统本身自带的系统调用的用户态使用上就一模一样了。
---------------------------------------------------------------------------------------------

以上是关于系统调用的封装例程的主要内容,如果未能解决你的问题,请参考以下文章

基于x86-64 Linux-5.0.1的Socket与系统调用深度分析

存储例程

Linux系统调用过程分析

Socket与系统调用深度分析

Socket与系统调用深度分析

Socket与系统调用深度分析