c - 通过 argv 操作进程名称和参数

标签 c linux argv procfs

我有一个用 C 编写的程序,只能在 Linux 上运行。我希望能够更改进程名称,如 ps 命令中所示。为此,我直接更改了 argv[0] 中的字符串,还使用了来自主线程的 prctl(PR_SET_NAME, argv[0]) 调用。我还想从动态加载的共享库访问 /proc/self/cmdline,将来甚至可能从其他程序访问。

我读到要使其工作,我必须使用从 argv[0] 开始的原始内存空间。 ELF标准规定这个空间是\0environ空间分开的。调查ps_status.c从 Postgres 代码中,可以看出他们将所有这些空间都用于 argv 字符串。真的,当我memset这个空间到'a'时,我可以在ps中看到超过3000个字符并从/读取它proc 文件系统。当我尝试使用此空间动态地(在运行时)在此空间中创建新参数时,问题就开始了。 (我已经阅读并从基本测试中知道 Chrome/Chromium 做了类似的事情 - 通过命令行参数在 ps 中导出它的 forked 进程的状态。)任何包含到达原始环境的空间中的 NULL 定界符被视为结束。 (我最初在 cmdline 参数中有 105 个字符,我能够得到 130 个字符,但其他参数最多为 3000 个字符标记未被读取。)据我所知,系统记住了原始大小并且只让我“阅读” "直到字符串结束。 (更改 char** argv 指针没有帮助。)

但 Chrome 正在以某种方式做到这一点。调查command_line.cc来源我看不出直接的方法。

有没有可能这样做呢?如果是这样,怎么办?告诉 Linux 内核 argv 内存和 argc 的大小发生了变化?

谢谢。

最佳答案

PR_SET_MM_ARG_STARTPR_SET_MM_ARG_END 如果您是根用户(更具体地说,如果进程具有 CAP_SYS_RESOURCE 能力),您可以执行此操作。

用法:

prctl(PR_SET_NAME, constructed_argv[0]);
prctl(PR_SET_MM, PR_SET_MM_ARG_START, constructed_argv, 0, 0);
prctl(PR_SET_MM, PR_SET_MM_ARG_END, end_of_constructed_argv, 0, 0);

这是一个在 systemd 中有据可查的用法示例:

            /* Now, let's tell the kernel about this new memory */
            if (prctl(PR_SET_MM, PR_SET_MM_ARG_START, (unsigned long) nn, 0, 0) < 0) {
                    /* HACK: prctl() API is kind of dumb on this point.  The existing end address may already be
                     * below the desired start address, in which case the kernel may have kicked this back due
                     * to a range-check failure (see linux/kernel/sys.c:validate_prctl_map() to see this in
                     * action).  The proper solution would be to have a prctl() API that could set both start+end
                     * simultaneously, or at least let us query the existing address to anticipate this condition
                     * and respond accordingly.  For now, we can only guess at the cause of this failure and try
                     * a workaround--which will briefly expand the arg space to something potentially huge before
                     * resizing it to what we want. */
                    log_debug_errno(errno, "PR_SET_MM_ARG_START failed, attempting PR_SET_MM_ARG_END hack: %m");

                    if (prctl(PR_SET_MM, PR_SET_MM_ARG_END, (unsigned long) nn + l + 1, 0, 0) < 0) {
                            log_debug_errno(errno, "PR_SET_MM_ARG_END hack failed, proceeding without: %m");
                            (void) munmap(nn, nn_size);
                            goto use_saved_argv;
                    }

                    if (prctl(PR_SET_MM, PR_SET_MM_ARG_START, (unsigned long) nn, 0, 0) < 0) {
                            log_debug_errno(errno, "PR_SET_MM_ARG_START still failed, proceeding without: %m");
                            goto use_saved_argv;
                    }
            } else {
                    /* And update the end pointer to the new end, too. If this fails, we don't really know what
                     * to do, it's pretty unlikely that we can rollback, hence we'll just accept the failure,
                     * and continue. */
                    if (prctl(PR_SET_MM, PR_SET_MM_ARG_END, (unsigned long) nn + l + 1, 0, 0) < 0)
                            log_debug_errno(errno, "PR_SET_MM_ARG_END failed, proceeding without: %m");
            }

关于c - 通过 argv 操作进程名称和参数,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/57749629/

相关文章:

c - 对于 Z3 中的所有量词

linux - 将文件重命名为数字,从特定数字开始

linux - Linux下如何分别压缩多个文件夹

linux - 远程桌面 Azure Linux VM

创建指向文件的指针数组

无法定义共享内存对象的大小

c - 使用结构的矩阵

c - 使用 system() 执行 shell 脚本返回 256。这是什么意思?

c - 将文件放入 main 函数的 argv[] 中

c++ - 将 yyin 更改为 argv[1] Flex & Bison