Главная > Блог > Назначить процессу имя в Си

Назначить процессу имя в Си

Приходит время, когда процессу нужно задать своё имя. Причины бывают разные, допустим нам просто не нравиться при вывод команды «ps» типа: «./a.out arg1 arg2» или же в консольной программе мы указываем секретные данные типа: «./a.out --password=my_password». В любом случае получим эстетическую радость от записи типа «my-prog».

Есть несколько способов переименовать свой процесс:

  • Первый способ использовать команду «prctl (PR_SET_NAME, "my-prog", 0, 0, 0);». Вроде бы всё хорошо но не имя процесса правильно отображается не везде «ps» и «htop» отображают разные имена, т.к. одна программа использует файловую систему «/proc», а другая системные вызовы.
  • Можно задать имя процесса с помощью команды «execl( "./a.out", "my-prog", ... );» (или любую другую функцию семейства «exec*»). Удобно использовать такую конструкцию когда процессу не нужно указывать передавать параметры клонируемому процессу, а если же главный процесс уже выполнил некоторые действия (допустим разобрал аргументы и создал необходимые переменные) их придётся как то передать порождаемому процессу.
  • Поместить название процесса в строку «argv[0]». Тогда процесс изменит название но в программах «ps» и «htop» будет отображаться по разному. Есть ещё один недостаток это длина названия процесса не может превышать размеры строк в массиве «argv[]», т.е. есть процесс запущен как «./a.out p1 p2» то для названия процесса у нас выделено 14 символов, включая «\0». Но ограничение на размер названия процесса можно снять с помощью массива «environ» и далее я покажу как.

Я расскажу про вариант с изменением имени процесса с помощью изменения строк в массиве «argv» и с использованием команды «prctl». Чтобы задать имя процесса строкой любой длины мы будем использовать массив «environ».

Массив «environ»

Массив «environ» идёт сразу за массивом «argv» и этим мы воспользуемся.

Для начала разберёмся в массиве «environ». Массив «environ» содержит строки с переменными окружения типа: «PARAM=value» или так «PARAM=value1:value2:value3». Доступ к этой переменной можно получить двумя способами:

  • Первый способ (популярный) с указанием «extern char ** environ;» как внешней переменной.
  • Второй способ это указании функции «main()» дополнительной третьей переменной, примерно так: «int main (int argc, char ** argv, char ** environ)»

Последнее значение массива содержит указатель на «NULL», т.е. перебирать массив мы будем без знания его размера. Такой способ задания массива «указания указателя на NULL в конце» часто используют чтобы не использовать дополнительную переменную для указания размера массива. У массива «argv» последнее значение также содежит указатель на «NULL», поэтому можно обойтись без переменной «argc», чтобы перебирать массив.

Пример распечатки массива «argv» и «environ».

#include <stdio.h>

extern char ** environ;

int main (int argc, char ** argv/*, char ** environ*/)
{
	char ** i;

	printf ("argv:\n");
	i = argv;
	while (*i != NULL)
	{
		printf ("\t%s\n", *i);
		i++;
	}

	printf ("\nenviron:\n");
	i = environ;
	while (*i != NULL)
	{
		printf ("\t%s\n", *i);
		i++;
	}

	return 0;
}

Вывод:

./a.out p1 p2
argv:
	./a.out
	p1
	p2

environ:
	PATH=/usr/local/bin:/usr/bin:/bin:/usr/local/games:/usr/games
	SHELL=/usr/bin/tcsh
	...
	...
	...
	HOME=/home/hharek

Вывод «argv» и «environ» с адресами

argv:
	[0]  - (char **)0x7fffa7aae228 - (char *)0x7fffa7aaeafa - "./a.out"
	[1]  - (char **)0x7fffa7aae230 - (char *)0x7fffa7aaeb02 - "p1"
	[2]  - (char **)0x7fffa7aae238 - (char *)0x7fffa7aaeb05 - "p2"
	[3]  - (char **)0x7fffa7aae240 - (char *)0x0

environ:
	[0]  - (char **)0x7fffa7aae248 - (char *)0x7fffa7aaeb08 - "PATH=/usr/local/bin:/usr/bin:/bin:/usr/local/games:/usr/games"
	[1]  - (char **)0x7fffa7aae250 - (char *)0x7fffa7aaeb3c - "SHELL=/usr/bin/tcsh"
	...
	...
	...
	[46] - (char **)0x7fffa7aae3b8 - (char *)0x7fffa7aaefe7 - "HOME=/home/hharek"
	[47] - (char **)0x7fffa7aae3c0 - (char *)0x0

