在下面的程序中,我试图导致以下情况发生:
问题在于,在步骤6中,父级只能看到in / proc / child_pid / mem的旧值,而孩子确实可以在/ proc / self / mem中看到新值。 这是为什么? 有没有办法让父母通过/ proc文件系统来查看孩子对其地址空间的更改?
#include <fcntl.h> #include <stdbool.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/types.h> #include <sys/stat.h> #include <sys/wait.h> #include <unistd.h> #define PAGE_SIZE 0x1000 #define LOG_PAGE_SIZE 0xc #define PAGE_ROUND_DOWN(v) ((v) & (~(PAGE_SIZE - 1))) #define PAGE_ROUND_UP(v) (((v) + PAGE_SIZE - 1) & (~(PAGE_SIZE - 1))) #define OFFSET_IN_PAGE(v) ((v) & (PAGE_SIZE - 1)) # if defined ARCH && ARCH == 32 #define BP "ebp" #define SP "esp" #else #define BP "rbp" #define SP "rsp" #endif typedef struct arg_t { int a; } arg_t; void func1(void * data) { arg_t * arg_ptr = (arg_t *)data; printf("func1: old value: %d\n", arg_ptr->a); arg_ptr->a = 53; printf("func1: address: %p\n", &arg_ptr->a); printf("func1: new value: %d\n", arg_ptr->a); } void expore_proc_mem(void (*fn)(void *), void * data) { off_t frame_pointer, stack_start; char buffer[PAGE_SIZE]; const char * path = "/proc/self/mem"; int child_pid, status; int parent_to_child[2]; int child_to_parent[2]; arg_t * arg_ptr; off_t child_offset; asm volatile ("mov %%"BP", %0" : "=m" (frame_pointer)); stack_start = PAGE_ROUND_DOWN(frame_pointer); printf("Stack_start: %lx\n", (unsigned long)stack_start); arg_ptr = (arg_t *)data; child_offset = OFFSET_IN_PAGE((off_t)&arg_ptr->a); printf("Address of arg_ptr->a: %p\n", &arg_ptr->a); pipe(parent_to_child); pipe(child_to_parent); bool msg; int child_mem_fd; char child_path[0x20]; child_pid = fork(); if (child_pid == -1) { perror("fork"); exit(EXIT_FAILURE); } if (!child_pid) { close(child_to_parent[0]); close(parent_to_child[1]); printf("CHILD (pid %d, parent pid %d).\n", getpid(), getppid()); fn(data); msg = true; write(child_to_parent[1], &msg, 1); child_mem_fd = open("/proc/self/mem", O_RDONLY); if (child_mem_fd == -1) { perror("open (child)"); exit(EXIT_FAILURE); } printf("CHILD: child_mem_fd: %d\n", child_mem_fd); if (lseek(child_mem_fd, stack_start, SEEK_SET) == (off_t)-1) { perror("lseek"); exit(EXIT_FAILURE); } if (read(child_mem_fd, buffer, sizeof(buffer)) != sizeof(buffer)) { perror("read"); exit(EXIT_FAILURE); } printf("CHILD: new value %d\n", *(int *)(buffer + child_offset)); read(parent_to_child[0], &msg, 1); exit(EXIT_SUCCESS); } else { printf("PARENT (pid %d, child pid %d)\n", getpid(), child_pid); printf("PARENT: child_offset: %lx\n", child_offset); read(child_to_parent[0], &msg, 1); printf("PARENT: message from child: %d\n", msg); snprintf(child_path, 0x20, "/proc/%d/mem", child_pid); printf("PARENT: child_path: %s\n", child_path); child_mem_fd = open(path, O_RDONLY); if (child_mem_fd == -1) { perror("open (child)"); exit(EXIT_FAILURE); } printf("PARENT: child_mem_fd: %d\n", child_mem_fd); if (lseek(child_mem_fd, stack_start, SEEK_SET) == (off_t)-1) { perror("lseek"); exit(EXIT_FAILURE); } if (read(child_mem_fd, buffer, sizeof(buffer)) != sizeof(buffer)) { perror("read"); exit(EXIT_FAILURE); } printf("PARENT: new value %d\n", *(int *)(buffer + child_offset)); close(child_mem_fd); printf("ENDING CHILD PROCESS.\n"); write(parent_to_child[1], &msg, 1); if (waitpid(child_pid, &status, 0) == -1) { perror("waitpid"); exit(EXIT_FAILURE); } } } int main(void) { arg_t arg; arg.a = 42; printf("In main: address of arg.a: %p\n", &arg.a); explore_proc_mem(&func1, &arg.a); return EXIT_SUCCESS; }
这个程序产生下面的输出。 注意,(粗体)的值在父/子读取/ proc / child_pid / mem文件时有所不同。
主要地址:arg.a:0x7ffffe1964f0
Stack_start:7ffffe196000
arg_ptr-> a的地址:0x7ffffe1964f0
家长(pid 20376,孩子pid 20377)
父母:child_offset:4f0
儿童(pid 20377,父母的pid 20376)。
func1:旧值:42
func1:地址:0x7ffffe1964f0
func1:新值:53
父母:孩子的留言:1
CHILD:child_mem_fd:4
PARENT:child_path:/ proc / 20377 / mem
儿童:新价值53
父母:child_mem_fd:7
父母:新的价值42
结束孩子的过程。
这个代码有一个愚蠢的错误:
const char * path = "/proc/self/mem"; ... snprintf(child_path, 0x20, "/proc/%d/mem", child_pid); printf("PARENT: child_path: %s\n", child_path); child_mem_fd = open(path, O_RDONLY);
所以你最终总是在这里阅读父母的记忆。 但是在改变这个之后,我得到:
CHILD: child_mem_fd: 4 CHILD: new value 53 read (parent): No such process
我不知道为什么会发生这种情况 – 可能/proc
在刷新条目时太慢了? (这是从父母的错误perror("read")
– 必须添加一个评论,看看哪一个失败)但这似乎很奇怪,因为seek
工作 – 以及open
自己。
这个问题似乎并不新鲜: http : //lkml.indiana.edu/hypermail/linux/kernel/0007.1/0939.html (ESRCH是“没有这样的过程”)
其实更好的链接是: http : //www.webservertalk.com/archive242-2004-7-295131.html – 有标记进程pthread-attach-safe的问题。 你可以在那里找到艾伦考克斯派人到太阳能设计师…对我来说,这个拼写“这里是龙”,这是不是可以解决,如果你没有在你的睡眠黑内核:(
也许这足以让你检查在这种情况下gdb做什么,并复制它? (可能它只是通过ptrace(PTRACE_PEEKDATA,...)
)
解决方案是使用ptrace来同步父和子。 即使我已经在父母和孩子之间进行沟通(并且ptrace的手册页上说,它导致两个进程的行为就好像他们是父母和孩子一样),即使孩子在阅读电话上阻塞,孩子也有显然没有“停止”足够的Linux允许父母读取孩子的/ proc / child_pid / mem文件。 但是,如果父节点首先使用PTRACE_ATTACH调用ptrace (在通过管道接收到消息之后),那么它可以打开文件并获取正确的内容! 然后父节点再次使用PTRACE_DETACH调用ptrace ,然后将消息发送回子节点终止。