프로그램이 실행 도중 변경된 환경변수의 값은 반영할수 없는건가요??

xoduddk123의 이미지

#include
#include

int main(void){
char *value;
while(1){
value = getenv("AUTO");
if(atoi(value)==1)
printf("AUTO = 1\n");
else if(atoi(value)==0)
printf("AUTO = 0\n",atoi(value));
else
printf("else \n");
usleep(10000000);
}
}

위와같이 프로그래밍을 하였습니다

while문을 무한으로 돌면서 10초간격으로 getenv함수를 통해 AUTO라는 환경변수의 값을 읽어서 출력하도록했습니다

테스트를 할떄

프로그램을 &을 통하여 백그라운드로 실행을 시켜서 계속 백그라운드에서 돌게한뒤

expoort AUTO=0으로 환경변수를 바꿔주고 source .profile 를 통해서 값도 적용되게끔했는데

echo $AUTO 를 하면 0 이 나오는데 이미 실행되어지고 있던 프로그램에서는 AUTO값이 1로 안바뀌고 1로 읽혀지는데 프로그램 실행도중에는

바뀌어진 환경변수에 대해서 적용을 못하는건가요 ?ㅠㅠㅠ

jick의 이미지

환경변수란 게 시스템 전체가 함께 사용하는 환경변수가 있는 게 아니라, *모든 프로세스가* 각각의 환경변수를 가지고 있는 것입니다.
Shell에서 프로그램을 실행시키면 프로그램이 시작하는 순간 셸의 환경변수를 모두 복사해서 프로그램이 들고 있게 됩니다.

따라서 그 이후에 셸의 환경변수를 바꾼다고 해도 이미 시작한 프로그램의 환경변수는 바뀌지 않습니다. 이후 새로 시작하는 프로그램의 환경변수가 바뀔 뿐이죠.

mirheekl의 이미지


다소 무식한 방법이지만 매번 새 프로세스를 띄우는 방법으로 폴링을 하면 환경변수의 값을 제대로 읽어 올 수 있겠네요. (이렇게 띄우는 프로그램은 아마 메인 프로그램과 별도로 스케줄링하셔야 할듯.. 본체에서 띄우면 환경변수를 상속받을 수가 있어서요. 이러면 결과는 똑같겠죠. 이부분 따로 설정이 가능한지는 모르겠습니다.) 그리고 그 폴링 프로그램과 메인 프로그램이 IPC로 업데이트된 환경변수를 알려주면 될듯 합니다. 꼭 환경변수를 써야 한다면 한번 고려해보세요.

하지만 애초에 런타임에 변경되어야 하는 부분에는 환경변수를 쓰지 않는 게 더욱 좋은 방법이 아닐까 합니다.

--

jick의 이미지

새 프로세스를 띄운다고 해도 프로세스를 띄우는 프로세스의 환경변수를 상속받기 때문에 결국 마찬가지일 것 같습니다.

그 프로세스가 환경변수 변화를 어떻게든 감지하고 (.profile을 계속 읽어서?) 이를 반영한다면 또 모르지만, 그렇게 코드를 짠다면 새로 프로세스를 띄울 필요도 없지요.

mirheekl의 이미지

부모 프로세스가 있어야만 스케줄링이 가능한건 아닙니다.

--

chanik의 이미지

좀 제한적이고 지저분하지만 부모프로세스와 자식프로세스가 서로
환경변수 변경사항을 알아낼 수 있는 방법이 생각나서 테스트해봤습니다.

주의: 쓰기에 안전한 방법인지는 모름.



아래와 같이 ps 명령으로 특정 프로세스의 환경변수 내용을 열람할 때,

$ ps eww p 4380
  PID TTY      STAT   TIME COMMAND
 4380 pts/1    Ss     0:00 -bash CVS_RSH=ssh DISPLAY=localhost:10.0 EDITOR=vim G_BROKEN_FILENAMES=1 HISTSIZE=1000 HOME=/home/test01 HOSTNAME=testpc INPUTRC=/etc/inputrc LANG=ko_KR.UTF-8 LESS=-iMSx4 -FX LESSOPEN=|/usr/bin/lesspipe.sh %s LOGNAME=test01 MAIL=/var/spool/mail/test01 PAGER=less PATH=/usr/local/sbin:/sbin:/usr/sbin:/usr/kerberos/bin:/usr/local/bin:/bin:/usr/bin ....

위의 환경변수의 내용은 ps가 /proc/4380/environ 으로부터 얻어내는 것입니다.
아래와 같이 ps에 의존하지 않고 직접 그 내용을 확인할 수도 있습니다.
( 물론, 해당 프로세스에 대한 접근 권한은 필요합니다.
root 사용자의 프로세스에 대해 아무나 환경변수를 열람하지는 못하는 식입니다 )

