我正在尝试创建一个执行某些任务并退出的程序。但是,如果用户在终端上输入一些字符串,我想退出程序。
我想解决的方法: 创建执行主任务的父进程。主要任务有一个循环。 创建一个子进程,它等待用户的输入。如果用户输入退出字符串,则子进程将更新变量并自行终止。父进程中的循环将检查变量的值,如果子进程更改了该值,它也将退出。如果用户未输入退出字符串,则父进程将完成任务并自行终止。此刻我也想杀死孩子的过程。 我怎么能这样做?
由于
答案 0 :(得分:3)
当主进程退出时,您可以使用atexit()
运行清理功能。
但是,进程(假设您正在讨论POSIX和经典进程)不与子进程共享数据,因此您的标志变量的更改将不会在父进程中显示。
更简洁的解决方案是使用系统提供的交互(即用户按Ctrl + C,这将导致tty向会话负责人(前台进程)发送SIGINT
,您可以陷阱以便干净地退出)或使用主应用程序中的select()/poll()
检查用户输入。
答案 1 :(得分:2)
感谢您说出您在这里想要实现的目标。
您可能会发现创建单个多线程进程要容易得多,而不是创建两个单线程进程。
线程#1在循环中工作并定期检查变量exit_flag
。如果设置了变量,则调用exit
。如果它完成了工作,则会调用exit
。
线程#2等待来自用户的输入。如果获得输入,则设置变量exit_flag
。
这样,当你调用exit
时,两个线程都会自动退出。您将需要使用互斥锁/信号量或其他类型的同步,例如,如果您有libatomic-ops
:
#include "atomic_ops.h"
AO_t exit_flag;
// In thread #2
AO_store(&exit_flag, 1);
// In thread #1
while (!AO_load(&exit_flag)) {
...
}
关于上述代码的警告:设置全局标志只会这样,因为主线程仅读取全局标志而只读取其他内容。如果您需要在线程之间发送多个单词,则需要某种同步机制,例如,互斥锁,信号量或AO_store_release
与AO_store_acquire
配对。
如果使用进程而不是线程:由于进程默认情况下不共享可变内存,因此它不像“更新变量”那么容易。每个进程都有自己的一组私有变量。您需要设置共享内存区域或以其他方式进行通信(例如,信号,管道,套接字),这对您来说是额外的工作。
这是一个快速完整的例子。主线程确实“工作”,涉及从5倒数。当它达到0时,它退出并打印一条消息。第二个线程读取用户输入,当它读取以'e'
开头的行时,它设置一个标志,导致主线程退出。
第二个线程仅作为分离的线程创建,因为我们不需要加入它。它不会影响这个特定程序的正确性。
如果您致电exit
,则应终止程序中的所有主题,除非您的平台出现问题。
注意:此示例需要libatomic-ops
并假设您正在使用POSIX线程。
#include <stdio.h>
#include <pthread.h>
#include <stdlib.h>
#include <atomic_ops.h>
#include <errno.h>
static AO_t exit_flag;
#define fail(x) fail_func(x, __LINE__)
static void fail_func(int code, int line)
{
fprintf(stderr, "line %d: failed with code %d\n", line, code);
abort();
}
static void *input_thread(void *param)
{
char buf[16], *p;
(void) param;
while (1) {
p = fgets(buf, sizeof(buf), stdin);
if (!p)
break;
if (buf[0] == 'e')
break;
else
puts("unknown command");
}
AO_store(&exit_flag, 1);
return NULL;
}
int main(int argc, char *argv[])
{
pthread_attr_t attr;
pthread_t thread2;
int r, c;
struct timespec t;
t.tv_sec = 1;
t.tv_nsec = 0;
r = pthread_attr_init(&attr);
if (r) fail(r);
// Note: making the thread detached is not actually necessary.
r = pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
if (r) fail(r);
r = pthread_create(&thread2, &attr, input_thread, NULL);
if (r) fail(r);
c = 5;
while (1) {
if (AO_load(&exit_flag)) {
puts("thread 1 exiting (canceled)");
exit(1);
}
printf("counter = %d\n", c);
if (c == 0) {
puts("thread 1 exiting (finished)");
exit(0);
}
c--;
r = nanosleep(&t, NULL);
if (r) fail(errno);
}
return 0;
}
示例输出(用户输入为粗体):
$ ./a.out counter = 5 counter = 4 counter = 3 counter = 2 counter = 1 counter = 0 thread 1 exiting (finished) $ ./a.out counter = 5 counter = 4 exit thread 1 exiting (canceled)
答案 2 :(得分:2)
当您分叉子进程时,父进程将接收该进程的进程ID。如果您希望按需终止,可以通过SIGTERM
功能向孩子发送kill()
信号。确保在发送wait()
信号后致电SIGTERM
,以防止子进程成为&#34; zombie&#34;过程
如果要做的只是从子进程获取用户输入的字符串,您可以使用popen
和cat
启动子进程,使用{ {1}},回显用户在命令行上输入的内容。为了避免阻塞读取到从管道创建的流,您可以使用cat
获取与流关联的文件描述符,然后使用fileno()
或poll
超时值。完成父项或查看用户输入的终止字符串后,它只需调用select
即可终止pclose()
进程。