process_vm_readv和process_vm_writev系统调用的用法及示例
process_vm_readv
和 process_vm_writev
是 Linux 提供的系统调用,允许一个进程直接读写另一个进程的内存空间,常用于高效进程间通信(IPC)。以下是它们的用法和示例:
1. 函数原型
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| #include <sys/uio.h>
ssize_t process_vm_readv(pid_t pid, const struct iovec *local_iov, unsigned long local_iovcnt, const struct iovec *remote_iov, unsigned long remote_iovcnt, unsigned long flags);
ssize_t process_vm_writev(pid_t pid, const struct iovec *local_iov, unsigned long local_iovcnt, const struct iovec *remote_iov, unsigned long remote_iovcnt, unsigned long flags);
|
参数说明
pid
: 目标进程的进程 ID。
local_iov
: 指向本地 iovec
结构数组的指针,描述本地缓冲区的地址和长度。
local_iovcnt
: 本地 iovec
数组的元素数量。
remote_iov
: 指向目标进程 iovec
结构数组的指针,描述目标进程内存的地址和长度。
remote_iovcnt
: 目标进程 iovec
数组的元素数量。
flags
: 保留参数,通常设为 0
。
2. 示例代码
示例 1:读取目标进程的内存
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
| #include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/uio.h> #include <unistd.h>
int main() { pid_t target_pid = 1234; struct iovec local[1], remote[1]; char buffer[100];
local[0].iov_base = buffer; local[0].iov_len = sizeof(buffer);
remote[0].iov_base = (void *)0x7ffd12345678; remote[0].iov_len = sizeof(buffer);
ssize_t bytes_read = process_vm_readv(target_pid, local, 1, remote, 1, 0); if (bytes_read < 0) { perror("process_vm_readv failed"); exit(EXIT_FAILURE); }
printf("Read %zd bytes: %s\n", bytes_read, buffer); return 0; }
|
示例 2:向目标进程写入内存
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
| #include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/uio.h> #include <unistd.h>
int main() { pid_t target_pid = 1234; struct iovec local[1], remote[1]; const char *data = "Hello from another process!";
local[0].iov_base = (void *)data; local[0].iov_len = strlen(data) + 1;
remote[0].iov_base = (void *)0x7ffd12345678; remote[0].iov_len = strlen(data) + 1;
ssize_t bytes_written = process_vm_writev(target_pid, local, 1, remote, 1, 0); if (bytes_written < 0) { perror("process_vm_writev failed"); exit(EXIT_FAILURE); }
printf("Wrote %zd bytes\n", bytes_written); return 0; }
|
3. 关键注意事项
权限要求:
- 调用进程需有权限操作目标进程(如
CAP_SYS_PTRACE
能力或 root
权限)。
- 目标进程的内存地址必须有效且可读写。
错误处理:
- 检查返回值:成功时返回传输的字节数,失败返回
-1
并设置 errno
。
- 常见错误码:
EPERM
(权限不足)、ESRCH
(目标进程不存在)、EINVAL
(参数无效)。
地址有效性:
- 目标进程的内存地址需通过调试符号、
/proc/[pid]/maps
或其他方式获取,硬编码地址可能不可靠。
性能优势:
- 相比
ptrace
,这两个系统调用避免了频繁的上下文切换,适合大量数据传输。
4. 对比 ptrace
ptrace
: 逐字节读写,适合调试或小数据操作,但性能较差。
process_vm_readv/writev
: 批量操作内存,效率更高,适合大规模数据传输。
通过合理使用这两个系统调用,可以实现高效的进程间通信,但需谨慎处理权限和内存安全问题。