0x00 前言
本文汇总下开发过程中收集的一些 Linux 系统调用及用法
0x01 常用系统调用分类
进程控制
fork:创建一个新进程clone:按指定条件创建子进程execve:运行可执行文件exit:中止进程_exit:立即中止当前进程getdtablesize:进程所能打开的最大文件数getpgid:获取指定进程组标识号setpgid:设置指定进程组标志号getpgrp:获取当前进程组标识号setpgrp:设置当前进程组标志号getpid:获取进程标识号getppid:获取父进程标识号getpriority:获取调度优先级setpriority:设置调度优先级modify_ldt:读写进程的本地描述表nanosleep:使进程睡眠指定的时间nice:改变分时进程的优先级pause:挂起进程,等待信号personality:设置进程运行域prctl:对进程进行特定操作ptrace:进程跟踪sched_get_priority_max:取得静态优先级的上限sched_get_priority_min:取得静态优先级的下限sched_getparam:取得进程的调度参数sched_getscheduler:取得指定进程的调度策略sched_rr_get_interval:取得按 RR 算法调度的实时进程的时间片长度sched_setparam:设置进程的调度参数sched_setscheduler:设置指定进程的调度策略和参数sched_yield:进程主动让出处理器, 并将自己等候调度队列队尾vfork:创建一个子进程,以供执行新程序,常与execve等同时使用wait:等待子进程终止wait3:参见waitwaitpid:等待指定子进程终止wait4:参见waitpidcapget:获取进程权限capset:设置进程权限getsid:获取会晤标识号setsid:设置会晤标识号
文件系统控制
1、文件读写操作
fcntl:文件控制open:打开文件creat:创建新文件close:关闭文件描述字read:读文件write:写文件readv:从文件读入数据到缓冲数组中writev:将缓冲数组里的数据写入文件pread:对文件随机读pwrite:对文件随机写lseek:移动文件指针_llseek:在64位地址空间里移动文件指针dup:复制已打开的文件描述字dup2:按指定条件复制文件描述字flock:文件加 / 解锁poll:I/O 多路转换truncate:截断文件ftruncate:参见truncateumask:设置文件权限掩码fsync:把文件在内存中的部分写回磁盘
2、文件系统操作
access:确定文件的可存取性chdir:改变当前工作目录fchdir:参见chdirchmod:改变文件方式fchmod:参见chmodchown:改变文件的属主或用户组fchown:参见chownlchown:参见chownchroot:改变根目录stat:取文件状态信息lstat:参见statfstat:参见statstatfs:取文件系统信息fstatfs:参见statfsreaddir:读取目录项getdents:读取目录项mkdir:创建目录mknod:创建索引节点rmdir:删除目录rename:文件改名link:创建链接symlink:创建符号链接unlink:删除链接readlink:读符号链接的值mount:安装文件系统umount:卸下文件系统ustat:取文件系统信息utime:改变文件的访问修改时间utimes:参见utimequotactl:控制磁盘配额
系统控制
ioctl:I/O 总控制函数_sysctl:读 / 写系统参数acct:启用或禁止进程记账getrlimit:获取系统资源上限setrlimit:设置系统资源上限getrusage:获取系统资源使用情况uselib:选择要使用的二进制函数库ioperm:设置端口 I/O 权限iopl:改变进程 I/O 权限级别outb:低级端口操作reboot:重新启动swapon:打开交换文件和设备swapoff:关闭交换文件和设备bdflush:控制bdflush守护进程sysfs:取核心支持的文件系统类型sysinfo: 取得系统信息adjtimex: 调整系统时钟alarm: 设置进程的闹钟getitimer: 获取计时器值setitimer: 设置计时器值gettimeofday: 取时间和时区settimeofday: 设置时间和时区stime: 设置系统日期和时间time: 取得系统时间times: 取进程运行时间uname: 获取当前 UNIX 系统的名称、版本和主机等信息vhangup: 挂起当前终端nfsservctl: 对 NFS 守护进程进行控制vm86: 进入模拟 8086 模式create_module: 创建可装载的模块项delete_module: 删除可装载的模块项init_module: 初始化模块query_module: 查询模块信息get_kernel_syms: 取得核心符号, 已被query_module代替
内存管理
brk: 改变数据段空间的分配sbrk: 参见brkmlock: 内存页面加锁munlock: 内存页面解锁mlockall: 调用进程所有内存页面加锁munlockall: 调用进程所有内存页面解锁mmap: 映射虚拟内存页munmap: 去除内存页映射mremap: 重新映射虚拟内存地址msync: 将映射内存中的数据写回磁盘mprotect: 设置内存映像保护getpagesize: 获取页面大小sync: 将内存缓冲区数据写回硬盘cacheflush: 将指定缓冲区中的内容写回磁盘
网络管理
getdomainname: 取域名setdomainname: 设置域名gethostid: 获取主机标识号sethostid: 设置主机标识号gethostname: 获取本主机名称sethostname: 设置主机名称
socket 控制
socketcall: socket 系统调用socket: 建立 socketbind: 绑定 socket 到端口connect: 连接远程主机accept: 响应 socket 连接请求send: 通过 socket 发送信息sendto: 发送 UDP 信息sendmsg: 参见sendrecv: 通过 socket 接收信息recvfrom: 接收 UDP 信息recvmsg: 参见recvlisten: 监听 socket 端口select: 对多路同步 I/O 进行轮询shutdown: 关闭 socket 上的连接getsockname: 取得本地 socket 名字getpeername: 获取通信对方的 socket 名字getsockopt: 取端口设置setsockopt: 设置端口参数sendfile: 在文件或端口间传输数据socketpair: 创建一对已联接的无名 socket
用户管理
getuid: 获取用户标识号setuid: 设置用户标志号getgid: 获取组标识号setgid: 设置组标志号getegid: 获取有效组标识号setegid: 设置有效组标识号geteuid: 获取有效用户标识号seteuid: 设置有效用户标识号setregid: 分别设置真实和有效的的组标识号setreuid: 分别设置真实和有效的用户标识号getresgid: 分别获取真实的, 有效的和保存过的组标识号setresgid: 分别设置真实的, 有效的和保存过的组标识号getresuid: 分别获取真实的, 有效的和保存过的用户标识号setresuid: 分别设置真实的, 有效的和保存过的用户标识号setfsgid: 设置文件系统检查时使用的组标识号setfsuid: 设置文件系统检查时使用的用户标识号getgroups: 获取后补组标志清单setgroups: 设置后补组标志清单
进程间通信
ipc: 进程间通信总控制调用
1、信号
sigaction: 设置对指定信号的处理方法sigprocmask: 根据参数对信号集中的信号执行阻塞 / 解除阻塞等操作sigpending: 为指定的被阻塞信号设置队列sigsuspend: 挂起进程等待特定信号signal: 参见 signalkill: 向进程或进程组发信号*sigblock: 向被阻塞信号掩码中添加信号, 已被sigprocmask代替*siggetmask: 取得现有阻塞信号掩码, 已被sigprocmask代替*sigsetmask: 用给定信号掩码替换现有阻塞信号掩码, 已被sigprocmask代替*sigmask: 将给定的信号转化为掩码, 已被sigprocmask代替*sigpause: 作用同sigsuspend, 已被sigsuspend代替sigvec: 为兼容 BSD 而设的信号处理函数, 作用类似sigactionssetmask: ANSI C 的信号处理函数, 作用类似sigaction
2、消息
msgctl: 消息控制操作msgget: 获取消息队列msgsnd: 发消息msgrcv: 取消息
3、管道
pipe: 创建管道
4、信号量
semctl: 信号量控制semget: 获取一组信号量semop: 信号量操作
5、共享内存
shmctl:控制共享内存shmget:获取共享内存shmat:连接共享内存shmdt:拆卸共享内存
0x02 系统调用关联的hook备份
fork
tracepoint/sched/sched_process_fork:进程创建事件tracepoint/sched/sched_process_exit:进程退出事件tracepoint/sched/sched_process_free:进程释放事件
execve*
tp/syscalls/sys_execvetp/syscalls/sys_exit_execvetp/syscalls/sys_execveattp/syscalls/sys_exit_execveat
bash
uretprobe/bash_readlineuretprobe/bash_retval:bash返回值
fd
tp/syscalls/sys_closetp/syscalls/sys_exit_closetp/syscalls/sys_enter_creattp/syscalls/sys_exit_creattp/syscalls/sys_enter_opentp/syscalls/sys_exit_opentp/syscalls/sys_enter_openattp/syscalls/sys_exit_openattp/syscalls/sys_enter_openat2tp/syscalls/sys_exit_openat2
connect
kprobe/tcp_v4_connectkretprobe/tcp_v4_connectkprobe/tcp_v6_connectkretprobe/tcp_v6_connect
socket
tp/syscalls/sys_enter_getpeernametp/syscalls/sys_exit_getpeernametracepoint/syscalls/sys_enter_getsocknametracepoint/syscalls/sys_exit_getsockname
pipe
tracepoint/syscalls/sys_enter_duptracepoint/syscalls/sys_enter_dup2tracepoint/syscalls/sys_enter_dup3tracepoint/syscalls/sys_eixt_duptracepoint/syscalls/sys_exit_dup2tracepoint/syscalls/sys_exit_dup3tracepoint:syscalls:sys_enter_pipetracepoint:syscalls:sys_enter_pipe2tracepoint:syscalls:sys_exit_pipetracepoint:syscalls:sys_exit_pipe2
安全对抗
tp/syscalls/sys_enter_getdents64tp/syscalls/sys_exit_getdents64tp/syscalls/sys_enter_getdentstp/syscalls/sys_exit_getdentstracepoint:syscalls:sys_enter_memfd_createtracepoint:syscalls:sys_exit_memfd_create
可观测(tracing)
tracepoint/syscalls/sys_enter_connecttracepoint/syscalls/sys_exit_connecttracepoint/syscalls/sys_enter_accepttracepoint/syscalls/sys_exit_accepttracepoint/syscalls/sys_enter_accept4tracepoint/syscalls/sys_exit_accept4tracepoint/syscalls/sys_enter_closetracepoint/syscalls/sys_exit_closetracepoint/syscalls/sys_enter_writetracepoint/syscalls/sys_exit_writetracepoint/syscalls/sys_enter_readtracepoint/syscalls/sys_exit_readtracepoint/syscalls/sys_enter_sendmsgtracepoint/syscalls/sys_exit_sendmsgtracepoint/syscalls/sys_enter_recvmsgtracepoint/syscalls/sys_exit_recvmsg
数据包(协议栈流向)可观测
kprobe/__skb_datagram_iterkprobe/skb_copy_datagram_iovectracepoint/skb/skb_copy_datagram_iovectracepoint/net/netif_receive_skbkprobe/tcp_queue_rcvkprobe/tcp_rcv_establishedkprobe/tcp_v4_do_rcvkprobe/tcp_v6_do_rcvkprobe/tcp_v4_rcvkprobe/ip_rcv_corekprobe/dev_hard_start_xmitkprobe/dev_queue_xmitkprobe/__ip_queue_xmitkprobe/__ip_queue_xmitkprobe/nf_nat_packetkprobe/nf_nat_manip_pktkprobe/security_socket_sendmsgkprobe/security_socket_recvmsgtracepoint/syscalls/sys_enter_recvfromtracepoint/syscalls/sys_exit_recvfromtracepoint/syscalls/sys_enter_readtracepoint/syscalls/sys_exit_readtracepoint/syscalls/sys_enter_recvmsgtracepoint/syscalls/sys_exit_recvmsgtracepoint/syscalls/sys_enter_readvtracepoint/syscalls/sys_exit_readvtracepoint/syscalls/sys_enter_sendfile64tracepoint/syscalls/sys_exit_sendfile64tracepoint/syscalls/sys_enter_sendtotracepoint/syscalls/sys_exit_sendtotracepoint/syscalls/sys_enter_writetracepoint/syscalls/sys_exit_writetracepoint/syscalls/sys_enter_sendmsgtracepoint/syscalls/sys_exit_sendmsgtracepoint/syscalls/sys_enter_writevtracepoint/syscalls/sys_exit_writevtracepoint/syscalls/sys_enter_closekprobe/sys_closetracepoint/syscalls/sys_exit_closetracepoint/syscalls/sys_enter_connecttracepoint/syscalls/sys_exit_connecttracepoint/syscalls/sys_enter_accept4tracepoint/syscalls/sys_exit_accept4
tcp/ip stack
tracepoint:sock:inet_sock_set_state:TCP 状态发生变化的时候被调用
VFS相关
sys_enter_writesys_enter_renameat2sys_enter_renamesys_enter_copy_file_rangesys_enter_unlinkat
0x03 追踪系统调用
strace
直接使用strace echo "abcdefg" >> test.log跟踪系统调用日志,会发现输出里面没有 openat或 open系统调用来打开test.log文件,原因在于 shell 重定向(>>)是由 shell 本身处理的,而不是由被执行的命令 (echo) 处理的
strace echo "abcdefg" >> test.log的执行过程如下:
- shell 解析命令:shell解析命令行,识别出重定向操作符
>> - shell 准备重定向:在启动 strace命令之前,Shell 会先自己执行
open系统调用来打开文件test.log。如果文件不存在,shell 会创建;如果文件存在,Shell 会以追加模式打开,并将文件指针移动到末尾 - shell 将这个新打开的文件描述符(通常是
fd=1,也就是标准输出 stdout)保存起来,准备给即将启动的进程使用 - shell 执行命令:Shell 准备好启动命令,先
fork创建子进程,然后在子进程中执行execve来启动 strace - strace 开始工作:strace进程启动,继承了 Shell 为其设置好的文件描述符表。这意味着它的标准输出(
fd=1)已经指向了test.log文件 - strace 跟踪目标:接着,strace又会
fork/execve来启动echo程序。echo进程继承了 strace的文件描述符表,因此它的标准输出(fd=1)同样指向test.log文件 echo执行:echo程序运行,将字符串abcdefg\n写入(write)到它的标准输出(fd= 1),对应strace的跟踪日志write(1, "abcdefg\n", 9) = 9;它无需要知道这个fd=1是终端、管道还是一个文件,因为fd=1已经被 shell 和 strace设置好了,数据自然就流入了test.log
总结下:test.log 这个操作是由登录 shell完成的,open系统调用发生在 shell 进程中,strace只跟踪它直接启动的进程(这里是 echo)及其子进程的系统调用,它不会跟踪它的父进程( Shell)的行为,因此在 strace echo ...的输出中看不到对 test.log的 open调用,因为这个调用发生在 strace之外,尝试采用下面的方法:
# 启动一个跟踪 shell 的 strace,并让这个 shell 去执行命令
strace -f -o shell_trace.log bash -c 'echo "abcdefg" >> test.log'
使用strace -f -o shell_trace.log bash -c 'echo "abcdefg" >> test.log'跟踪echo写文件的过程
[root@VM-X-X-tencentos ~]# cat shell_trace.log
415499 execve("/usr/bin/bash", ["bash", "-c", "echo \"abcdefg\" >> test.log"], 0x7ffc636511b8 /* 33 vars */) = 0
415499 brk(NULL) = 0x555deaba9000
415499 arch_prctl(0x3001 /* ARCH_??? */, 0x7ffc982164c0) = -1 EINVAL (Invalid argument)
415499 access("/etc/ld.so.preload", R_OK) = 0
415499 openat(AT_FDCWD, "/etc/ld.so.preload", O_RDONLY|O_CLOEXEC) = 3
415499 fstat(3, {st_mode=S_IFREG|0644, st_size=35, ...}) = 0
415499 mmap(NULL, 35, PROT_READ|PROT_WRITE, MAP_PRIVATE, 3, 0) = 0x7fddcf224000
415499 close(3) = 0
415499 readlink("/proc/self/exe", "/usr/bin/bash", 4096) = 13
415499 openat(AT_FDCWD, "/lib64/libtdsp.so", O_RDONLY|O_CLOEXEC) = 3
415499 read(3, "\177ELF\2\1\1\0\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0\340\16\0\0\0\0\0\0"..., 832) = 832
415499 fstat(3, {st_mode=S_IFREG|0755, st_size=48747, ...}) = 0
415499 mmap(NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fddcf222000
415499 mmap(NULL, 2109392, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7fddcedf3000
415499 mprotect(0x7fddcedf6000, 2093056, PROT_NONE) = 0
415499 mmap(0x7fddceff5000, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x2000) = 0x7fddceff5000
415499 close(3) = 0
415499 openat(AT_FDCWD, "/lib64/libonion.so", O_RDONLY|O_CLOEXEC) = 3
415499 read(3, "\177ELF\2\1\1\0\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0\20\22\0\0\0\0\0\0"..., 832) = 832
415499 fstat(3, {st_mode=S_IFREG|0755, st_size=19656, ...}) = 0
415499 mmap(NULL, 31552, PROT_READ, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7fddcf21a000
415499 mmap(0x7fddcf21b000, 8192, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x1000) = 0x7fddcf21b000
415499 mmap(0x7fddcf21d000, 4096, PROT_READ, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x3000) = 0x7fddcf21d000
415499 mmap(0x7fddcf21e000, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x3000) = 0x7fddcf21e000
415499 mmap(0x7fddcf220000, 6976, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x7fddcf220000
415499 close(3) = 0
415499 munmap(0x7fddcf224000, 35) = 0
415499 openat(AT_FDCWD, "/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3
415499 fstat(3, {st_mode=S_IFREG|0644, st_size=29391, ...}) = 0
415499 mmap(NULL, 29391, PROT_READ, MAP_PRIVATE, 3, 0) = 0x7fddcf212000
415499 close(3) = 0
415499 openat(AT_FDCWD, "/lib64/libtinfo.so.6", O_RDONLY|O_CLOEXEC) = 3
415499 read(3, "\177ELF\2\1\1\0\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0000\351\0\0\0\0\0\0"..., 832) = 832
415499 lseek(3, 165144, SEEK_SET) = 165144
415499 read(3, "\4\0\0\0\20\0\0\0\5\0\0\0GNU\0\2\0\0\300\4\0\0\0\3\0\0\0\0\0\0\0", 32) = 32
415499 fstat(3, {st_mode=S_IFREG|0755, st_size=187552, ...}) = 0
415499 lseek(3, 165144, SEEK_SET) = 165144
415499 read(3, "\4\0\0\0\20\0\0\0\5\0\0\0GNU\0\2\0\0\300\4\0\0\0\3\0\0\0\0\0\0\0", 32) = 32
415499 mmap(NULL, 2279808, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7fddcebc6000
415499 mprotect(0x7fddcebef000, 2093056, PROT_NONE) = 0
415499 mmap(0x7fddcedee000, 20480, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x28000) = 0x7fddcedee000
415499 close(3) = 0
415499 openat(AT_FDCWD, "/lib64/libdl.so.2", O_RDONLY|O_CLOEXEC) = 3
415499 read(3, "\177ELF\2\1\1\0\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0p\16\0\0\0\0\0\0"..., 832) = 832
415499 fstat(3, {st_mode=S_IFREG|0755, st_size=19128, ...}) = 0
415499 mmap(NULL, 2109600, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7fddce9c2000
415499 mprotect(0x7fddce9c5000, 2093056, PROT_NONE) = 0
415499 mmap(0x7fddcebc4000, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x2000) = 0x7fddcebc4000
415499 close(3) = 0
415499 openat(AT_FDCWD, "/lib64/libc.so.6", O_RDONLY|O_CLOEXEC) = 3
415499 read(3, "\177ELF\2\1\1\3\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0\300\250\3\0\0\0\0\0"..., 832) = 832
415499 fstat(3, {st_mode=S_IFREG|0755, st_size=2164648, ...}) = 0
415499 lseek(3, 808, SEEK_SET) = 808
415499 read(3, "\4\0\0\0\20\0\0\0\5\0\0\0GNU\0\2\0\0\300\4\0\0\0\3\0\0\0\0\0\0\0", 32) = 32
415499 mmap(NULL, 4020448, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7fddce5ec000
415499 mprotect(0x7fddce7b9000, 2093056, PROT_NONE) = 0
415499 mmap(0x7fddce9b8000, 24576, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x1cc000) = 0x7fddce9b8000
415499 mmap(0x7fddce9be000, 14560, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x7fddce9be000
415499 close(3) = 0
415499 mmap(NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fddcf210000
415499 arch_prctl(ARCH_SET_FS, 0x7fddcf210f00) = 0
415499 mprotect(0x7fddce9b8000, 16384, PROT_READ) = 0
415499 mprotect(0x7fddcebc4000, 4096, PROT_READ) = 0
415499 mprotect(0x7fddcedee000, 16384, PROT_READ) = 0
415499 mprotect(0x7fddcf21e000, 4096, PROT_READ) = 0
415499 mprotect(0x555dea9ad000, 16384, PROT_READ) = 0
415499 mprotect(0x7fddcf225000, 4096, PROT_READ) = 0
415499 munmap(0x7fddcf212000, 29391) = 0
415499 openat(AT_FDCWD, "/dev/tty", O_RDWR|O_NONBLOCK) = 3
415499 close(3) = 0
415499 getrandom("\xb4\x67\xa7\xde\x7a\x5f\x55\x55", 8, GRND_NONBLOCK) = 8
415499 brk(NULL) = 0x555deaba9000
415499 brk(0x555deabca000) = 0x555deabca000
415499 brk(NULL) = 0x555deabca000
415499 openat(AT_FDCWD, "/usr/lib/locale/locale-archive", O_RDONLY|O_CLOEXEC) = 3
415499 fstat(3, {st_mode=S_IFREG|0644, st_size=217800224, ...}) = 0
415499 mmap(NULL, 217800224, PROT_READ, MAP_PRIVATE, 3, 0) = 0x7fddc1636000
415499 close(3) = 0
415499 getuid() = 0
415499 getgid() = 0
415499 geteuid() = 0
415499 getegid() = 0
415499 rt_sigprocmask(SIG_BLOCK, NULL, [], 8) = 0
415499 ioctl(-1, TIOCGPGRP, 0x7ffc982163a4) = -1 EBADF (Bad file descriptor)
415499 sysinfo({uptime=2693704, loads=[0, 0, 0], totalram=16500895744, freeram=8356208640, sharedram=294432768, bufferram=420241408, totalswap=0, freeswap=0, procs=280, totalhigh=0, freehigh=0, mem_unit=1}) = 0
415499 rt_sigaction(SIGCHLD, {sa_handler=SIG_DFL, sa_mask=[], sa_flags=SA_RESTORER|SA_RESTART, sa_restorer=0x7fddce63a5b0}, {sa_handler=SIG_DFL, sa_mask=[], sa_flags=0}, 8) = 0
415499 rt_sigaction(SIGCHLD, {sa_handler=SIG_DFL, sa_mask=[], sa_flags=SA_RESTORER|SA_RESTART, sa_restorer=0x7fddce63a5b0}, {sa_handler=SIG_DFL, sa_mask=[], sa_flags=SA_RESTORER|SA_RESTART, sa_restorer=0x7fddce63a5b0}, 8) = 0
415499 rt_sigaction(SIGINT, {sa_handler=SIG_DFL, sa_mask=[], sa_flags=SA_RESTORER, sa_restorer=0x7fddce63a5b0}, {sa_handler=SIG_DFL, sa_mask=[], sa_flags=0}, 8) = 0
415499 rt_sigaction(SIGINT, {sa_handler=SIG_DFL, sa_mask=[], sa_flags=SA_RESTORER, sa_restorer=0x7fddce63a5b0}, {sa_handler=SIG_DFL, sa_mask=[], sa_flags=SA_RESTORER, sa_restorer=0x7fddce63a5b0}, 8) = 0
415499 rt_sigaction(SIGQUIT, {sa_handler=SIG_DFL, sa_mask=[], sa_flags=SA_RESTORER, sa_restorer=0x7fddce63a5b0}, {sa_handler=SIG_DFL, sa_mask=[], sa_flags=0}, 8) = 0
415499 rt_sigaction(SIGQUIT, {sa_handler=SIG_DFL, sa_mask=[], sa_flags=SA_RESTORER, sa_restorer=0x7fddce63a5b0}, {sa_handler=SIG_DFL, sa_mask=[], sa_flags=SA_RESTORER, sa_restorer=0x7fddce63a5b0}, 8) = 0
415499 rt_sigaction(SIGTSTP, {sa_handler=SIG_DFL, sa_mask=[], sa_flags=SA_RESTORER, sa_restorer=0x7fddce63a5b0}, {sa_handler=SIG_DFL, sa_mask=[], sa_flags=0}, 8) = 0
415499 rt_sigaction(SIGTSTP, {sa_handler=SIG_DFL, sa_mask=[], sa_flags=SA_RESTORER, sa_restorer=0x7fddce63a5b0}, {sa_handler=SIG_DFL, sa_mask=[], sa_flags=SA_RESTORER, sa_restorer=0x7fddce63a5b0}, 8) = 0
415499 rt_sigaction(SIGTTIN, {sa_handler=SIG_DFL, sa_mask=[], sa_flags=SA_RESTORER, sa_restorer=0x7fddce63a5b0}, {sa_handler=SIG_DFL, sa_mask=[], sa_flags=0}, 8) = 0
415499 rt_sigaction(SIGTTIN, {sa_handler=SIG_DFL, sa_mask=[], sa_flags=SA_RESTORER, sa_restorer=0x7fddce63a5b0}, {sa_handler=SIG_DFL, sa_mask=[], sa_flags=SA_RESTORER, sa_restorer=0x7fddce63a5b0}, 8) = 0
415499 rt_sigaction(SIGTTOU, {sa_handler=SIG_DFL, sa_mask=[], sa_flags=SA_RESTORER, sa_restorer=0x7fddce63a5b0}, {sa_handler=SIG_DFL, sa_mask=[], sa_flags=0}, 8) = 0
415499 rt_sigaction(SIGTTOU, {sa_handler=SIG_DFL, sa_mask=[], sa_flags=SA_RESTORER, sa_restorer=0x7fddce63a5b0}, {sa_handler=SIG_DFL, sa_mask=[], sa_flags=SA_RESTORER, sa_restorer=0x7fddce63a5b0}, 8) = 0
415499 rt_sigprocmask(SIG_BLOCK, NULL, [], 8) = 0
415499 rt_sigaction(SIGQUIT, {sa_handler=SIG_IGN, sa_mask=[], sa_flags=SA_RESTORER, sa_restorer=0x7fddce63a5b0}, {sa_handler=SIG_DFL, sa_mask=[], sa_flags=SA_RESTORER, sa_restorer=0x7fddce63a5b0}, 8) = 0
415499 uname({sysname="Linux", nodename="VM-137-151-tencentos", ...}) = 0
415499 rt_sigprocmask(SIG_BLOCK, NULL, [], 8) = 0
415499 openat(AT_FDCWD, "/usr/lib64/gconv/gconv-modules.cache", O_RDONLY) = 3
415499 fstat(3, {st_mode=S_IFREG|0644, st_size=26998, ...}) = 0
415499 mmap(NULL, 26998, PROT_READ, MAP_SHARED, 3, 0) = 0x7fddcf213000
415499 close(3) = 0
415499 rt_sigprocmask(SIG_BLOCK, NULL, [], 8) = 0
415499 rt_sigprocmask(SIG_BLOCK, NULL, [], 8) = 0
415499 rt_sigprocmask(SIG_BLOCK, NULL, [], 8) = 0
415499 rt_sigprocmask(SIG_BLOCK, NULL, [], 8) = 0
415499 rt_sigprocmask(SIG_BLOCK, NULL, [], 8) = 0
415499 stat("/root", {st_mode=S_IFDIR|0550, st_size=4096, ...}) = 0
415499 stat(".", {st_mode=S_IFDIR|0550, st_size=4096, ...}) = 0
415499 stat("/root", {st_mode=S_IFDIR|0550, st_size=4096, ...}) = 0
415499 getpid() = 415499
415499 getppid() = 415496
415499 stat(".", {st_mode=S_IFDIR|0550, st_size=4096, ...}) = 0
415499 stat("/usr/share/Modules/bin/bash", 0x7ffc98216020) = -1 ENOENT (No such file or directory)
415499 stat("/usr/local/sbin/bash", 0x7ffc98216020) = -1 ENOENT (No such file or directory)
415499 stat("/usr/local/bin/bash", 0x7ffc98216020) = -1 ENOENT (No such file or directory)
415499 stat("/usr/sbin/bash", 0x7ffc98216020) = -1 ENOENT (No such file or directory)
415499 stat("/usr/bin/bash", {st_mode=S_IFREG|0755, st_size=1163048, ...}) = 0
415499 stat("/usr/bin/bash", {st_mode=S_IFREG|0755, st_size=1163048, ...}) = 0
415499 geteuid() = 0
415499 getegid() = 0
415499 getuid() = 0
415499 getgid() = 0
415499 access("/usr/bin/bash", X_OK) = 0
415499 stat("/usr/bin/bash", {st_mode=S_IFREG|0755, st_size=1163048, ...}) = 0
415499 geteuid() = 0
415499 getegid() = 0
415499 getuid() = 0
415499 getgid() = 0
415499 access("/usr/bin/bash", R_OK) = 0
415499 stat("/usr/bin/bash", {st_mode=S_IFREG|0755, st_size=1163048, ...}) = 0
415499 stat("/usr/bin/bash", {st_mode=S_IFREG|0755, st_size=1163048, ...}) = 0
415499 geteuid() = 0
415499 getegid() = 0
415499 getuid() = 0
415499 getgid() = 0
415499 access("/usr/bin/bash", X_OK) = 0
415499 stat("/usr/bin/bash", {st_mode=S_IFREG|0755, st_size=1163048, ...}) = 0
415499 geteuid() = 0
415499 getegid() = 0
415499 getuid() = 0
415499 getgid() = 0
415499 access("/usr/bin/bash", R_OK) = 0
415499 getpid() = 415499
415499 getpgrp() = 415496
415499 ioctl(2, TIOCGPGRP, [415496]) = 0
415499 rt_sigaction(SIGCHLD, {sa_handler=0x555dea6fe560, sa_mask=[], sa_flags=SA_RESTORER|SA_RESTART, sa_restorer=0x7fddce63a5b0}, {sa_handler=SIG_DFL, sa_mask=[], sa_flags=SA_RESTORER|SA_RESTART, sa_restorer=0x7fddce63a5b0}, 8) = 0
415499 prlimit64(0, RLIMIT_NPROC, NULL, {rlim_cur=62800, rlim_max=62800}) = 0
415499 rt_sigprocmask(SIG_BLOCK, NULL, [], 8) = 0
415499 getpeername(0, 0x7ffc982163a0, [16]) = -1 ENOTSOCK (Socket operation on non-socket)
415499 socket(AF_UNIX, SOCK_STREAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0) = 3
415499 connect(3, {sa_family=AF_UNIX, sun_path="/var/run/nscd/socket"}, 110) = 0
415499 sendto(3, "\2\0\0\0\v\0\0\0\7\0\0\0passwd\0", 19, MSG_NOSIGNAL, NULL, 0) = 19
415499 poll([{fd=3, events=POLLIN|POLLERR|POLLHUP}], 1, 5000) = 1 ([{fd=3, revents=POLLIN|POLLHUP}])
415499 recvmsg(3, {msg_name=NULL, msg_namelen=0, msg_iov=[{iov_base="passwd\0", iov_len=7}, {iov_base="\310O\3\0\0\0\0\0", iov_len=8}], msg_iovlen=2, msg_control=[{cmsg_len=20, cmsg_level=SOL_SOCKET, cmsg_type=SCM_RIGHTS, cmsg_data=[4]}], msg_controllen=20, msg_flags=MSG_CMSG_CLOEXEC}, MSG_CMSG_CLOEXEC) = 15
415499 mmap(NULL, 217032, PROT_READ, MAP_SHARED, 4, 0) = 0x7fddcf1db000
415499 close(4) = 0
415499 close(3) = 0
415499 getpid() = 415499
415499 openat(AT_FDCWD, "/proc/415499/sched", O_RDONLY) = 3
415499 fstat(3, {st_mode=S_IFREG|0644, st_size=0, ...}) = 0
415499 read(3, "bash (415499, #threads: 1)\n-----"..., 1024) = 1024
415499 close(3) = 0
415499 openat(AT_FDCWD, "/proc/415499/stat", O_RDONLY) = 3
415499 read(3, "415499 (bash) R 415496 415496 41"..., 799) = 324
415499 close(3) = 0
415499 openat(AT_FDCWD, "/proc/415496/sched", O_RDONLY) = 3
415499 fstat(3, {st_mode=S_IFREG|0644, st_size=0, ...}) = 0
415499 read(3, "strace (415496, #threads: 1)\n---"..., 1024) = 1024
415499 close(3) = 0
415499 openat(AT_FDCWD, "/proc/415496/cmdline", O_RDONLY) = 3
415499 fstat(3, {st_mode=S_IFREG|0444, st_size=0, ...}) = 0
415499 read(3, "strace\0-f\0-o\0shell_trace.log\0bas"..., 9216) = 64
415499 read(3, "", 9216) = 0
415499 close(3) = 0
415499 openat(AT_FDCWD, "/proc/415496/stat", O_RDONLY) = 3
415499 read(3, "415496 (strace) S 415298 415496 "..., 799) = 336
415499 close(3) = 0
415499 openat(AT_FDCWD, "/proc/415298/sched", O_RDONLY) = 3
415499 fstat(3, {st_mode=S_IFREG|0644, st_size=0, ...}) = 0
415499 read(3, "bash (415298, #threads: 1)\n-----"..., 1024) = 1024
415499 close(3) = 0
415499 readlink("/proc/415298/exe", "/usr/bin/bash", 1023) = 13
415499 openat(AT_FDCWD, "/proc/415298/cmdline", O_RDONLY) = 3
415499 fstat(3, {st_mode=S_IFREG|0444, st_size=0, ...}) = 0
415499 read(3, "-bash\0", 9216) = 6
415499 read(3, "", 9216) = 0
415499 close(3) = 0
415499 openat(AT_FDCWD, "/proc/415499/stat", O_RDONLY) = 3
415499 read(3, "415499 (bash) R 415496 415496 41"..., 799) = 324
415499 close(3) = 0
415499 openat(AT_FDCWD, "/proc/415496/cmdline", O_RDONLY) = 3
415499 fstat(3, {st_mode=S_IFREG|0444, st_size=0, ...}) = 0
415499 read(3, "strace\0-f\0-o\0shell_trace.log\0bas"..., 1024) = 64
415499 read(3, "", 1024) = 0
415499 close(3) = 0
415499 openat(AT_FDCWD, "/proc/415496/stat", O_RDONLY) = 3
415499 read(3, "415496 (strace) S 415298 415496 "..., 799) = 336
415499 close(3) = 0
415499 openat(AT_FDCWD, "/proc/415298/cmdline", O_RDONLY) = 3
415499 fstat(3, {st_mode=S_IFREG|0444, st_size=0, ...}) = 0
415499 read(3, "-bash\0", 1024) = 6
415499 read(3, "", 1024) = 0
415499 close(3) = 0
415499 openat(AT_FDCWD, "/proc/415298/stat", O_RDONLY) = 3
415499 read(3, "415298 (bash) S 415297 415298 41"..., 799) = 344
415499 close(3) = 0
415499 openat(AT_FDCWD, "/proc/415297/cmdline", O_RDONLY) = 3
415499 fstat(3, {st_mode=S_IFREG|0444, st_size=0, ...}) = 0
415499 read(3, "su\0-\0root\0", 1024) = 10
415499 read(3, "", 1024) = 0
415499 close(3) = 0
415499 openat(AT_FDCWD, "/proc/415297/stat", O_RDONLY) = 3
415499 read(3, "415297 (su) S 415296 415296 4152"..., 799) = 332
415499 close(3) = 0
415499 openat(AT_FDCWD, "/proc/415296/cmdline", O_RDONLY) = 3
415499 fstat(3, {st_mode=S_IFREG|0444, st_size=0, ...}) = 0
415499 read(3, "sudo\0su\0-\0root\0", 1024) = 15
415499 read(3, "", 1024) = 0
415499 close(3) = 0
415499 openat(AT_FDCWD, "/proc/415296/stat", O_RDONLY) = 3
415499 read(3, "415296 (sudo) S 415243 415296 41"..., 799) = 326
415499 close(3) = 0
415499 openat(AT_FDCWD, "/proc/415243/cmdline", O_RDONLY) = 3
415499 fstat(3, {st_mode=S_IFREG|0444, st_size=0, ...}) = 0
415499 read(3, "-bash\0", 1024) = 6
415499 read(3, "", 1024) = 0
415499 close(3) = 0
415499 openat(AT_FDCWD, "/proc/415243/stat", O_RDONLY) = 3
415499 read(3, "415243 (bash) S 415242 415243 41"..., 799) = 344
415499 close(3) = 0
415499 openat(AT_FDCWD, "/proc/415242/cmdline", O_RDONLY) = 3
415499 fstat(3, {st_mode=S_IFREG|0444, st_size=0, ...}) = 0
415499 read(3, "sshd: pandaychen@pts/0\0\0\0\0\0\0\0\0\0\0"..., 1024) = 1024
415499 close(3) = 0
415499 getcwd("/root", 512) = 6
415499 ioctl(0, TCGETS, {B38400 opost isig icanon echo ...}) = 0
415499 fstat(0, {st_mode=S_IFCHR|0620, st_rdev=makedev(0x88, 0), ...}) = 0
415499 readlink("/proc/self/fd/0", "/dev/pts/0", 255) = 10
415499 stat("/dev/pts/0", {st_mode=S_IFCHR|0620, st_rdev=makedev(0x88, 0), ...}) = 0
415499 socket(AF_UNIX, SOCK_DGRAM, 0) = 3
415499 fcntl(3, F_GETFL) = 0x2 (flags O_RDWR)
415499 fcntl(3, F_SETFL, O_RDWR|O_NONBLOCK) = 0
415499 fcntl(3, F_GETFD) = 0
415499 fcntl(3, F_SETFD, FD_CLOEXEC) = 0
415499 sendto(3, "\0\0\0\6\0\0\0\377\0\4bash\0#bash -c echo \"ab"..., 255, 0, {sa_family=AF_UNIX, sun_path="/usr/local/sa/agent/log/agent_cmd.sock"}, 40) = 255
415499 rt_sigprocmask(SIG_BLOCK, NULL, [], 8) = 0
415499 openat(AT_FDCWD, "test.log", O_WRONLY|O_CREAT|O_APPEND, 0666) = 4
415499 fcntl(1, F_GETFD) = 0
415499 fcntl(1, F_DUPFD, 10) = 10
415499 fcntl(1, F_GETFD) = 0
415499 fcntl(10, F_SETFD, FD_CLOEXEC) = 0
415499 dup2(4, 1) = 1
415499 close(4) = 0
415499 getpid() = 415499
415499 getcwd("/root", 512) = 6
415499 ioctl(0, TCGETS, {B38400 opost isig icanon echo ...}) = 0
415499 fstat(0, {st_mode=S_IFCHR|0620, st_rdev=makedev(0x88, 0), ...}) = 0
415499 readlink("/proc/self/fd/0", "/dev/pts/0", 255) = 10
415499 stat("/dev/pts/0", {st_mode=S_IFCHR|0620, st_rdev=makedev(0x88, 0), ...}) = 0
415499 sendto(3, "\0\0\0\6\0\0\0\351\0\4echo\0\recho abcdefg \0\0\4"..., 233, 0, {sa_family=AF_UNIX, sun_path="/usr/local/sa/agent/log/agent_cmd.sock"}, 40) = 233
415499 fstat(1, {st_mode=S_IFREG|0644, st_size=0, ...}) = 0
415499 write(1, "abcdefg\n", 8) = 8
415499 dup2(10, 1) = 1
415499 fcntl(10, F_GETFD) = 0x1 (flags FD_CLOEXEC)
415499 close(10) = 0
415499 rt_sigprocmask(SIG_BLOCK, [CHLD], [], 8) = 0
415499 rt_sigprocmask(SIG_SETMASK, [], NULL, 8) = 0
415499 exit_group(0) = ?
415499 +++ exited with 0 +++
上面的日志的细节:
415499 openat(AT_FDCWD, "test.log", O_WRONLY|O_CREAT|O_APPEND, 0666) = 4
......
415499 dup2(4, 1) = 1 #使 fd 1(标准输出)指向与 fd 4 相同的文件表项
415499 close(4) = 0 #关闭 fd 4(因为不再需要它)
......
415499 write(1, "abcdefg\n", 8) = 8
shell 在处理重定向 >>时,使用 dup2系统调用来将文件描述符重定向到标准输出(fd=1),所以write写入的fd=1而不是4
当 shell 执行重定向时,它需要先打开文件 test.log,由于 shell 进程本身已经打开了标准输入(fd=0)、标准输出(fd=1)和标准错误(fd=2),可能还有其他文件描述符,所以新打开的文件通常会分配下一个可用的文件描述符,这里是 fd=4。此外,shell 不会直接使用新打开的 fd=4 进行写入,相反它使用 dup2系统调用来将 fd=4 复制到标准输出 fd=1
对上面的流程进行简化如下
#shell 打开文件:
open("test.log", O_WRONLY|O_CREAT|O_APPEND, 0666) = 4
#shell 将 fd 4 复制到 fd 1:
dup2(4, 1) = 1
#shell 关闭 fd 4(可选,但常见):
close(4) = 0
#shell 执行 echo命令(通过 execve):
execve("/usr/bin/echo", ["echo", "abcdefg"], ...)
#echo进程写入 fd 1:
write(1, "abcdefg\n", 9) = 9