Как говорил выше массив «environ» идёт сразу за «argv» (в большинстве функций случаев, хотя нигде об этом прямо не говорится). Это даёт возможность расширить массив «argv» до необходимого, но для начала нужно скопировать «environ» и переменной «extern char ** environ» назначить новый адрес, т.к. старый адрес будет указывать «не туда» (возможно на участок строки с название процесса). Копировать нужно ещё для того, т.к. переменная «extern char ** environ» используется системными функциями.

Копируем массив «environ» и «argv».

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

extern char ** environ;

int main (int argc, char ** argv/*, char ** environ*/)
{
	/* Копируем argv и чистим */
	char ** argv_old = argv;							/* Сохраняем ссылку на старый «argv» */
	argv = malloc (sizeof (char *) * (argc + 1));		/* «argv» указывает на новый массив */
	for (unsigned int i = 0; i < argc; i++)
	{
		argv[i] = malloc (sizeof(char) * (strlen(argv_old[i]) + 1));
		strcpy (argv[i], argv_old[i]);

		/* Чистим старый массив */
		memset (argv_old[i], '\0', strlen(argv_old[i]));
	}
	argv[argc] = NULL;	/* Последний элемент массива указывает на NULL */

	/* Вычисляем размер environ */
	int environ_size = 0;
	for (int i = 0; environ[i] != NULL; i++)
	{
		environ_size++;
	}

	/* Копируем в environ новый массив и чистим */
	char ** environ_old = environ;											/* Сохраняем ссылку на старый «environ» */
	environ = malloc (sizeof (char *) * (environ_size + 1));		/* «environ» указывает на новый массив */
	for (unsigned int i = 0; i < environ_size; i++)
	{
		environ[i] = malloc (sizeof(char) * (strlen(environ_old[i]) + 1));
		strcpy (environ[i], environ_old[i]);

		/* Чистим старый массив */
		memset (environ_old[i], '\0', strlen(environ_old[i]));
	}
	environ[environ_size] = NULL;	/* Последний элемент массива указывает на NULL */

	return 0;
}

Назначаем имя процессу.

Для этого необходимо на адрес старого «argv[0]» скопировать строку с названием. И далее использовать функцию «prctl». Сообственно это основной код, а всё что сверху лишь подготовка.

const char * proc_title = "my-prog";

strcpy (argv_old[0], proc_title);
prctl (PR_SET_NAME, proc_title, 0, 0, 0); /* Прописываем имя потока */

Важно понимать, что недостаточно просто назначить переменной «argv[0]» новую строку, т.е. «argv[0] = strdup("my-prog");», это работать не будет, т.к. переменная содержит адрес, который потом используют такие программы как «ps», «htop» и другие.

Название процессу также можно задать в символах UTF-8, но это как говориться «на свой страх и риск».

Полный код программы.

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/prctl.h>
#include <unistd.h>

extern char ** environ;

int main (int argc, char ** argv/*, char ** environ*/)
{
	/* Копируем argv и чистим */
	char ** argv_old = argv;							/* Сохраняем ссылку на старый «argv» */
	argv = malloc (sizeof (char *) * (argc + 1));		/* «argv» указывает на новый массив */
	for (unsigned int i = 0; i < argc; i++)
	{
		argv[i] = malloc (sizeof(char) * (strlen(argv_old[i]) + 1));
		strcpy (argv[i], argv_old[i]);

		/* Чистим старый массив */
		memset (argv_old[i], '\0', strlen(argv_old[i]));
	}
	argv[argc] = NULL;	/* Последний элемент массива указывает на NULL */

	/* Вычисляем размер environ */
	int environ_size = 0;
	for (int i = 0; environ[i] != NULL; i++)
	{
		environ_size++;
	}

	/* Копируем в environ новый массив и чистим */
	char ** environ_old = environ;											/* Сохраняем ссылку на старый «environ» */
	environ = malloc (sizeof (char *) * (environ_size + 1));		/* «environ» указывает на новый массив */
	for (unsigned int i = 0; i < environ_size; i++)
	{
		environ[i] = malloc (sizeof(char) * (strlen(environ_old[i]) + 1));
		strcpy (environ[i], environ_old[i]);

		/* Чистим старый массив */
		memset (environ_old[i], '\0', strlen(environ_old[i]));
	}
	environ[environ_size] = NULL;	/* Последний элемент массива указывает на NULL */

	/* Назначаем имя процессу */
	const char * proc_title = "my-prog";

	strcpy (argv_old[0], proc_title);
	prctl (PR_SET_NAME, proc_title, 0, 0, 0);		/* Назначаем имя потоку */

	/* Фоном работаем */
	while (1)
	{
		sleep (1);
	}

	return 0;
}

Источники:

си, имя процесса, environ, argv

Добавить комментарий

x

Добавить