Как запустить процесс во время выполнения в c - PullRequest
0 голосов
/ 21 сентября 2018

У меня проблема C, когда один процесс запускает другие процессы во время выполнения, и этот процесс должен осуществлять межпроцессное взаимодействие с другими процессами.Теперь я знаком с основами fork () и execl (), но кроме этого мои знания о процессах довольно просты (особенно о том, как запускать процесс во время выполнения), поэтому любая помощь будет высоко оценена.

1 Ответ

0 голосов
/ 21 сентября 2018

Самая простая форма связи между одним процессом, запущенным отдельно, - это передача информации через аргументы командной строки.Помимо этого, есть много способов IPC, включая общий файл, который отправитель открывает для записи, получатель для чтения.

Это то, что я уже написал в своем комментарии.Я не удержался, чтобы сделать небольшую демонстрацию.

testSimpleIPC.c:

#include <assert.h>
#include <ctype.h>
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <unistd.h>

/* This is done in one program which is started
 * - without command line arguments to behave as parent
 * - with command line argument to behave as child.
 */
int mainParent(void);
int mainChild(int argc, char **argv);

int main(int argc, char **argv)
{
  if (argc == 1) { /* becomes parent */
    return mainParent();
  } else {
    return mainChild(argc, argv);
  }
}

int mainParent(void)
{
  const char *ipcFileName = "testSimpleIPC.txt";
  /* open file for IPC */
  FILE *fIn = fopen(ipcFileName, "a+");
  if (!fIn) {
    perror("Failed to fopen() for reading");
    return -1;
  }
  /* fork and start child */
  int pid;
  switch (pid = fork()) {
    case -1: /* failed */
      perror("Failed to fork()");
      return -1;
    case 0: /* returned in child process */
      execlp("./testSimpleIPC",
        "./testSimpleIPC", ipcFileName, NULL);
      /* If this point is reached execlp failed! */
      perror("Failed to execlp()");
      return -1;
    default:
      printf("testSimpleIPC spawned child with PID %d\n", pid);
  }
  /* read messages from child */
  char buffer[80];
  for (;;) {
    if (fgets(buffer, sizeof buffer, fIn) != NULL) {
      /* clip line-ending from buffer end */
      for (size_t len = strlen(buffer); len--;) {
        if (isspace(buffer[len])) buffer[len] = '\0';
        else break;
      }
      /* report */
      printf("Parent received :'%s'\n", buffer);
      /* bail out in case */
      if (strcmp(buffer, "quit") == 0) break;
    }
  }
  fclose(fIn);
  /* done */
  return 0;
}

int mainChild(int argc, char **argv)
{
  assert(argc == 2);
  const char *const ipcFileName = argv[1];
  /* write messages to parent */
  FILE *fOut = fopen(ipcFileName, "a");
  if (!fOut) {
    perror("Failed to fopen() for writing");
    return -1;
  }
  for (int i = 1; i < 10; ++i) {
    printf("Sending 'message %d'...\n", i);
    if (fprintf(fOut, "message %d\n", i) < 0
      || fflush(fOut)) {
      perror("Failed to fprintf()");
      return -1;
    }
  }
  if (fprintf(fOut, "quit\n") < 0 || fclose(fOut)) {
    perror("Failed to fprintf()");
    return -1;
  }
  /* done */
  return 0;
}

Скомпилировано и протестировано в cygwin64 в Windows 10:

$ gcc -std=c11 -o testSimpleIPC testSimpleIPC.c 

$ ./testSimpleIPC
testSimpleIPC spawned child with PID 27320
Sending 'message 1'...
Sending 'message 2'...
Parent received :'message 1'
Sending 'message 3'...
Parent received :'message 2'
Sending 'message 4'...
Parent received :'message 3'
Sending 'message 5'...
Parent received :'message 4'
Sending 'message 6'...
Parent received :'message 5'
Sending 'message 7'...
Parent received :'message 6'
Sending 'message 8'...
Parent received :'message 7'
Sending 'message 9'...
Parent received :'message 8'
Parent received :'message 9'
Parent received :'quit'

$

Примечания:

  1. Сначала я забыл передать путь к файлу исполняемого файла в качестве первого аргумента командной строки в execlp().Таким образом, ребенок начинался только с одного аргумента - признавая себя родителем.Отчаянное нажатие Ctrl C не помогло - мне пришлось убить xterm (до того, как это убило мою систему).Итак, не забывайте: argv[0] является условным обозначением пути к файлу самого исполняемого файла, но вы должны явно указать его в качестве аргумента в execlp().

  2. Последнее добавление былоfflush() в mainChild().До того, как я это сделал, ребенок написал все, прежде чем родитель даже начал получать.Хотя причина была для меня очевидна, я обнаружил, что стоит упомянуть.


Немного подробнее о execlp():

Я использовал execlp (3) - справочная страница Linux для вызова подробностей.

Подпись execlp() равна

int execlp(const char *file, const char *arg, ...);

Параметры:

  • file предоставляет имя файла, связанного с исполняемым файлом.
  • arg - это первый аргумент, передаваемый исполняемому файлу.
  • ... может быть произвольным числом дополнительных аргументов, передаваемых исполняемому файлу.

Обратите внимание, что последний переданный аргумент должен быть NULL, чтобы завершить список.(В противном случае вызов execlp() может привести к неопределенному поведению ).

Первым аргументом является неприятная ошибка (в execlp() 2 nd с именемarg).

При использовании argc и argv в int main(int argc, char **argv) мы используем, чтобы argc всегда было не менее 1, а argv[0] предоставляет путь к файлу, с которымСам исполняемый файл был назван.

Плохая новость: это соглашение (которое просто везде рассматривается).Итак, где-то нет встроенного автоматизма - вызывающий execlp() должен учитывать это:

повторяет имя файла исполняемого файла в качестве первого аргумента, перед предоставлением 0 ... n дополнительных аргументов, и незабудь окончательный NULL.

В приведенном выше примере:

      execlp("./testSimpleIPC",
        "./testSimpleIPC", ipcFileName, NULL);

Вспоминая мое 1. Примечание (выше), когда я забыл первый аргумент "./testSimpleIPC", содержимое ipcFileName стал первым и был передан дочернему процессу как argv[0].Поскольку в коде не используется argv[0], я не заметил этого.Однако тот факт, что argv[1] отсутствовал, привел к тому, что дочерний процесс снова идентифицировал себя как родительский.Итак, я получил лавину запущенных процессов, которые было трудно остановить.

На самом деле, я не забыл assert(argc == 2);, который проверяет ожидаемое значение argc.Однако, это активно только в коде отладки и не спасло меня, когда я компилировал без -g, так что assert стал неактивным.

Только сегодня я нашел 1251 процесс (оставленный после моего недавнего теста), который съел 40% загрузки процессора, пока мне не удалось остановить их с помощью

for I in $( ps | grep testSimpleIPC | awk '{ print $1 }' ); do kill $I ; done
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...