Linux kernel network stack study notes (a) - Analysis glibc2.30 the socket function call

glibc\sysdeps\unix\sysv\linux\socket.c

This file defines a socket function

int
__socket (int fd, int type, int domain)
{
#ifdef __ASSUME_SOCKET_SYSCALL
  return INLINE_SYSCALL (socket, 3, fd, type, domain);
#else
  return SOCKETCALL (socket, fd, type, domain);
#endif
}
libc_hidden_def (__socket)
weak_alias (__socket, socket) //给__socket起了个别名 socket

First we give an example socket calls are:

socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);

linux network protocol stack from BSD socket layer outset entirely part of the kernel, so linux network programming interface almost nothing has been done at the application layer, for example, the socket function is called, only glibc provides an interface to a system call, the lower step follow SOCKETCALL macro definitions to analyze the operating mode (of course at mach the system, socket calls and functions of linux big difference in system works, but only focus on the analysis of linux network protocol stack)

\glibc\sysdeps\unix\sysv\linux\socketcall.h

#define SOCKETCALL(name, args...)					\
  ({									\
    long int sc_ret = __SOCKETCALL (SOCKOP_##name, args);		\
    sc_ret;								\
  })

Found in the code block and call another __SOCKETCALL (SOCKOP _ ## name, args ) and returns sc_ret
continue to follow up __SOCKETCALL will find a range of definitions following

#define __SOCKETCALL_NARGS_X(a,b,c,d,e,f,g,h,n,...) n
#define __SOCKETCALL_NARGS(...) \
  __SOCKETCALL_NARGS_X (__VA_ARGS__,7,6,5,4,3,2,1,0,)
#define __SOCKETCALL_CONCAT_X(a,b)     a##b
#define __SOCKETCALL_CONCAT(a,b)       __SOCKETCALL_CONCAT_X (a, b)
#define __SOCKETCALL_DISP(b,...) \
  __SOCKETCALL_CONCAT (b,__SOCKETCALL_NARGS(__VA_ARGS__))(__VA_ARGS__)

#define __SOCKETCALL(...) __SOCKETCALL_DISP (__SOCKETCALL, __VA_ARGS__)

#define __SOCKETCALL1(name, a1) \
  INLINE_SYSCALL (socketcall, 2, name, \
     ((long int [1]) { (long int) (a1) }))
#define __SOCKETCALL2(name, a1, a2) \
  INLINE_SYSCALL (socketcall, 2, name, \
     ((long int [2]) { (long int) (a1), (long int) (a2) }))
#define __SOCKETCALL3(name, a1, a2, a3) \
  INLINE_SYSCALL (socketcall, 2, name, \
     ((long int [3]) { (long int) (a1), (long int) (a2), (long int) (a3) }))
#define __SOCKETCALL4(name, a1, a2, a3, a4) \
  INLINE_SYSCALL (socketcall, 2, name, \
     ((long int [4]) { (long int) (a1), (long int) (a2), (long int) (a3), \
                       (long int) (a4) }))
#define __SOCKETCALL5(name, a1, a2, a3, a4, a5) \
  INLINE_SYSCALL (socketcall, 2, name, \
     ((long int [5]) { (long int) (a1), (long int) (a2), (long int) (a3), \
                       (long int) (a4), (long int) (a5) }))
#define __SOCKETCALL6(name, a1, a2, a3, a4, a5, a6) \
  INLINE_SYSCALL (socketcall, 2, name, \
     ((long int [6]) { (long int) (a1), (long int) (a2), (long int) (a3), \
                       (long int) (a4), (long int) (a5), (long int) (a6) }))

所以翻译__SOCKETCALL(SOCKOP_#name, args)可以得到:(按步骤)
1.__SOCKETCALL_DISP (__SOCKETCALL, SOCKOP__socket,args)
2.__SOCKETCALL_CONCAT (__SOCKETCALL,__SOCKETCALL_NARGS( SOCKOP__socket,fd, type, domain))( SOCKOP#name,args)
3.翻译__SOCKETCALL_NARGS
__SOCKETCALL_CONCAT (__SOCKETCALL,__SOCKETCALL_NARGS_X (SOCKOP__socket,fd, type, domain,7,6,5,4,3,2,1,0,))( SOCKOP__socket,fd, type, domain)
4.翻译__SOCKETCALL_NARGS_X
__SOCKETCALL_CONCAT (__SOCKETCALL,3)( SOCKOP__socket,fd, type, domain)
5.__SOCKETCALL_CONCAT_X (__SOCKETCALL,3)( SOCKOP__socket,fd, type, domain)
6.翻译__SOCKETCALL_CONCAT_X
__SOCKETCALL3(SOCKOP_socket,fd,type,domain)

This time our case on behalf of the
socket (AF_INET, SOCK_STREAM, IPPROTO_TCP) ;
where:

#define AF_INET PF_INET
#define PF_INET 2

enum __socket_type
{
	SOCK_STREAM = 1,
#define SOCK_STREAM SOCK_STREAM
	...
}

enum {
	IPPROTO_TCP = 6,
#define IPPROTO_TCP IPPROTO_TCP
	...
}

#define SOCKOP_socket 1

These definitions are better does not provide the source code to find a location

Finally obtained
__SOCKETCALL3 (1,2,1,6)
and substituting

#define __SOCKETCALL3(name, a1, a2, a3) \
  INLINE_SYSCALL (socketcall, 2, name, \
     ((long int [3]) { (long int) (a1), (long int) (a2), (long int) (a3) }))

Last translation is:

INLINE_SYSCALL(socketcall,2,1,((long int [3]){(long int)(2),(long int)(1),(long int)(6)}))

Do not forget this step is SOCKETCALL come from, that is:

int
__socket (int fd, int type, int domain)
{
#ifdef __ASSUME_SOCKET_SYSCALL
  return INLINE_SYSCALL (socket, 3, fd, type, domain);
#else
  return INLINE_SYSCALL(socketcall,2,1,((long int [3]){(long int)(2),(long int)(1),(long int)(6)}))
#endif
}

Now the application layer socket become like
us to follow up INLINE_SYSCALL, to this step, it is more nearly away from the system call

glibc\sysdeps\unix\sysv\linux\i386\sysdep.h

#undef INLINE_SYSCALL
#if IS_IN (libc)
# define INLINE_SYSCALL(name, nr, args...) \
  ({									      \
    unsigned int resultvar = INTERNAL_SYSCALL (name, , nr, args);	      \
    __glibc_unlikely (INTERNAL_SYSCALL_ERROR_P (resultvar, ))		      \
    ? __syscall_error (-INTERNAL_SYSCALL_ERRNO (resultvar, ))		      \
    : (int) resultvar; })
#else
# define INLINE_SYSCALL(name, nr, args...) \
  ({									      \
    unsigned int resultvar = INTERNAL_SYSCALL (name, , nr, args);	      \
    if (__glibc_unlikely (INTERNAL_SYSCALL_ERROR_P (resultvar, )))	      \
      {									      \
	__set_errno (INTERNAL_SYSCALL_ERRNO (resultvar, ));		      \
	resultvar = 0xffffffff;						      \
      }									      \
    (int) resultvar; })
#endif

For #if SI_IN (libc)
The official explanation is this: to detect what component is being compiled

Because the two definitions is not much difference, but all borrowed INTERNAL_SYSCALL, and returns the value of the macro definition
so that it is in accordance with the above macro to analyze

Now the application layer socket a long way:

int
__socket (int fd, int type, int domain)
{
#ifdef __ASSUME_SOCKET_SYSCALL
  return INLINE_SYSCALL (socket, 3, fd, type, domain);
#else
  return INTERNAL_SYSCALL (socketcall, , 2, 1,((long int [3]){(long int)(2),(long int)(1),(long int)(6)}))#endif
}

Analysis INTERNAL_SYSCALL

\glibc\sysdeps\unix\sysv\linux\i386

#define INTERNAL_SYSCALL(name, err, nr, args...) \
  ({									      \
    register unsigned int resultvar;					      \
    INTERNAL_SYSCALL_MAIN_##nr (name, err, args);			      \
    (int) resultvar; })

Let's retain the macro look inside the first translation INTERNAL_SYSCALL_MAIN _ ## nr (name, err , args)
namely:
INTERNAL_SYSCALL_MAIN_2 (socketcall, 1, ((Long int [3]) {(Long int) (2), (Long int ) (1), (long int ) (6)})
to continue:
#define INTERNAL_SYSCALL_MAIN_2 (name, ERR, args ...)
INTERNAL_SYSCALL_MAIN_INLINE (name, ERR, 2, args)
to give:
INTERNAL_SYSCALL_MAIN_INLINE (the socketcall, 2,. 1, ((Long int) {(long int) ( 2), (long int) (1), (long int) (6)}) [3]
so in this case like function block is:

({									      \
    register unsigned int resultvar;					      \
    INTERNAL_SYSCALL_MAIN_INLINE(socketcall,, 2, 1,((long int [3]){(long int)(2),(long int)(1),(long int)(6)});			      \
    (int) resultvar; })

Let us now look at sort:
now the application layer so long:

int
__socket (int fd, int type, int domain)
{
#ifdef __ASSUME_SOCKET_SYSCALL
  return INLINE_SYSCALL (socket, 3, fd, type, domain);
#else
  register unsigned int resultvar;
  INTERNAL_SYSCALL_MAIN_INLINE(socketcall,, 2, 1,((long int [3]){(long int)(2),(long int)(1),(long int)(6)});
  return (int)resultvar;
#endif
}

Continue to analyze INTERNAL_SYSCALL_MAIN_INLINE:

\glibc\sysdeps\unix\sysv\linux\i386\sysdep.h

#if I386_USE_SYSENTER
# ifdef OPTIMIZE_FOR_GCC_5
#  ifdef PIC
#   define INTERNAL_SYSCALL_MAIN_INLINE(name, err, nr, args...) \
    LOADREGS_##nr(args)							\
    asm volatile (							\
    "call *%%gs:%P2"							\
    : "=a" (resultvar)							\
    : "a" (__NR_##name), "i" (offsetof (tcbhead_t, sysinfo))		\
      ASMARGS_##nr(args) : "memory", "cc")
#   define INTERNAL_SYSCALL_NCS(name, err, nr, args...) \
  ({									\
    register unsigned int resultvar;					\
    LOADREGS_##nr(args)							\
    asm volatile (							\
    "call *%%gs:%P2"							\
    : "=a" (resultvar)							\
    : "a" (name), "i" (offsetof (tcbhead_t, sysinfo))			\
      ASMARGS_##nr(args) : "memory", "cc");				\
    (int) resultvar; })
#  else
#   define INTERNAL_SYSCALL_MAIN_INLINE(name, err, nr, args...) \
    LOADREGS_##nr(args)							\
    asm volatile (							\
    "call *_dl_sysinfo"							\
    : "=a" (resultvar)							\
    : "a" (__NR_##name) ASMARGS_##nr(args) : "memory", "cc")
#   define INTERNAL_SYSCALL_NCS(name, err, nr, args...) \
  ({									\
    register unsigned int resultvar;					\
    LOADREGS_##nr(args)							\
    asm volatile (							\
    "call *_dl_sysinfo"							\
    : "=a" (resultvar)							\
    : "a" (name) ASMARGS_##nr(args) : "memory", "cc");			\
    (int) resultvar; })
#  endif
# else /* GCC 5  */
#  ifdef PIC
#   define INTERNAL_SYSCALL_MAIN_INLINE(name, err, nr, args...) \
    EXTRAVAR_##nr							      \
    asm volatile (							      \
    LOADARGS_##nr							      \
    "movl %1, %%eax\n\t"						      \
    "call *%%gs:%P2\n\t"						      \
    RESTOREARGS_##nr							      \
    : "=a" (resultvar)							      \
    : "i" (__NR_##name), "i" (offsetof (tcbhead_t, sysinfo))		      \
      ASMFMT_##nr(args) : "memory", "cc")
#   define INTERNAL_SYSCALL_NCS(name, err, nr, args...) \
  ({									      \
    register unsigned int resultvar;					      \
    EXTRAVAR_##nr							      \
    asm volatile (							      \
    LOADARGS_##nr							      \
    "call *%%gs:%P2\n\t"						      \
    RESTOREARGS_##nr							      \
    : "=a" (resultvar)							      \
    : "0" (name), "i" (offsetof (tcbhead_t, sysinfo))			      \
      ASMFMT_##nr(args) : "memory", "cc");				      \
    (int) resultvar; })
#  else
#   define INTERNAL_SYSCALL_MAIN_INLINE(name, err, nr, args...) \
    EXTRAVAR_##nr							      \
    asm volatile (							      \
    LOADARGS_##nr							      \
    "movl %1, %%eax\n\t"						      \
    "call *_dl_sysinfo\n\t"						      \
    RESTOREARGS_##nr							      \
    : "=a" (resultvar)							      \
    : "i" (__NR_##name) ASMFMT_##nr(args) : "memory", "cc")
#   define INTERNAL_SYSCALL_NCS(name, err, nr, args...) \
  ({									      \
    register unsigned int resultvar;					      \
    EXTRAVAR_##nr							      \
    asm volatile (							      \
    LOADARGS_##nr							      \
    "call *_dl_sysinfo\n\t"						      \
    RESTOREARGS_##nr							      \
    : "=a" (resultvar)							      \
    : "0" (name) ASMFMT_##nr(args) : "memory", "cc");			      \
    (int) resultvar; })
#  endif
# endif /* GCC 5  */
#else
# ifdef OPTIMIZE_FOR_GCC_5
#  define INTERNAL_SYSCALL_MAIN_INLINE(name, err, nr, args...) \
    LOADREGS_##nr(args)							\
    asm volatile (							\
    "int $0x80"								\
    : "=a" (resultvar)							\
    : "a" (__NR_##name) ASMARGS_##nr(args) : "memory", "cc")
#  define INTERNAL_SYSCALL_NCS(name, err, nr, args...) \
  ({									\
    register unsigned int resultvar;					\
    LOADREGS_##nr(args)							\
    asm volatile (							\
    "int $0x80"								\
    : "=a" (resultvar)							\
    : "a" (name) ASMARGS_##nr(args) : "memory", "cc");			\
    (int) resultvar; })
# else /* GCC 5  */
#  define INTERNAL_SYSCALL_MAIN_INLINE(name, err, nr, args...) \
    EXTRAVAR_##nr							      \
    asm volatile (							      \
    LOADARGS_##nr							      \
    "movl %1, %%eax\n\t"						      \
    "int $0x80\n\t"							      \
    RESTOREARGS_##nr							      \
    : "=a" (resultvar)							      \
    : "i" (__NR_##name) ASMFMT_##nr(args) : "memory", "cc")
#  define INTERNAL_SYSCALL_NCS(name, err, nr, args...) \
  ({									      \
    register unsigned int resultvar;					      \
    EXTRAVAR_##nr							      \
    asm volatile (							      \
    LOADARGS_##nr							      \
    "int $0x80\n\t"							      \
    RESTOREARGS_##nr							      \
    : "=a" (resultvar)							      \
    : "0" (name) ASMFMT_##nr(args) : "memory", "cc");			      \
    (int) resultvar; })
# endif /* GCC 5  */
#endif

Here you can see there are many sides to this macro definition
emmmmm I do not know which one should be. . .
So I write to you first

But it is certain that the macro definition is nothing but the assembly code into the kernel software interrupt instruction, use of sysenter go into the kernel, use of system calls int $ 0x80 into the kernel, the difference between the two is that the cpu use not the same resources, int $ 0x80 is caught by the kernel interrupt gate, the next one blog to explain (to open a new pit)

This blog is just the application layer of the socket macro definition translation function again, finally found his compilation system call section
can know the socket application layer interface does not make any substantive tasks, only the system calls package

to be continue

Released seven original articles · won praise 1 · views 703

Guess you like

Origin blog.csdn.net/qq_41957544/article/details/102528948