Я заранее извиняюсь за то, что это будет немного дампа кода, я обрезал как можно больше неважного кода:
// Global vars / mutex stuff
extern char **environ;
pthread_mutex_t env_mutex = PTHREAD_MUTEX_INITIALIZER;
int
putenv_r(char *string)
{
int len;
int key_len = 0;
int i;
sigset_t block;
sigset_t old;
sigfillset(&block);
pthread_sigmask(SIG_BLOCK, &block, &old);
// This function is thread-safe
len = strlen(string);
for (int i=0; i < len; i++) {
if (string[i] == '=') {
key_len = i; // Thanks Klas for pointing this out.
break;
}
}
// Need a string like key=value
if (key_len == 0) {
errno = EINVAL; // putenv doesn't normally return this err code
return -1;
}
// We're moving into environ territory so start locking stuff up.
pthread_mutex_lock(&env_mutex);
for (i = 0; environ[i] != NULL; i++) {
if (strncmp(string, environ[i], key_len) == 0) {
// Pointer assignment, so if string changes so does the env.
// This behaviour is POSIX conformant, instead of making a copy.
environ[i] = string;
pthread_mutex_unlock(&env_mutex);
return(0);
}
}
// If we get here, the env var didn't already exist, so we add it.
// Note that malloc isn't async-signal safe. This is why we block signals.
environ[i] = malloc(sizeof(char *));
environ[i] = string;
environ[i+1] = NULL;
// This ^ is possibly incorrect, do I need to grow environ some how?
pthread_mutex_unlock(&env_mutex);
pthread_sigmask(SIG_SETMASK, &old, NULL);
return(0);
}
Как следует из названия, я пытаюсь закодировать потокобезопасную, безопасную для асинхронных сигналов реентерабельную версию putenv
. Код работает в том смысле, что он устанавливает переменную среды, как putenv
, но у меня есть несколько проблем:
- Мой метод сделать его безопасным для асинхронных сигналов кажется немного неуклюжим, просто блокируя все сигналы (конечно, кроме SIGKILL/SIGSTOP). Или это самый правильный способ сделать это.
- Не является ли место блокировки моего сигнала слишком консервативным? Я знаю, что
strlen
не гарантирует безопасность асинхронного сигнала, а это означает, что моя блокировка сигнала должна произойти заранее, но, возможно, я ошибаюсь. - Я почти уверен, что это потокобезопасно, учитывая, что все функции потокобезопасны и что я блокирую взаимодействие с
environ
, но я хотел бы, чтобы было доказано обратное. - Я действительно не слишком уверен в том, является ли он реентерабельным или нет. Хотя это не гарантируется, я полагаю, что если я отмечу два других поля, он, скорее всего, будет реентерабельным?
Я нашел другое решение этого вопроса здесь, в котором они просто устанавливают соответствующую блокировку сигнала и блокировку мьютекса (больные рифмы), а затем вызывают putenv
в обычном режиме. Это действительно? Если это так, то это, очевидно, намного проще, чем мой подход.
Извините за большой блок кода, надеюсь, я установил MCVE. Для краткости мне не хватает проверки ошибок в моем коде. Спасибо!
Вот остальная часть кода, включая основной, если вы хотите проверить код самостоятельно:
#include <string.h>
#include <errno.h>
#include <pthread.h>
#include <stdlib.h>
#include <stdio.h>
#include <signal.h>
// Prototypes
static void thread_init(void);
int putenv_r(char *string);
int
main(int argc, char *argv[]) {
int ret = putenv_r("mykey=myval");
printf("%d: mykey = %s\n", ret, getenv("mykey"));
return 0;
}
key_len = i;
, а неkey_len = i - 1;
- person Klas Lindbäck   schedule 21.12.2016i - 1
, потому что хочуstrncmp
key часть key=value.i
в момент разрыва цикла находится в позиции знака=
, поэтому мне нужно вычесть 1 из индекса. - person Daniel Porteous   schedule 21.12.2016strncmp
требуется количество символов, а не индекс последнего символа. Если у вас естьfoo=bar
, вы получитеi = 3
иkey_len = 2
, а вашstrncmp
будет сравнивать только два первых символа... - person Klas Lindbäck   schedule 21.12.2016malloc()
к блокировке сигналов имеет небезопасность асинхронного сигнала? Я что-то упускаю. - person Andrew Henle   schedule 21.12.2016malloc
прерывается (возможно, по сигналу, отсюда и мое замешательство), то его нельзя безопасно возобновить, потому что это может испортить стек? - person Daniel Porteous   schedule 21.12.2016malloc()
будет прерван сигналом, он будет невидим для вызывающего абонента. - person Andrew Henle   schedule 21.12.2016malloc()
из обработчика сигнала, и в этом случае небезопасность асинхронного сигнала является проблемой. - person Daniel Porteous   schedule 21.12.2016