Как запустить отдельный процесс?

Тема в разделе "WASM.UNIX", создана пользователем drem1lin, 19 фев 2017.

Метки:
  1. drem1lin

    drem1lin Member

    Публикаций:
    0
    Регистрация:
    17 мар 2009
    Сообщения:
    300
    Добрый вечер! Ищу помощи, я совсем недавно начал писать под Linux и в частности под Android, поэтому многим мой вопрос может показаться глупым. Я хочу запустить программку, которая складывает два числа, из другой программы для этого написал небольшую функцию. Вызывается как то так
    Код (Text):
    1. RunFile("SimpleApp");
    Код (Text):
    1. int RunFile(char const* FileName)
    2. {
    3.   string LaunchName(".//");
    4.   LaunchName.append(FileName);
    5.   char *arg[] = {const_cast<char*>(FileName), NULL};
    6.   int res = 0;
    7.   int pid = 0;
    8.   switch(pid=fork()) {
    9.   case -1:
    10.         printf("fork error %d\n", pid); /* произошла ошибка */
    11.         res = -1; /*выход из родительского процесса*/
    12.     break;
    13.   case 0:
    14.     printf("execv start\n"); /* произошла ошибка */
    15.     execv(LaunchName.c_str(), arg);[spoiler][/spoiler]
    16.     printf("execv end\n");
    17.     res = 0;
    18.     break;
    19.   default:
    20.     exit(0);
    21.     break;
    22.   }
    23.   return res;
    24. }
    В общем получилось что-то странное, пока работает основное приложение, второе живет и корректно обрабатывает свой ввод, но если родитель умер, то умер сразу и ребенок, а как сделать так, что бы ребенок остался жив?
     
  2. T800

    T800 Member

    Публикаций:
    0
    Регистрация:
    7 дек 2006
    Сообщения:
    293
    Адрес:
    Moscow
    Видимо нужно сбросить сигнал (читайте о функе signal).
     
  3. psh3nka

    psh3nka Active Member

    Публикаций:
    0
    Регистрация:
    21 янв 2017
    Сообщения:
    104
    Можно использовать двойной форк. Это когда родитель запускает чайлда, а чайлд уже запускает нужный нам процесс. Тогда при килле родителя, прибивается он сам и его чайлд, а grandchild'а усыновляет init.
    Сам недавно сталкаивался с подобной проблемой, но писал на Bash. Выглядит так:
    Код (Bash):
    1.  
    2. xPID=$(xautolock $sleeper >/dev/null & echo $!)
    3.  
    Здесь я запускаю демона xautolock через двойной форк и выкидываю вверх его pid, чтобы потом обрабатывать. При килле основного процесса, xautolock остается жив и переходит к init'у. Как-то так. Если есть вопросы, то спршивайте :)
    UPD: а лучше загуглите "double fork", когда искал, то видел реализации на Сях.
     
    drem1lin нравится это.
  4. drem1lin

    drem1lin Member

    Публикаций:
    0
    Регистрация:
    17 мар 2009
    Сообщения:
    300
    Огромное спасибо! Не представляю, как я должен был сам до такого дойти!
    в данный момент получается следующее:
    Код (Text):
    1. int RunFile(char const* FileName)
    2. {
    3.     //http://thinkiii.blogspot.ru/2009/12/double-fork-to-avoid-zombie-process.html
    4.     string LaunchName(".//");
    5.     LaunchName.append(FileName);
    6.     char *arg[] = {const_cast<char*>(FileName), NULL};
    7.     int res = 0;
    8.     pid_t pid_1 = 0;
    9.     pid_t pid_2 = 0;
    10.     int status=0;
    11.     switch(pid_1=fork()) {
    12.     case -1:
    13.         printf("fork error %d\n", pid_1); /* произошла ошибка */
    14.         res = -1; /*выход из родительского процесса*/
    15.         break;
    16.     case 0:
    17.         printf("fork 2\n");
    18.         pid_2=fork();
    19.         if(pid_2)
    20.             exit(0);
    21.         else if(!pid_2)
    22.         {
    23.             printf("execv start\n"); /* произошла ошибка */
    24.             execv(LaunchName.c_str(), arg);
    25.             printf("execv end\n");
    26.         }
    27.         else
    28.         {
    29.             res = -1;
    30.             printf("fork 2 error %d\n", pid_2);
    31.         }
    32.         break;
    33.     default:
    34.         waitpid(pid_1, &status, 0);
    35.         break;
    36.     }
    37.     return res;
    38. }
    Запускается такая программа
    Код (Text):
    1.  
    2. #include <stdio.h>
    3. int main() {
    4.  int a,b;
    5.  printf("Enter two numbers a and b: ");
    6.  scanf("%d %d", &a, &b);
    7.  printf("a+b = %d\n", a+b);
    8.  return 0;
    9. }
    10.  
    Результат в терминале
    Код (Text):
    1.  
    2. root@hammerhead:/data/local # ./loader                                        
    3. fork 2
    4. execv start
    5. root@hammerhead:/data/local # Enter two numbers a and b: a+b = -1225222659
    6. 3 4
    7. sush: 3: not found
    8.  
    не очень понятно почему так, и почему он ожидает ввода после вывода a+b?
     
  5. drem1lin

    drem1lin Member

    Публикаций:
    0
    Регистрация:
    17 мар 2009
    Сообщения:
    300
    Попробовал сегодня сделать следующее, заменить код запускаемого приложения на
    Код (Text):
    1. int main() {
    2.     for(int i = 0; i<20;i++)
    3.     {
    4.         printf("SimpleApp working\n");
    5.         sleep(5);
    6.     }
    7.  return 0;
    8. }
    Код выводит, прога работает, но почему та версия, не ждет ввода мне не понятно.
     
  6. psh3nka

    psh3nka Active Member

    Публикаций:
    0
    Регистрация:
    21 янв 2017
    Сообщения:
    104
    Хм, переписал Ваш пример на чистых сях, он запускает его ,но не дает ничего ввести. Далее я заподозрил проблему в stdin, и решил даблфоркнуть оконное приложение, которое писалось на yad - все отлично работает. Проблема 100% в stdin. Идем дальше. Повторный просмотр вашего кода выявил проблему: завершение родителя приводит к закрытию stdin, а у Вас в коде я не вижу wait()...
    Накладываем патч:
    Код (C):
    1.  
    2. ....
    3.  case 0:
    4.   printf("fork 2\n");
    5.   pid_2=fork();
    6.   if(pid_2){
    7.        wait(NULL); /* ждем грэндчайлд*/
    8.        exit(0);
    9.    }
    10.   else if(!pid_2)
    11. ....
    12.  

    Проблема скорее в scanf()...
     
    drem1lin нравится это.
  7. SadKo

    SadKo Владимир Садовников

    Публикаций:
    8
    Регистрация:
    4 июн 2007
    Сообщения:
    1.610
    Адрес:
    г. Санкт-Петербург
    Вообще, странно. При убивании родителя дочерний процесс должен сменить родителя на init. Вот пример:
    child.cpp:
    Код (C++):
    1.  
    2. #include <stdio.h>
    3. #include <unistd.h>
    4.  
    5. int main()
    6. {
    7.     while (1)
    8.     {
    9.         printf("Hello, world!\n");
    10.         sleep(1);
    11.     }
    12.  
    13.     return 0;
    14. }
    15.  
    root.cpp:
    Код (C++):
    1.  
    2. #include <stdio.h>
    3. #include <unistd.h>
    4. #include <sys/wait.h>
    5.  
    6. int main()
    7. {
    8.     pid_t pid = fork();
    9.  
    10.     if (pid == 0)
    11.     {
    12.         char *const argv[] = { NULL };
    13.         execv("./child", argv);
    14.     }
    15.     else if (pid > 0)
    16.     {
    17.         int status = 0;
    18.         waitpid(pid, &status, 0);
    19.     }
    20.  
    21.     return 0;
    22. }
    23.  
    Компиляем так:
    Код (Bash):
    1.  
    2. g++ root.cpp -o root
    3. g++ child.cpp -o child
    4.  
    Запускаем:
    Код (Bash):
    1.  
    2. ./root
    3. Hello, world!
    4. Hello, world!
    5. Hello, world!
    6. Hello, world!
    7. ...
    8.  
    Смотрим в процессы:
    Код (Bash):
    1.  
    2. ps afxu
    3. ...
    4. sadko    24745  0.0  0.0  18668  2380 pts/19   Ss   фев13   0:00  \_ /bin/bash
    5. sadko    27593  0.1  0.1  46500  9460 pts/19   S+   12:15   0:01  |   \_ /usr/bin/mc -P /tmp/mc-sadko/mc.pwd.24745
    6. sadko    27595  0.0  0.0  19012  6640 pts/33   Ss   12:15   0:00  |       \_ bash -rcfile .bashrc
    7. sadko    27995  0.0  0.0  12404  1404 pts/33   S+   12:31   0:00  |           \_ ./root
    8. sadko    27996  0.0  0.0  12408  1424 pts/33   S+   12:31   0:00  |               \_ [child]
    9.  
    10. ...
    11.  
    Видим, что child дочерний от root
    Убиваем root:
    Код (Bash):
    1.  
    2. kill -9 27995
    3.  
    Смотрим, что стало с child:
    Код (Bash):
    1.  
    2. ps afxu | grep child
    3. sadko    28009  0.0  0.0  10576  1460 pts/20   S+   12:33   0:00  |   \_ grep --color=auto child
    4. sadko    27996  0.0  0.0  12408  1424 pts/33   S    12:31   0:00 [child]
    5.  
    Видим, что child жив-здоров, только теперь наследуется от init.
    Смотрим по-прежнему в консоль, где запущен child:
    Код (Bash):
    1.  
    2. Hello, world!
    3. Hello, world!
    4. Hello, world!
    5. Hello, world!
    6. Hello, world!
    7. Hello, world!
    8. Hello, world!
    9. ...
    10.  
    Теперь child можно прибить только по kill:
    Код (Bash):
    1.  
    2. kill -9 27996
    3.  
     
    drem1lin нравится это.
  8. psh3nka

    psh3nka Active Member

    Публикаций:
    0
    Регистрация:
    21 янв 2017
    Сообщения:
    104
    Это я уже тупанул. В Bash юзал либу, а она подчищает всех чайлдов автоматически. Вообще это хороший тон: зачищать все дочерние процессы. Поэтому пришлось извращаться с double fork'ами. Проблема в wait() и stdin в общем-то...
     
    drem1lin нравится это.
  9. drem1lin

    drem1lin Member

    Публикаций:
    0
    Регистрация:
    17 мар 2009
    Сообщения:
    300
    psh3nka, SadKo, большое спасибо за ваши ответы. То есть в текущем варианте, у меня все будет работать, за исключением ввода? меня в целом это устроит... Что бы жил ввод надо дожидаться внука. А есть ли другой способ сделать ввод? как то подвязаться программно к stdin? это вопрос более теоретический.
     
  10. SadKo

    SadKo Владимир Садовников

    Публикаций:
    8
    Регистрация:
    4 июн 2007
    Сообщения:
    1.610
    Адрес:
    г. Санкт-Петербург
    drem1lin, проблема в том, что когда ваш родительский процесс убивается, stdin автоматом закрывается. И это, в принципе, логично, так как вы теряете связь с терминалом, из которого были запущены. Вернее, происходит так: интерпретатор ловит сигнал SIGCHLD (завершение дочернего процесса) и закрывает pipe, который был у него ассоциирован с stdin дочернего процесса. Разумеется, после одностроннего закрытия пайпа вы ничего не сможете оттуда прочитать.

    child.cpp:
    Код (C++):
    1.  
    2. #include <stdio.h>
    3. #include <unistd.h>
    4. #include <errno.h>
    5.  
    6. int main()
    7. {
    8.     char str[256];
    9.    
    10.     while (1)
    11.     {
    12.         printf("Please enter something: ");
    13.         fflush(stdout);
    14.         if (fgets(str, sizeof(str), stdin) == NULL)
    15.                 break;
    16.         printf("You've entered: %s", str);
    17.     }
    18.    
    19.     printf("STDIN has been closed, error code=%d\n", int(errno));
    20.  
    21.     return 0;
    22. }
    23.  
    root.cpp:
    Код (C++):
    1.  
    2. #include <stdio.h>
    3. #include <unistd.h>
    4. #include <sys/wait.h>
    5.  
    6. int main()
    7. {
    8.     pid_t pid = fork();
    9.    
    10.     if (pid == 0)
    11.     {
    12.         char *const argv[] = { NULL };
    13.         execv("./child", argv);
    14.     }
    15.     else if (pid > 0)
    16.     {
    17.         fclose(stdin);
    18.         int status = 0;
    19.         waitpid(pid, &status, 0);
    20.     }
    21.  
    22.     return 0;
    23. }
    24.  
    В данном случае, если вы прибьёте родителя, дочерний процесс отвалится по EIO (errno == 5).
     
    drem1lin нравится это.
  11. drem1lin

    drem1lin Member

    Публикаций:
    0
    Регистрация:
    17 мар 2009
    Сообщения:
    300
    Большое спасибо, я все понял