$ strings /proc/4380/environ
CVS_RSH=ssh
DISPLAY=localhost:10.0
EDITOR=vim
G_BROKEN_FILENAMES=1
HISTSIZE=1000
HOME=/home/test01
HOSTNAME=testpc
INPUTRC=/etc/inputrc
LANG=ko_KR.UTF-8
LESS=-iMSx4 -FX
LESSOPEN=|/usr/bin/lesspipe.sh %s
LOGNAME=test01
MAIL=/var/spool/mail/test01
PAGER=less
PATH=/usr/local/sbin:/sbin:/usr/sbin:/usr/kerberos/bin:/usr/local/bin:/bin:/usr/bin:/home/test01/bin:/usr/hs/bin
...

아래의 명령들 중 아무것이나 써도 됩니다.

$ strings /proc/4380/environ
$ cat /proc/4380/environ | xargs -0 -L 1 echo
$ cat /proc/4380/environ | tr "\0" "\n"

/proc/?/environ 은 "VAR1=VALUE1\0VAR2=VALUE2\0...VARn=VALUEn\0" 형태의 문자열이며,
프로세스가 처음 구동될 때의 환경변수를 NULL 구분자로 모두 합쳐놓은 형태입니다.

그런데, 이후에 해당 프로세스에서 변경하거나 추가하는 내용은 /proc/?/environ에 반영되지 않더군요.
프로세스가 처음 구동될 때 부모에게서 물려받은 환경변수 내용은 그 크기에 맞게 스택에 할당되고
이후에 변경/추가되는 내용은 힙에 반영된다고 하는데, /proc/?/environ은 스택의 내용물을 보여주므로
마치 처음 구동시의 스냅샷같은 모습을 유지하게 되는 모양입니다.

이래서는 변경분을 부모/자식 프로세스가 서로 알아낼 수가 없겠죠.
하지만 방법이 없는 것은 아닙니다. 환경변수를 수정할 때 putenv() / setenv()를 이용하지 않고,
main() 함수 인자로 넘어온 env 포인터를 이용하여 직접 스택내용물을 수정하면 됩니다.
샘플을 하나 만들었습니다.

#include <unistd.h>
#include <stdio.h>
#include <string.h>
 
// <string.h>의 문자열함수를 이용해 /proc/?/environ 내용물을 직접 다뤄도 되지만
// 번거로와서 그냥 UNIX 도구를 사용.
void read_proc_env(pid_t pid, char *key)
{
    char cmd[256];
    sprintf(cmd, "strings /proc/%d/environ | grep '^%s='", pid, key);
    FILE *f = popen(cmd, "r");
    char buf[256];
    while (fgets(buf, sizeof(buf), f) != 0) {
        printf("READ: %s", buf);
    }
    pclose(f);
}
 
int main(int argc, char **argv, char **env)
{
    pid_t pid = fork();
    if(pid) {
        // parent process
        // search for the position of the parent variable
        while(*env) {
            //puts(*env);
            if(!strncmp(*env, "PARENT_VAR=", 11))
                break;
            env++;
        }
 
        if(!*env)   return 1;
        char *var = *env;
        int size = strlen(var) + 1;
 
        int i = -1;
        while(i-- > -6) {
            sleep(3);
            snprintf(var, size, "PARENT_VAR=%d", i);    // modify parent variable
            read_proc_env(pid, "CHILD_VAR");            // read child env
        }
    }
    else {
        // child process
        // search for the position of the child variable
        while(*env) {
            //puts(*env);
            if(!strncmp(*env, "CHILD_VAR=", 10))
                break;
            env++;
        }
 
        if(!*env)   return 1;
        char *var = *env;
        int size = strlen(var) + 1;
 
        int i = 5;
        while(i++ < 10) {
            sleep(3);
            snprintf(var, size, "CHILD_VAR=%d", i);     // modify child variable
            read_proc_env(getppid(), "PARENT_VAR");     // read parent env
        }
    }
 
    sleep(1);
 
    return 0;
}

샘플을 실행하면 실행 직후 fork()부터 합니다.
부모프로세스는 3초마다 자신의 환경변수중 PARENT_VAR 의 값을 변경(-1 ~ -6까지)하고
자식프로세스의 환경변수 가운데 CHILD_VAR 변수값을 읽어옵니다.
자식프로세스는 3초마다 자신의 환경변수중 CHILD_VAR 의 값을 변경(5 ~ 10까지)하고
부모프로세스의 환경변수 가운데 PARENT_VAR 변수값을 읽어옵니다.

실행해보면 변경된 내용물을 잘 읽어오는 것을 알 수 있습니다.

주의할 점은, 실행할 때 PARENT_VAR과 CHILD_VAR 환경변수가 정의되어 있어야 한다는 것과
필요한 공간을 충분히 확보해야 한다는 것입니다.

예를 들어, PARENT_VAR=000 식으로 정의하면 3바이트를 저장할 수 있는 공간이 확보될 것입니다.
PARENT_VAR=0 처럼 1바이트 공간만 정의해서 프로그램을 구동해놓고
프로세스 내에서 PARENT_VAR=-1 처럼 두 바이트를 저장하는 일이 생기지 않도록 주의해야 합니다.

