- [The]
putenv(char *string);
[...] вызов кажется фатальным.
Да, это смертельно опасно. Это было сохранено в POSIX (1988), потому что это был предшествующий уровень техники. Механизм setenv()
появился позже. Исправление: Стандарт POSIX 1990 гласит в §B.4.6.1 «Дополнительные функции putenv () и clearenv () были рассмотрены, но отклонены ". В спецификации Single Unix (SUS) версии 2 1997 года перечислены putenv()
, но не setenv()
или unsetenv()
. Следующая редакция (2004) также определила как setenv()
и unsetenv()
.
Поскольку он не копирует переданную строку, вы не можете вызвать ее с локальным именем, и нет гарантии, что выделенная куча строка не будет перезаписана или случайно удалена.
Вы правы, что локальная переменная почти всегда является плохим выбором для передачи putenv()
- исключения неясны до такой степени, что почти не существуют. Если строка выделена в куче (с malloc()
и др.), Вы должны убедиться, что ваш код не изменяет ее. Если это так, он одновременно изменяет среду.
Более того (хотя я не проверял это), поскольку одно из применений переменных среды - передача значений в дочернюю среду, это кажется бесполезным, если дочерний процесс вызывает одну из exec*()
функций. Я не прав в этом?
Функции exec*()
делают копию среды и передают ее исполняемому процессу. Там нет никаких проблем.
Страница man Linux указывает, что glibc 2.0-2.1.1 отказался от вышеуказанного поведения и начал копировать строку, но это привело к утечке памяти, которая была исправлена в glibc 2.1.2. Мне не ясно, что это за утечка памяти или как ее исправить.
Утечка памяти возникает из-за того, что после того, как вы вызвали putenv()
со строкой, вы не можете снова использовать эту строку для каких-либо целей, потому что вы не можете определить, используется ли она по-прежнему, хотя вы можете изменить значение, перезаписав его (с неопределенными результатами, если вы измените имя на переменную среды, найденную в другой позиции в среде). Таким образом, если вы выделили пространство, классический putenv()
пропустит его, если вы снова измените переменную. Когда putenv()
начал копировать данные, выделенные переменные перестали ссылаться, потому что putenv()
больше не сохранял ссылку на аргумент, но пользователь ожидал, что среда будет ссылаться на него, поэтому произошла утечка памяти. Я не уверен, что это было за исправление - я бы ожидал, что оно вернется к старому поведению.
setenv()
копирует строку, но я не знаю точно, как это работает. Пространство для среды выделяется при загрузке процесса, но оно фиксировано.
Исправлено исходное пространство среды; когда вы начинаете изменять его, правила меняются. Даже с putenv()
исходная среда изменяется и может увеличиваться в результате добавления новых переменных или в результате изменения существующих переменных для получения более длинных значений.
Есть ли здесь какое-то (произвольное?) Соглашение? Например, выделение большего количества слотов в массиве указателей строки env, чем используется в настоящее время, и перемещение нулевого завершающего указателя вниз при необходимости?
Вот что, вероятно, будет делать механизм setenv()
. (Глобальная) переменная environ
указывает на начало массива указателей на переменные окружения. Если он указывает на один блок памяти одновременно и другой блок в другое время, то среда переключается, вот так.
Распределена ли память для новой (скопированной) строки в адресном пространстве самой среды, и если она слишком велика для вас, просто получите ENOMEM?
Ну, да, вы могли бы получить ENOMEM, но вам пришлось бы очень стараться. А если вы слишком увеличите объем среды, возможно, вы не сможете правильно запустить другие программы - либо среда будет усечена, либо операция exec завершится неудачей.
Учитывая вышеперечисленные проблемы, есть ли основания предпочитать putenv (), а не setenv ()?
- Использовать
setenv()
в новом коде. - Обновитьстарый код для использования
setenv()
, но не делайте его главным приоритетом. - Не используйте
putenv()
в новом коде.