浅尝辄止23-Linux系统调用1

接着浅尝辄止21-Linux系统调用0,接着寻找pwrite的系统调用之路,线索就在浅尝辄止22-C语言属性-alias。

pwrite的多个身份

执行git grep "alias.*pwrite",只关注Linux ARM32相关的就可以看到下面的关键信息。

sysdeps/unix/sysv/linux/pwrite.c:strong_alias (__libc_pwrite, __pwrite)
sysdeps/unix/sysv/linux/pwrite.c:weak_alias (__libc_pwrite, pwrite)

在libc-symbols.h中可以找到如下信息,两种alias都是产生别名而已,只是其中一个是weak symbol。因此,我们可以知道__libc_pwrite,__pwritepwrite这些名字实际上是一回事。

/* Define ALIASNAME as a strong alias for NAME.  */
# define strong_alias(name, aliasname) _strong_alias(name, aliasname)
# define _strong_alias(name, aliasname) \
  extern __typeof (name) aliasname __attribute__ ((alias (#name)));
...
/* Define ALIASNAME as a weak alias for NAME.
   If weak aliases are not available, this defines a strong alias.  */
# define weak_alias(name, aliasname) _weak_alias (name, aliasname)
# define _weak_alias(name, aliasname) \
  extern __typeof (name) aliasname __attribute__ ((weak, alias (#name)));

pwrite的实现

进入pwrite.c看一下,pwrite的实现就靠一个宏SYSCALL_CANCEL

ssize_t
__libc_pwrite (int fd, const void *buf, size_t count, off_t offset)
{
  return SYSCALL_CANCEL (pwrite, fd, buf, count, SYSCALL_LL_PRW (offset));
}

SYSCALL_CANCEL的展开

根据libc-symbols.h和sysdep.h中的定义,可以展开如下

SYSCALL_CANCEL (pwrite, fd, buf, count, SYSCALL_LL_PRW (offset))
=SYSCALL_CANCEL (pwrite, fd, buf, count, offset)
展开并去掉不关心的内容=>
INLINE_SYSCALL_CALL (pwrite, fd, buf, count, offset);
=>
__INLINE_SYSCALL_DISP (__INLINE_SYSCALL, pwrite, fd, buf, count, offset)
=>
__SYSCALL_CONCAT (__INLINE_SYSCALL,__INLINE_SYSCALL_NARGS(pwrite, fd, buf, count, offset))(pwrite, fd, buf, count, offset)
=>
__SYSCALL_CONCAT_X (__INLINE_SYSCALL, __INLINE_SYSCALL_NARGS(pwrite, fd, buf, count, offset))(pwrite, fd, buf, count, offset)
=>
__INLINE_SYSCALL__INLINE_SYSCALL_NARGS(pwrite, fd, buf, count, offset))(pwrite, fd, buf, count, offset)
=>
__INLINE_SYSCALL__INTERNAL_SYSCALL_NARGS_X (pwrite, fd, buf, count, offset,7,6,5,4,3,2,1,0,)(pwrite, fd, buf, count, offset)
=>
__INLINE_SYSCALL4(pwrite, fd, buf, count, offset)
=>
INLINE_SYSCALL (pwrite, 4, fd, buf, count, offset)
展开并去掉不关心的内容=>
INTERNAL_SYSCALL (pwrite, , 4, fd, buf, count, offset);
=>
INTERNAL_SYSCALL_RAW(SYS_ify(pwrite),  , 4, fd, buf, count, offset)
=>
INTERNAL_SYSCALL_RAW(__NR_pwrite,  , 4, fd, buf, count, offset)
=>
({                              \
       register int _a1 asm ("r0"), _nr asm ("r7");     \
       LOAD_ARGS_4 (fd, buf, count, offset)                 \
       _nr = __NR_pwrite;                       \
       asm volatile ("swi   0x0 @ syscall " "__NR_pwrite"   \
             : "=r" (_a1)               \
             : "r" (_nr) ASM_ARGS_4         \
             : "memory");               \
       _a1; })
=>
({                              \
       register int _a1 asm ("r0"), _nr asm ("r7");     \
       int _a4tmp = (int) (offset);         \
       int _a3tmp = (int) (count);            \
       int _a2tmp = (int) (buf);          \
       int _a1tmp = (int) (fd);         \
       _a1 = _a1tmp;                \
       register int _a2 asm ("buf") = _a2tmp;                \
       register int _a3 asm ("count") = _a3tmp;            \
       register int _a4 asm ("offset") = _a4tmp;                    \
       _nr = __NR_pwrite;                       \
       asm volatile ("swi   0x0 @ syscall " "__NR_pwrite"   \
             : "=r" (_a1)               \
             : "r" (_nr), "r" (_a1), "r" (_a2), "r" (_a3), "r" (_a4)            \
             : "memory");               \
       _a1; })

最后这一大坨就是Linux ARM32的pwrite在glibc里面的最终实现了,下面逐行注释解说

//将{}用()包起来,这个括号就成了一个表达式,值就是最后面的表达式的值,即_a1
({                              \
//声明2个int型变量
//_a1用r0寄存器,用来传递系统调用的第一个参数
//_nr用r7寄存器,用来传递著名的系统调用号
       register int _a1 asm ("r0"), _nr asm ("r7");     \
//依次声明4个变量,分别将4个参数赋给它们
       int _a4tmp = (int) (offset);         \
       int _a3tmp = (int) (count);            \
       int _a2tmp = (int) (buf);          \
       int _a1tmp = (int) (fd);         \
//将第一个参数赋给寄存器r0
       _a1 = _a1tmp;                \
//将参数依次赋给寄存器,会顺次分配r1,r2,r3寄存器
       register int _a2 asm ("buf") = _a2tmp;                \
       register int _a3 asm ("count") = _a3tmp;            \
       register int _a4 asm ("offset") = _a4tmp;                    \
//将系统调用号__NR_pwrite给_nr
       _nr = __NR_pwrite;                       \
//以下四句gcc内联汇编
//swi 0x0,产生软件中断,这条指令就会触发内核的中断处理,从这条指令到内核中断处理,就是所谓的陷入内核过程了
//"=r" (_a1) 是将内联汇编的输出结果存到_a1,即系统调用的返回值
// "r" (_nr), "r" (_a1), "r" (_a2), "r" (_a3), "r" (_a4) 是输入参数列表,会通过r7,r0,r1,r2,r3传递
// "memory"是被更改的资源,即内存有可能会被改变
       asm volatile ("swi   0x0 @ syscall " "__NR_pwrite"   \
             : "=r" (_a1)               \
             : "r" (_nr), "r" (_a1), "r" (_a2), "r" (_a3), "r" (_a4)            \
             : "memory");               \
//内联汇编的输出结果会存到_a1,这就是这整个({...})表达式的值,即系统调用返回值
       _a1; })

__NR_pwrite是哪来的?

你可能感兴趣的:(浅尝辄止23-Linux系统调用1)