`
字符串
  • 浏览: 37924 次
文章分类
社区版块
存档分类
最新评论

Syscall系统调用Linux内核跟踪

 
阅读更多
在Linux的用户空间,我们经常会调用系统调用,下面我们跟踪一下read系统调用,使用的Linux内核版本为Linux2.6.37。不同的Linux版本其中的实现略有不同。
在一些应用中我们可以看到下面的一些定义:
#define real_read(fd, buf, count ) (syscall(SYS_read, (fd), (buf), (count)))
其实真正调用的还是系统函数syscall(SYS_read),也就是sys_read()函数中,在Linux2.6.37中的利用几个宏定义实现。
Linux 系统调用(SCI,system call interface)的实现机制实际上是一个多路汇聚以及分解的过程,该汇聚点就是 0x80 中断这个入口点(X86 系统结构)。也就是说,所有系统调用都从用户空间中汇聚到 0x80 中断点,同时保存具体的系统调用号。当 0x80 中断处理程序运行时,将根据系统调用号对不同的系统调用分别处理(调用不同的内核函数处理)。
引起系统调用的两种途径
(1)int $0×80 , 老式linux内核版本中引起系统调用的唯一方式
(2)sysenter汇编指令
在Linux内核中使用下面的宏进行系统调用
SYSCALL_DEFINE3(read, unsigned int, fd, char __user *, buf, size_t, count)
{
    struct file *file;
    ssize_t ret = -EBADF;
    int fput_needed;

    file = fget_light(fd, &fput_needed);
    if (file) {
        loff_t pos = file_pos_read(file);
        ret = vfs_read(file, buf, count, &pos);
        file_pos_write(file, pos);
        fput_light(file, fput_needed);
    }

    return ret;
}
其中SYSCALL_DEFINE3的宏定义如下:
#define SYSCALL_DEFINE3(name, ...) SYSCALL_DEFINEx(3, _##name, __VA_ARGS__)
##的意思就是宏中的字符直接替换,
如果name = read,那么在宏中__NR_##name就替换成了__NR_read了。 __NR_##name是系统调用号,##指的是两次宏展开.即用实际的系统调用名字代替"name",然后再把__NR_...展开.如name == ioctl,则为__NR_ioctl。

#ifdef CONFIG_FTRACE_SYSCALLS
#define SYSCALL_DEFINEx(x, sname, ...)                \
    static const char *types_##sname[] = {            \
        __SC_STR_TDECL##x(__VA_ARGS__)            \
    };                            \
    static const char *args_##sname[] = {            \
        __SC_STR_ADECL##x(__VA_ARGS__)            \
    };                            \
    SYSCALL_METADATA(sname, x);                \
    __SYSCALL_DEFINEx(x, sname, __VA_ARGS__)
#else
#define SYSCALL_DEFINEx(x, sname, ...)                \
    __SYSCALL_DEFINEx(x, sname, __VA_ARGS__)
#endif
不管是否定义CONFIG_FTRACE_SYSCALLS宏,最终都会执行 下面的这个宏定义:
__SYSCALL_DEFINEx(x, sname, __VA_ARGS__)
#ifdef CONFIG_HAVE_SYSCALL_WRAPPERS

#define SYSCALL_DEFINE(name) static inline long SYSC_##name

#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__))

#else /* CONFIG_HAVE_SYSCALL_WRAPPERS */

#define SYSCALL_DEFINE(name) asmlinkage long sys_##name
#define __SYSCALL_DEFINEx(x, name, ...)                    \
    asmlinkage long sys##name(__SC_DECL##x(__VA_ARGS__))

#endif /* CONFIG_HAVE_SYSCALL_WRAPPERS */
最终会调用下面类型的宏定义:
asmlinkage long sys##name(__SC_DECL##x(__VA_ARGS__))
也就是我们前面提到的sys_read()系统函数。
asmlinkage通知编译器仅从栈中提取该函数的参数。所有的系统调用都需要这个限定词!这和我们上一篇文章quagga中提到的宏定义,有异曲同工之妙。
也就是宏定义中的下面代码:
struct file *file;
    ssize_t ret = -EBADF;
    int fput_needed;

    file = fget_light(fd, &fput_needed);
    if (file) {
        loff_t pos = file_pos_read(file);
        ret = vfs_read(file, buf, count, &pos);
        file_pos_write(file, pos);
        fput_light(file, fput_needed);
    }

    return ret;
代码解析:
fget_light() :根据 fd 指定的索引,从当前进程描述符中取出相应的 file 对象(见图3)。
如果没找到指定的 file 对象,则返回错误
如果找到了指定的 file 对象:
调用 file_pos_read() 函数取出此次读写文件的当前位置。
调用 vfs_read() 执行文件读取操作,而这个函数最终调用 file->f_op.read() 指向的函数,代码如下:
if (file->f_op->read)
ret = file->f_op->read(file, buf, count, pos);
调用 file_pos_write() 更新文件的当前读写位置。
调用 fput_light() 更新文件的引用计数。
最后返回读取数据的字节数。
到此,虚拟文件系统层所做的处理就完成了,控制权交给了 ext2 文件系统层。
分享到:
评论

相关推荐

    my_syscall.zip_my_syscall_syscall_内核模块_系统调用

    2. **获取系统调用号**:由于系统调用号是有限的,并且已经被Linux内核的内置系统调用占用,因此你需要找到一个未被使用的系统调用号。这可能需要检查`arch/x86/entry/syscalls/syscall_32.tbl`或`arch/x86/entry/...

    linux2.4内核添加系统调用

    Linux 2.4 内核添加系统调用详解 Linux 操作系统的内核是操作系统的核心部分,负责管理计算机的硬件资源和提供系统服务。系统调用是操作系统提供给应用程序的接口,允许应用程序请求操作系统提供的服务。在 Linux ...

    向Linux内核增加一个系统调用【CentOS和Ubuntu版本】

    本实验旨在让学生深入理解系统调用的工作原理,并通过实践操作学习如何向Linux内核添加自定义的系统调用,以此扩展内核的功能。 首先,实验要求向Linux内核增加一个新的系统调用。系统调用的添加通常涉及以下几个...

    linux内核知识系列:系统调用

    Linux内核知识系列:系统调用 在计算机操作系统中,系统调用是用户程序与操作系统交互的主要手段。在Linux环境中,系统调用是用户空间进程请求操作系统服务的接口,允许程序执行特权操作,如文件I/O、进程管理、...

    为linux内核添加新的系统调用

    通过以上步骤,我们成功地为Linux内核版本3.4.4添加了一个名为`my_syscall`的新系统调用,并在用户态进行了测试验证。需要注意的是,这里仅为示例演示,实际操作时应根据具体需求调整代码实现细节。 ### 扩展知识点...

    linux2.6系统调用18内核升级

    3. **更新系统调用表**:在 `arch/i386/kernel/syscall_table.S` 文件中添加新的系统调用地址。 具体实现如下: ```c asmlinkage int sys_mycall(void) { printk(KERN_INFO "this is wulongping's first sys_call...

    linux内核详解 描述linux内核 方便调用linux内核

    为了更好地理解Linux内核,我们可以尝试添加一个新的系统调用。这涉及到修改`syscalls.h`以声明新调用,更新`syscall_32.tbl`或`syscall_64.tbl`(根据架构),实现新的函数并将其链接到`sys_call_table`。此外,...

    linux syscall系统调用获取线程PID.docx

    在早期的Linux内核(2.6.18之前),`gettid()`系统调用可以通过`_syscall0`宏定义来实现,如`_syscall0(pid_t, getTId)`,这会创建一个`pid_t gettid(void)`的函数,可以直接调用`gettid()`来获取TID。 然而,从...

    linux中添加系统调用

    在Linux操作系统中,系统调用是用户空间与内核空间交互的主要途径,它们提供了安全、高效的方法来执行底层操作,如文件I/O、进程管理、内存管理等。本主题将深入探讨如何在Linux 2.6.30版本中添加自定义的系统调用,...

    在Linux中添加新的系统调用

    在Linux系统中,添加一个新的系统调用涉及到对内核代码的修改、内核的重新编译以及新系统调用的功能测试等多个步骤。 #### 二、了解Linux系统调用机制 在深入探讨如何添加一个新系统调用之前,我们先来了解一下...

    Linux内核系统调用扩展研究.pdf

    【Linux内核系统调用扩展研究】 Linux内核系统调用是操作系统内核与用户空间交互的主要途径,它为用户提供了一种安全的方式去访问和利用内核提供的服务。系统调用是操作系统设计中的核心组成部分,它允许用户级别的...

    ubuntu9.04 linux内核编译和添加系统调用的实验报告

    在本实验报告中,我们将深入探讨如何在Ubuntu 9.04操作系统上编译Linux内核版本2.6.34,并添加自定义的系统调用。Linux内核是操作系统的核心部分,负责管理硬件资源、提供系统服务以及调度进程。系统调用是用户空间...

    linux 通过模块实现自己的系统调用

    要实现自己的系统调用,通常需要深入理解Linux内核的工作原理和编程。本教程将引导你了解如何通过内核模块来添加自定义的系统调用。 首先,我们来看“syscall_module”这个目录,它可能包含了一个内核模块的源代码...

    linux 系统调用视频讲解

    在Linux操作系统中,系统调用是用户空间程序与内核进行交互的重要机制。它们提供了标准接口,使得应用程序能够请求操作系统...视频教程`lesson01-system_call`将是一个很好的起点,带你逐步揭开Linux内核的神秘面纱。

    Linux内核详细操作demo

    - **系统调用调试**:通过strace或syscall接口,学习如何跟踪和分析系统调用,以了解程序与内核之间的交互。 - **内核配置和编译**:理解如何使用menuconfig或make命令定制内核,以满足项目特定需求。 - **内存...

    Linux内核设计与实现.pdf

    fork()函数是Linux内核中用于创建新进程的系统调用。在执行fork()函数时,父进程和子进程共享同一段代码空间,这给人的感觉好像是有两次返回。实际上,对于调用fork的父进程来说,如果fork出来的子进程没有得到调度...

Global site tag (gtag.js) - Google Analytics