$ gcc -o print_env print_env.c
$ PARENT_VAR=000 CHILD_VAR=000 ./print_env
READ: CHILD_VAR=6
READ: PARENT_VAR=-2
READ: CHILD_VAR=7
READ: PARENT_VAR=-3
READ: CHILD_VAR=8
READ: PARENT_VAR=-4
READ: CHILD_VAR=9
READ: PARENT_VAR=-5
READ: CHILD_VAR=10
READ: PARENT_VAR=-6

부모나 자식 프로세스가 bash같은 shell일 경우엔 이 방식은 쓸 수 없습니다.
부모와 자식 모두 C/C++ 처럼 스택의 환경변수 공간을 직접 건드릴 수 있어야 합니다.

서로의 환경변수를 파악해야 하는 것이 아니라, 한 쪽에서만 파악하면 되는 상황이라면
부모나 자식 중 하나가 shell 같은 것이어도 상관없겠죠.

ymir의 이미지

running process 의 environment variable 을 변경하는 건, 그냥 안 된다고 보시면 됩니다.
트릭으로 비슷한 효과를 낼 수 있을지는 몰라도, 실행중인 프로세스에게 어떤 상태 변화를 알리는데 일반적으로 쓸 수 있는게 아닙니다.

signal handler 를 써서 상태 변수를 바꾸거나, SIGHUP 등에 설정 파일 등을 읽어들이도록 하는게 낫고..
아니면 주기적으로 특정 파일의 변경 여부를 체크해서, 파일이 변경된 경우에 파일을 읽어들이는게 낫습니다.
원래 질문과 같은 경우에는, 단순히 파일 존재 여부로 상태를 체크해도 되겠네요.

되면 한다! / feel no sorrow, feel no pain, feel no hurt, there's nothing gained.. only love will then remain.. 『 Mizz 』

xoduddk123의 이미지

예상외로 너무많은 답변이 달려서 너무 감사한마음입니다.

간단한방법으로 IPC의 FIFO를 사용해서 해볼려고하고있습니다.

A와 B프로그램이 있다면 A는 백그라운드(&)로 돌고 환경변수가 바뀔때마다 B라는 프로그램을 한번실행하고 main()함수 맨마지막에 exit(1)을 줘서 다시 종료해서

B라는 프로그램이 실행될 시점의 환경변수를 A 프로그램에 FIFO를 통해서 전달하여 실행하고자 하는데 괜찮은 방법일까요??

ymir의 이미지

반대로 왜 꼭 환경 변수를 이용해야만 하는지 그 이유가 궁금해지네요.
오히려 그 때문에 프로세스 구동 방식마저 아주 보기 드문 형태로 가고 있는데 말이죠.

환경변수는 sshd 와 같이 fork 된 shell 을 통해 ssh 상태를 보여주거나..
특정 경로의 설정파일을 사용하기 힘든 경우, argument 로 전달하기 귀찮거나 하는 등 제한적인 경우 아니면..
일부러 그걸 쓸 이유가 없다고 보이는데 말이죠.

환경변수가 필요해 의해 바뀌는 상황이고, 프로그램이 항상 동작되는 상태로 있어야 하는게 아니라면..
스크립트를 통해 기존의 프로세스를 kill 하고, 새로 export 한 후에 프로그램을 다시 시작하는게 낫지 않을까요?

되면 한다! / feel no sorrow, feel no pain, feel no hurt, there's nothing gained.. only love will then remain.. 『 Mizz 』

xoduddk123의 이미지

답변이 느려서 죄송합니다.

그렇게 할려고 생각을 하고있었으나 필요에 의해서 환경변수가 바뀔때마다 kill하는 방식은 안쓰도록 하였습니다.

댓글 달기

Filtered HTML

  • 텍스트에 BBCode 태그를 사용할 수 있습니다. URL은 자동으로 링크 됩니다.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>
  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.

BBCode

  • 텍스트에 BBCode 태그를 사용할 수 있습니다. URL은 자동으로 링크 됩니다.
  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param>
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.

Textile

  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • You can use Textile markup to format text.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>

Markdown

  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • Quick Tips:
    • Two or more spaces at a line's end = Line break
    • Double returns = Paragraph
    • *Single asterisks* or _single underscores_ = Emphasis
    • **Double** or __double__ = Strong
    • This is [a link](http://the.link.example.com "The optional title text")
    For complete details on the Markdown syntax, see the Markdown documentation and Markdown Extra documentation for tables, footnotes, and more.
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>

Plain text

  • HTML 태그를 사용할 수 없습니다.
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.
  • 줄과 단락은 자동으로 분리됩니다.
댓글 첨부 파일
이 댓글에 이미지나 파일을 업로드 합니다.
파일 크기는 8 MB보다 작아야 합니다.
허용할 파일 형식: txt pdf doc xls gif jpg jpeg mp3 png rar zip.
CAPTCHA
이것은 자동으로 스팸을 올리는 것을 막기 위해서 제공됩니다.