目录

  • ​​man查看类型​​
  • ​​基础说明​​
  • ​​glibc库函数的范例​​
  • ​​系统调用的范例​​
  • ​​系统调用查看​​
  • ​​查看内核代码的方法​​
  • ​​内核中的系统调用函数的查找方法​​
  • ​​范例一:socket的系统调用​​
  • ​​范例二:kill的系统调用实现​​
  • ​​glibc库文件源码查看​​
  • ​​glibc版本查看​​
  • ​​源码查看​​
  • ​​参考​​

man查看类型

基础说明

man man

Linux-C中libc函数以及系统调用函数查看_系统调用


如上所示:

  • 左上角有1表示为linux命令/可执行文件;
  • 左上角有2表示为系统调用(内核提供);
  • 左上角有3表示为库函数(一般为glibc提供);

glibc库函数的范例

man 3 printf
man 3 printf 表示直接看printf的库函数;如果man printf,则默认是printf(1),即printf命令;如下所示:

Linux-C中libc函数以及系统调用函数查看_系统调用_02


Linux-C中libc函数以及系统调用函数查看_搜索_03


如上所示:

  • 左上角的3表示是库函数;
  • <stdio.h> 以及 <stdlib.h> 头文件也说明是库函数;

系统调用的范例

man getpid

Linux-C中libc函数以及系统调用函数查看_bc_04


如上所示:

  • 函数什么在头文件 <sys/types.h> 中,说明是系统调用;
  • 左上角的getpid(2)中的2也说明是系统调用;

系统调用查看

系统调用的函数实现,一般是在内核中实现。

查看内核代码的方法

  • 下载对应内核版本(uname -r)的代码进行查看;
    【https://github.com/torvalds/linux : github linux 代码】
  • 选择对应的内核版本,在线查看、搜索源码
    【https://elixir.bootlin.com/linux/v4.18/C/ident/ : 在线查看、搜索源码】

    如上所示,还可以在线查看dpdk, glibc等的源码;

内核中的系统调用函数的查找方法

  • Tips 1: 用户空间的方法xxx,对应系统调用层方法则是sys_xxx;
  • Tips 2: unistd.h文件记录着系统调用中断号的信息。
    故用户空间kill方法则对应系统调用层便是sys_kill,这个方法去哪里找呢?从/kernel/include/uapi/asm-generic/unistd.h等还有很多unistd.h去慢慢查看,查看关键字sys_kill,便能看到下面几行:

/* kernel/signal.c */ __SYSCALL(__NR_kill, sys_kill)

  • Tips 3: 宏定义SYSCALL_DEFINEx(xxx,…),展开后对应的方法则是sys_xxx;
    SYSCALL_DEFINEx 中的 x 表示系统调用的参数个数;
    比如 SYSCALL_DEFINE, SYSCALL_DEFINE1, SYSCALL_DEFINE2;

