• Используя копию командного интерпретатора, вызов system()
может инициировать процесс, исполняющий и бинарную программу, и скрипт на языке самого командного интерпретатора (shell), а также программный код на интерпретирующих языках, таких как Perl, Tcl/Tk и др. Многие из рассматриваемых ниже «чисто программных» способов могут загружать и исполнять только бинарный исполняемый код (по крайней мере, без использования ими весьма громоздких искусственных и альтернативных возможностей).
• Остановка родительского процесса в ожидании завершения порожденного также легко разрешается: просто запускайте дочерний процесс из отдельного потока [11] Детали создания потока и и частности передачи ему параметра обстоятельно рассматриваются далее.
:
#include
void* process(void* command) {
system((char*)command);
delete command;
return NULL;
}
int main(int argc, char *argv[]) {
...
char* comstr = "ls -l";
pthread_create(NULL, NULL, strdup(comstr), &process);
...
}
• Часто в качестве недостатка этого способа отмечают «автономность» и невозможность взаимодействия родительского и порожденного процессов.
Но для расширения возможностей взаимосвязи процессов можно прежде всего воспользоваться вызовом popen()
(POSIX 1003.1a), являющимся в некотором роде эквивалентом, расширяющим возможности system()
. Возможности popen()
часто упускаются из виду, так как в описаниях этот вызов относится не к области создания процессов, а к области программных каналов (pipe). Синтаксис этого вызова таков:
FILE* popen(const char* command, const char* mode);
где command
— командная строка, как и у system()
; mode
— режим создаваемого программного канала со стороны порождающего процесса: ввод ( mode
= «r») или вывод ( mode
= «w»). Любые другие значения, указанные для mode
, дают непредсказуемый результат.
В результате выполнения этой функции создается открытый файловый дескриптор канала (pipe), из которого породивший процесс может ( mode
= «r») читать (стандартный поток вывода дочернего процесса STDOUT_FILENO
) или в который может ( mode
= «w») писать (стандартный поток ввода дочернего процесса STDIN_FILENO
) стандартным образом, как это делается для типа FILE (в частности, с отработкой ситуации EOF).
Рассмотрим следующий пример. Конечно, посимвольный ввод/вывод — это не лучшее решение, и здесь мы используем его только для простоты:
int main(int argc, char** argv) {
FILE* f = popen("ls -l", "r");
if (f == NULL) perror("popen"), exit(EXIT_FAILURE);
char c;
while((с = fgetc(f)) != EOF )
cout << (islower(с) ? toupper(с) : c);
pclose(f);
return EXIT_SUCCESS;
}
Примечание
Новый процесс выполняется с тем же окружением, что и родительский. Процесс, указанный в команде, запускается примерно следующим эквивалентом:
spawnlp(P_NOWAIT, shell_command, shell_command, "-с", command, (char*)NULL);
где shell_command
— командный интерпретатор, специфицированный переменной окружения SHELL или утилита /bin/sh
. В этом кроется причина возможного различия в выполнении вызовов system()
и popen()
.
Если popen()
возвращает не NULL
, то выполнение прошло успешно. В противном случае устанавливается errno
: EINVAL
— недопустимый аргумент mode
, ENOSYS
— в системе не выполняется программа менеджера каналов. После завершения работы с каналом, созданным popen()
, он должен быть закрыт парной операцией pclose()
.
При использовании system()
в более сложных случаях, например при запуске в качестве дочернего собственного процесса, являющегося составной частью комплекса (до сих пор мы рассматривали в качестве дочерних только стандартные программы UNIX), причем запуск производится из отдельного потока (то есть без ожидания завершения, как предлагалось выше), мы можем реализовать сколь угодно изощренные способы взаимодействия с помощью механизмов IPC, например, открывая в дочернем процессе двунаправленные каналы к родителю.
Вызов fork()
создает клон (полную копию) вызывающего процесса в точке вызова. Вызов fork()
является одной из самых базовых конструкций всего UNIX-программирования. Его толкованию посвящено столько страниц в литературе, сколько не уделено никакому другому элементу API. Синтаксис этого вызова (проще по синтаксису не бывает, сложнее по семантике — тоже):
#include
pid_t fork(void);
Действие вызова fork()
следующее:
Читать дальше
Конец ознакомительного отрывка
Купить книгу