系统调用入门到精通:关键概念、原理剖析与实操演练

购买与学习资源



系统调用入门到精通:关键概念、原理剖析与实操演练

作者:嵌入式Jerry
视频教程请关注 B 站:“嵌入式 Jerry”


一、带着问题学习:系统调用到底是什么?有什么用?我们怎么用?

  1. 应用程序为啥不能直接操作硬件?
  2. 系统调用和普通函数有啥区别?为什么需要它?
  3. 系统调用到底做了什么,底层是怎么跑起来的?
  4. 作为驱动工程师,系统调用和我的驱动开发有什么关系?
  5. 我能不能动手体验一次“系统调用的全流程”?

二、什么是系统调用?为什么需要系统调用?

1. 概念简述

  • 系统调用(System Call)是用户程序请求操作系统核心服务(如文件操作、内存管理、进程控制等)的唯一正规通道
  • 作用:安全、受控地让应用访问内核资源和硬件,防止系统崩溃和恶意操作。

2. 核心本质

  • 系统调用是一种“受控异常”(Trap),是CPU提供的同步异常机制。
  • 不是普通函数调用,而是特权级切换:用户态 → 内核态。

三、系统调用和普通函数的本质区别

  • 普通函数只在当前权限级别(用户态)运行,不能越权。
  • 系统调用必须切换到内核态,由内核以最高权限帮你完成敏感操作。
  • 典型系统调用有:open、read、write、ioctl、fork、exec、mmap、close、socket 等。

在这里插入图片描述

四、系统调用工作流程全景图

1. 总体流程逻辑图

+--------------------+
|  应用程序代码      |
|  write(fd, ...)    |
+---------+----------+
          |
          v
+--------------------+
|  系统调用接口      |
|  (libc包装函数)    |
+---------+----------+
          |
          v
+--------------------+
|  Trap/异常指令     |
|  (如 svc/syscall)  |
+---------+----------+
          |
          v
+--------------------+
|  内核Trap入口      |
|  (异常向量表)      |
+---------+----------+
          |
          v
+--------------------+
|  系统调用分发      |
|  (sys_call_table)  |
+---------+----------+
          |
          v
+--------------------+
|  具体内核服务      |
|  (如 sys_write)    |
+---------+----------+
          |
          v
+--------------------+
|  VFS/驱动/文件系统 |
+--------------------+

五、通俗易懂的流程讲解

1. 用户空间

  • 你写的C代码:write(fd, buf, len);
  • 这个write其实是库函数,最终通过syscall或者类似机制触发系统调用指令

2. 系统调用Trap

  • CPU遇到系统调用指令(如 ARM 的 svc #0,x86 的 syscall),直接进入内核态,找到异常向量表的Trap入口。

3. 内核分发

  • 内核Trap入口(如 el0_sync),把系统调用号、参数等找出来。
  • 根据系统调用号查 sys_call_table,转到正确的内核实现函数,比如 sys_write()

4. 内核服务实现

  • sys_write()做参数校验、安全检查,然后调用VFS或具体驱动代码。
  • 最终调用到你驱动里实现的file_operations.write函数。

5. 结果返回

  • 操作完成后,把结果放回用户空间,程序继续执行。

六、驱动工程师视角:系统调用与驱动开发的关系

  • 用户空间对 /dev/xxx 节点进行 open/read/write/ioctl 等操作,实质都是系统调用
  • 内核VFS根据设备号找到你的驱动,把请求交给你在 file_operations 里实现的函数。
  • 你只需要实现 file_operations,VFS 和系统调用机制会帮你搞定用户到内核的所有中转。

七、实操演练:亲手体验系统调用全过程

1. 编写简单测试程序

#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
int main() {
    int fd = open("/dev/mydev", O_RDWR);
    if (fd < 0) {
        perror("open");
        return 1;
    }
    char buf[8] = "hello";
    write(fd, buf, 5);
    close(fd);
    return 0;
}

2. 编写简单字符设备驱动

#include <linux/module.h>
#include <linux/fs.h>
#include <linux/uaccess.h>

#define DEV_NAME "mydev"
static int major;

static ssize_t my_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos)
{
    char kbuf[16] = {0};
    copy_from_user(kbuf, buf, min(count, sizeof(kbuf)-1));
    printk("my_write called, data: %s\n", kbuf);
    return count;
}

static struct file_operations my_fops = {
    .owner = THIS_MODULE,
    .write = my_write,
};

static int __init my_init(void)
{
    major = register_chrdev(0, DEV_NAME, &my_fops);
    printk("mydev registered with major %d\n", major);
    return 0;
}

static void __exit my_exit(void)
{
    unregister_chrdev(major, DEV_NAME);
    printk("mydev unregistered\n");
}

module_init(my_init);
module_exit(my_exit);
MODULE_LICENSE("GPL");

3. 创建设备节点并测试

sudo insmod mydev.ko
sudo mknod /dev/mydev c [major号] 0
./your_test_program
# 查看 dmesg 输出,能看到 "my_write called, data: hello"

4. 用 strace 跟踪系统调用

strace ./your_test_program
  • 你会看到 open、write、close 等系统调用全过程。

八、核心知识点总结与问答

1. 系统调用是什么?

是应用程序请求内核完成敏感操作的唯一通道,通过Trap机制切换到内核态。

2. 为什么不能直接访问硬件?

为了操作系统的安全、稳定、资源隔离,必须受控访问。

3. 系统调用和普通函数的最大区别?

系统调用伴随特权切换,权限高,可以访问内核和硬件;普通函数不能。

4. VFS与file_operations的关系?

VFS负责中转,file_operations负责具体实现。你的驱动只要实现file_operations接口,系统调用的请求就会送达你这。

5. 怎么验证系统调用到了自己的驱动?

用printk打印、用strace跟踪应用、驱动里打断点。


九、一图看懂核心关系

应用程序调用write等
libc包装/系统调用指令
Trap/异常切换到内核
内核Trap入口
sys_call_table查找分发
sys_write等内核实现
VFS层
你的file_operations

十、结语

系统调用就是应用和内核/驱动之间唯一的高速通道。理解它的本质、掌握底层流程、动手验证、结合驱动开发,是你成为高级嵌入式/驱动工程师的必经之路!


购买与学习资源


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值