实用技巧
根据参数个数,以及系统调用的名称,直接搜索即可,比如 kill 函数,存在2个参数,直接搜索:SYSCALL_DEFINE2(kill, 即可直接找到。

Linux-C中libc函数以及系统调用函数查看_搜索_05

Linux-C中libc函数以及系统调用函数查看_搜索_06


如上所示:如果是在线搜索,则直接基于SYSCALL_DEFINE2(kill,来进行搜索,则搜索不到,因为没有这样的标签,且不能基于字符串来搜索。


此时的方法为:


1.先查看kill的man定义,判断可能的函数定义位于哪个文件。此中猜测在signal.c文件中;


2.基于SYSCALL_DEFINE2来搜索,然后过滤signal文件;


3.进入到signal.c文件中,在基于SYSCALL_DEFINE2(kill来过滤。

SYSCALL_DEFINE的定义,如下:

#define SYSCALL_DEFINE0(name)    asmlinkage long sys_##name(void)
#define SYSCALL_DEFINE1(name, ...) SYSCALL_DEFINEx(1, _##name, __VA_ARGS__)
#define SYSCALL_DEFINE2(name, ...) SYSCALL_DEFINEx(2, _##name, __VA_ARGS__)
#define SYSCALL_DEFINE3(name, ...) SYSCALL_DEFINEx(3, _##name, __VA_ARGS__)
#define SYSCALL_DEFINE4(name, ...) SYSCALL_DEFINEx(4, _##name, __VA_ARGS__)
#define SYSCALL_DEFINE5(name, ...) SYSCALL_DEFINEx(5, _##name, __VA_ARGS__)
#define SYSCALL_DEFINE6(name, ...) SYSCALL_DEFINEx(6, _##name, __VA_ARGS__)

#define SYSCALL_DEFINEx(x, name, ...) \
asmlinkage long sys##name(__SC_DECL##x(__VA_ARGS__)); \
static inline long SYSC##name(__SC_DECL##x(__VA_ARGS__)); \
asmlinkage long SyS##name(__SC_LONG##x(__VA_ARGS__)) \
{ \
__SC_TEST##x(__VA_ARGS__); \
return (long) SYSC##name(__SC_CAST##x(__VA_ARGS__)); \
} \
SYSCALL_ALIAS(sys##name, SyS##name); \
static inline long SYSC##name(__SC_DECL##x(__VA_ARGS__))

##: 表示连接字符串;
__VA_ARGS__代表前面...里面的可变参数

范例一:socket的系统调用

原形:int socket(int domain, int type, int protocol);
存在3个参数,SYSCALL_DEFINEx里面的x代表的是系统调用参数个数;
所以为:
SYSCALL_DEFINE3(socket, int, family, int, type, int, protocol)

展开为:
SYSCALL_DEFINEx(3, _socket, int, family, int, type, int, protocol)

再次展开为:
asmlinkage long sys_socket(__SC_DECL3(int, family, int, type, int, protocol)); \
static inline long SYSC_socket(__SC_DECL3(int, family, int, type, int, protocol)); \
asmlinkage long SyS_socket(__SC_LONG3(int, family, int, type, int, protocol)) \
{ \
__SC_TEST3(int, family, int, type, int, protocol); \
return (long) SYSC_socket(__SC_CAST3(int, family, int, type, int, protocol)); \
} \
SYSCALL_ALIAS(sys_socket, SyS_socket); \
static inline long SYSC_sockt(__SC_DECL3(int, family, int, type, int, protocol))

第一行熟悉的sys_socket了。这只是一个函数声明,不是定义;
定义其实在最后一行,结尾没有加分号,下面再加上一对大括号,就是定义。

范例二:kill的系统调用实现

kill 函数的系统调用:kernel/signal.c
SYSCALL_DEFINE2(kill, pid_t, pid, int, sig)
{
struct siginfo info;
clear_siginfo(&info);
info.si_signo = sig;
info.si_errno = 0;
info.si_code = SI_USER;
info.si_pid = task_tgid_vnr(current);
info.si_uid = from_kuid_munged(current_user_ns(), current_uid());
return kill_something_info(sig, &info, pid);
}

glibc库文件源码查看

glibc版本查看

  • 方法一:ldd --version
  • 方法二:通过 libc.so 获取版本号
  1. 首先查找到 libc 库的位置
    如何找到 GLIBC 库, 有多种方法:

方法一: ldd 一个当前系统中 C 库编写的动态可执行程序; ldd `which top` | grep "libc.so" libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f6e32226000) 方法二:从正在运行的程序,查看其打开的文件中查看; lsof -p $$ | grep "libc-" 方法三:GNU_LIBC_VERSION 标记 GLIBC 版本号的变量为 GNU_LIBC_VERSION, 直接查看它即可; #getconf GNU_LIBC_VERSION glibc 2.17 方法四:函数中获取glibc版本信息; gnu_get_libc_version 和 gnu_get_libc_release 就是这样的内置信息, 我们可以通过 man 手册来获取详细信息. 方法五:查看发行版安装包版本 发行版打包的软件包一般都是有版本号后缀的, 因此查看我们发行版安装的 GLIBC 包的名字, 就可以知道版本号. Centos 可以使用 rpm -q glibc / yum list installed |grep glibc 查看对应软件包的名称及版本.