系统调用机制

一、什么是系统调用

二、调用流程

应用 ──> glibc 封装 ──> syscall 指令 / swi
              ↓
       用户态 ──> 内核态
              ↓
       sys_call_table[__NR_xxx] 查表
              ↓
       内核实现
              ↓
       返回用户态

三、查看系统调用

1. man 手册

man 2 open    # 第 2 节 = 系统调用
man 3 fopen   # 第 3 节 = 库函数

2. strace 跟踪

strace ls                  # 看所有调用
strace -e open,read ls     # 只看指定调用
strace -p PID              # attach 进程
strace -f -o log.txt cmd   # 跟踪子进程
strace -c cmd              # 统计

3. ltrace 跟踪库调用

四、典型系统调用分类

1. 进程

fork()                  // 创建子进程
vfork()                 // 共享地址空间(少用)
execve(path, argv, envp)// 加载新程序
_exit(status)           // 立即退出(不调用 atexit)
wait(status)            // 等待子进程
waitpid(pid, status, options)
getpid() / getppid()
getuid() / geteuid() / setuid()
kill(pid, sig)          // 发信号

2. 文件 I/O

open(path, flags, mode)
openat(dirfd, path, flags, mode)
close(fd)
read(fd, buf, count)
write(fd, buf, count)
pread/pwrite             // 带偏移,不更新文件指针
lseek(fd, offset, whence)
fcntl(fd, cmd, arg)      // 改属性
ioctl(fd, cmd, arg)      // 设备特定
dup(fd) / dup2(o, n) / dup3
pipe(fds[2])             // 匿名管道
ftruncate(fd, len)

3. 内存

brk(addr)                 // 调整堆顶
mmap(addr, len, prot, flags, fd, off)
mprotect(addr, len, prot) // 改页权限
munmap(addr, len)
madvise(addr, len, advice)
mlock/munlock             // 锁内存

4. 信号

signal(sig, handler)
sigaction(sig, act, oldact)
sigprocmask(how, set, old)
kill(pid, sig)
raise(sig)
pause()
sigwait / sigtimedwait

5. 时间

time(t)              // 秒
gettimeofday(tv, tz) // 微秒
clock_gettime(clk, tp) // 纳秒,推荐
nanosleep(req, rem)
clock_getres(clk, res)
alarm(seconds)
setitimer(which, val, old)
timer_create / timer_settime

6. 网络

socket(domain, type, proto)
bind / listen / accept / connect
send / recv / sendto / recvfrom
select / poll / epoll_*

7. 进程间通信

pipe / socketpair
msgget / msgsnd / msgrcv / msgctl    // System V 消息队列
semget / semop / semctl              // System V 信号量
shmget / shmat / shmdt / shmctl      // System V 共享内存
mq_open / mq_send / ...              // POSIX 消息队列
sem_open / sem_wait / ...            // POSIX 信号量
shm_open / mmap                      // POSIX 共享内存

// inotify
inotify_init / inotify_add_watch
// eventfd / timerfd / signalfd

8. 用户 / 组

getuid / setuid / getgid / setgid
getgroups / setgroups
getrlimit / setrlimit               // 资源限制
getrusage(who, usage)               // 资源使用

9. 扩展(非标准,但常见)

eventfd / signalfd / timerfd
prctl / seccomp / capabilities
clone3 / close_range / memfd_create / io_uring_setup

五、直接使用 syscall

// glibc 未封装或想看 raw syscall
#include <sys/syscall.h>
#include <unistd.h>

long ret = syscall(SYS_getpid);
long fd  = syscall(SYS_openat, AT_FDCWD, "/dev/null", O_RDONLY);

六、错误处理

ssize_t n;
do {
    n = read(fd, buf, sizeof(buf));
} while (n == -1 && errno == EINTR);
if (n == -1) { perror("read"); }

七、踩坑清单

问题 原因
EAGAIN 出现 非阻塞 IO 暂时无数据
EINTR 被信号打断,处理或重试
ENOMEM 内存不够
EFAULT buf 指针无效
EBADF fd 无效
EPERM 权限不足
ENOENT 文件不存在