系统调用机制
一、什么是系统调用
- 用户态到内核态的"官方桥梁"
- 受保护:用户不能直接访问硬件或内核数据
- 比库函数慢(涉及上下文切换 / 模式切换)
- POSIX 标准接口
二、调用流程
应用 ──> 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);
六、错误处理
- 系统调用返回 -1 时设置
errno
perror() 打印人话
strerror(errno) 取字符串
-EAGAIN / -EINTR 经常需要重试
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 |
文件不存在 |