Linux C程序修改进程名称

Posted 彼方丶

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Linux C程序修改进程名称相关的知识,希望对你有一定的参考价值。

1、前言

Linux C程序运行时,进程的名称通常就是argv[0],而通过修改内存中argv[0]存储的内容就可以修改进程名了。下面对此作详细介绍。

2、命令行参数(argc, argv)以及环境变量(environ)介绍

2.1、C程序典型的存储空间布局

如图所示,是C程序典型的存储空间布局,可以很明显看到命令行参数环境变量处在最顶端,而且是挨在一起的,命令行参数后面紧接着就是环境变量,具体的内容大家可以自行看一下《UNIX环境高级编程》第七章,下图也是从里面摘录出来的
在这里插入图片描述

2.2、argc, argv介绍

每个C语言程序都必须有一个称为main的函数,一般形式为int main(int argc, char* argc[]),改函数作为程序启动的起点。执行程序时,命令行参数通过argcargv这两个两个入参传递给main函数。第一个表示命令行参数的个数,而第二个参数则是一个指向命令行参数的指针数组,每一参数都是C类型字符串,以空字符\\0结尾的,而最后一个参数argc[argv]则是一个空指针NULL。其中第一个字符串(argv[0]),则是该程序的名称,也就是运行时的进程名。

内存示例如图所示
在这里插入图片描述

2.3、environ介绍

每一个进程都有与其相关的称为环境列表(environment list)的字符串数组。其中每个字符串都以名称=值(name=value)形式定义。新进程在创建时,会继承其父进程的环境副本,子进程创建后,父、子进程均可更改各自的环境变量,且这些变更对对方而言不再可见。
在C程序中,可以使用全局变量extern char** environ获取环境变量表(C运行时启动代码定义了该变量并以当前环境列表位置为其赋值)environargv参数比较相似,是一个指针数组,数组每个成员指向C类型字符串,最后一个指针也是一个NULL

内存示例如图所示
在这里插入图片描述

2.4、编写程序验证修改进程名是否可以成功

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>

int main(int argc, char* argv[])
{
    sleep(8);
    strcpy(argv[0], "112233445566");
    while (1);                                                                                                           
 
    return 0;
}

代码如上,可以看到,程序先休眠8秒钟,然后修改argv[0]的值,编译运行一下,运行结果如下,可以看到,一开始进程名是./all,8秒后就变成了112233445566,修改成功,而且我们也能看出一点细节,那就是修改后的进程名超过了原先的进程名也没有报错,有可能的原因是argc[0]后面的内存也是可用的(其实从内存布局就可以看出来可用了)
在这里插入图片描述

2.5、查看进程名变长之后影响了那部分内存的数据

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>

extern char** environ;

int main(int argc, char* argv[])
{
    printf("begin environ:\\n");
    for (int i = 0; environ[i] != NULL && i < 5; i++)
        printf("%s\\n", environ[i]);

    sleep(5);
    strcpy(argv[0], "112233445566");
    printf("\\n\\n");

    printf("end environ:\\n");
    for (int i = 0; environ[i] != NULL && i < 5; i++)
        printf("%s\\n", environ[i]);

    return 0;
}

代码如上,为了方便显示,环境变量只打印前五个,看看效果就行。运行结果如下图所示,可以看到,一开始打印的第一个环境变量为XDG_SESSION_ID=3585,当修改完进程名之后,该变量变为445566,原先进程名为./all,加上结束符是6个字节,剩余四个字节的内容也就是445566刚好把第一个环境变量的内存给覆盖了(这里没有传入参数,所以覆盖的是环境变量的内存)。
在这里插入图片描述

2.6、结论

Linux C程序中传入参数和环境变量是存放在一段连续的内存空间的,而argvenviron这两个指针数组里面存放的指针就是指向这些内存的

3、Redis修改进程名的做法

Redis作为一个优秀的企业级存储框架,里面有许多值得学习的地方,而里面也有一段代码修改进程名的,接下来看看Redis是如何实现的

extern char **environ;

static struct {
	/* original value */
	const char *arg0;

	/* title space available */
	char *base, *end;

	 /* pointer to original nul character within base */
	char *nul;

	_Bool reset;
	int error;
} SPT;

void spt_init(int argc, char *argv[]) {
    char **envp = environ;
	char *base, *end, *nul, *tmp;
	int i, error, envc;

	if (!(base = argv[0]))
		return;

	nul = &base[strlen(base)];
	end = nul + 1;

	for (i = 0; i < argc || (i >= argc && argv[i]); i++) {
		if (!argv[i] || argv[i] < end)
			continue;

		if (end >= argv[i] && end <= argv[i] + strlen(argv[i]))
			end = argv[i] + strlen(argv[i]) + 1;
	}

	for (i = 0; envp[i]; i++) {
		if (envp[i] < end)
			continue;

		if (end >= envp[i] && end <= envp[i] + strlen(envp[i]))
			end = envp[i] + strlen(envp[i]) + 1;
	}
	envc = i;

	if (!(SPT.arg0 = strdup(argv[0])))
		goto syerr;

	if (!(tmp = strdup(program_invocation_name)))
		goto syerr;

	program_invocation_name = tmp;

	if (!(tmp = strdup(program_invocation_short_name)))
		goto syerr;

	program_invocation_short_name = tmp;

    /* Now make a full deep copy of the environment and argv[] */
	if ((error = spt_copyenv(envc, envp)))
		goto error;

	if ((error = spt_copyargs(argc, argv)))
		goto error;

	SPT.nul  = nul;
	SPT.base = base;
	SPT.end  = end;

	return;
syerr:
	error = errno;
error:
	SPT.error = error;
}

从代码中可以看出以下几点:

  1. base指向的是argv[0]nul指向argv[1],而end经过两个for循环遍历之后指向的是传入参数+环境变量内存空间的最末尾
  2. 使用strdup函数将原本的进程名复制给SPT.arg0
  3. spt_copyenvspt_copyargs函数的作用是将原先的传入参数(从argv[1]开始)和环境变量复制到一个新的内存区域,然后原先的指针数组里元素指向新的内存地址,完成了数据的迁移
  4. 经过上面的步骤之后,argv[0]还是指向原来的地址,而其他的传入参数以及环境变量均指向新的内存空间了,现在就可以任意修改进程名而不影响其他数据了

4、总结

本文介绍了如何修改Linux C程序的进程名,以及Redis源代码中是如何在保持原有传入参数以及环境变量的情况下,对程序的进程名进行修改。测试代码比较简单,大家可以自行编写程序去进行测试验证

以上是关于Linux C程序修改进程名称的主要内容,如果未能解决你的问题,请参考以下文章

linux c 退出进程的代码

linux c 退出进程的代码

VC用API修改其他进程的Edit或Button的文本。

Android:如何在 C 语言中更改进程名称?

VB6中如何获得指定进程名称

linux下如何运行程序?