当 运行 作为后台进程时,防止 read() 系统调用返回 0
Prevent read() systemcall returing with 0 when run as background process
我有一个软件能够在单独的线程中从 stdin 读取命令以进行调试。当我的软件 运行s 作为前台进程 read
按预期运行时,它会阻塞并等待用户输入,即线程休眠。
当软件运行作为后台进程时,read
不断returns0
(可能检测到EOF?)。
这里的问题是,这个特定的读取是在一个 while(true)
循环中。它 运行 尽可能快地窃取了我嵌入式设备上宝贵的 CPU 负载。
我尝试将 /dev/null
重定向到进程,但行为是一样的。我正在 运行在 ARM Cortex A5 板上设置我的自定义 Linux。
下面是有问题的代码,运行 在它自己的线程中:
char bufferUserInput[256];
const int sizeOfBuffer = SIZE_OF_ARRAY(bufferUserInput);
while (1)
{
int n = read(0, bufferUserInput, sizeOfBuffer); //filedes = 0 equals to reading from stdin
printf("n is: %d\n", n);
printf("Errno: %s",strerror(errno));
if (n == 1)
{
continue;
}
if ((1 < n)
&& (n < sizeOfBuffer)
&& ('\n' == bufferUserInput[n - 1]))
{
printf("\r\n");
bufferUserInput[n - 1] = '[=11=]';
ProcessUserInput(&bufferUserInput[0]);
} else
{
n = 0;
}
}
我正在寻找一种方法来防止 read
在 运行 在后台等待用户输入(当然永远不会出现)时不断返回。
read()
在后台进程中从终端读取时不会 return 0。
它要么继续阻塞,同时导致 SIGTTIN
被发送到进程(这可能会打破阻塞并导致 retval=-1,errno=EINTR
被 returned 或它导致 retval=-1, errno EIO
如果 SIGTTIN
忽略。
下面的代码片段演示了这一点:
#include <unistd.h>
#include <stdio.h>
#include <signal.h>
int main()
{
char c[256];
ssize_t nr;
signal(SIGTTIN,SIG_IGN);
nr = read(0,&c,sizeof(c));
printf("%zd\n", nr);
if(0>nr) perror(0);
fflush(stdout);
}
您显示的代码片段不可能测试显示 0-returns,因为您从未测试 return 值中的零性。
当 stdin
不是终端时,read
返回 0,因为您位于文件末尾。 read
仅在读取所有可用输入后阻塞,当将来 可能 有更多输入时,这被认为可能用于终端、管道、套接字等,但不适用于常规文件也不是 /dev/null
。 (是的,另一个进程可能会使常规文件变大,但 read
的规范中并未考虑这种可能性。)
忽略其他人指出的有关您的读取循环的各种问题(无论如何您都应该修复 ,因为这将使从用户读取调试命令更加可靠)解决您现在遇到的问题的最简单的代码更改是:在启动时检查 stdin 是否是终端,如果不是,则不要启动调试线程。您可以使用 isatty
函数执行此操作,该函数在 unistd.h
.
中声明
#include <stdio.h>
#include <unistd.h>
// ...
int main(void)
{
if (isatty(fileno(stdin)))
start_debug_thread();
// ...
}
(根据您的使用上下文,当 stdin
是管道或套接字时 运行 调试线程也可能有意义,但我个人不会打扰,我会依赖ssh
必要时提供远程(伪)终端。)
如果您从 shell 脚本 在 "background"(如 ./program &
)中启动程序,它的标准输入将从/dev/null
(有一些 exceptions)。
尝试从 /dev/null
读取将始终 return 0 (EOF)。
示例(在 linux 上):
sh -c 'ls -l /proc/self/fd/0 & wait'
... -> /dev/null
sh -c 'dd & wait'
... -> 0 bytes copied, etc
上面 link 中的修复应该也适用于您:
#! /bin/sh
...
exec 3<&0
./your_program <&3 &
...
我有一个软件能够在单独的线程中从 stdin 读取命令以进行调试。当我的软件 运行s 作为前台进程 read
按预期运行时,它会阻塞并等待用户输入,即线程休眠。
当软件运行作为后台进程时,read
不断returns0
(可能检测到EOF?)。
这里的问题是,这个特定的读取是在一个 while(true)
循环中。它 运行 尽可能快地窃取了我嵌入式设备上宝贵的 CPU 负载。
我尝试将 /dev/null
重定向到进程,但行为是一样的。我正在 运行在 ARM Cortex A5 板上设置我的自定义 Linux。
下面是有问题的代码,运行 在它自己的线程中:
char bufferUserInput[256];
const int sizeOfBuffer = SIZE_OF_ARRAY(bufferUserInput);
while (1)
{
int n = read(0, bufferUserInput, sizeOfBuffer); //filedes = 0 equals to reading from stdin
printf("n is: %d\n", n);
printf("Errno: %s",strerror(errno));
if (n == 1)
{
continue;
}
if ((1 < n)
&& (n < sizeOfBuffer)
&& ('\n' == bufferUserInput[n - 1]))
{
printf("\r\n");
bufferUserInput[n - 1] = '[=11=]';
ProcessUserInput(&bufferUserInput[0]);
} else
{
n = 0;
}
}
我正在寻找一种方法来防止 read
在 运行 在后台等待用户输入(当然永远不会出现)时不断返回。
read()
在后台进程中从终端读取时不会 return 0。
它要么继续阻塞,同时导致 SIGTTIN
被发送到进程(这可能会打破阻塞并导致 retval=-1,errno=EINTR
被 returned 或它导致 retval=-1, errno EIO
如果 SIGTTIN
忽略。
下面的代码片段演示了这一点:
#include <unistd.h>
#include <stdio.h>
#include <signal.h>
int main()
{
char c[256];
ssize_t nr;
signal(SIGTTIN,SIG_IGN);
nr = read(0,&c,sizeof(c));
printf("%zd\n", nr);
if(0>nr) perror(0);
fflush(stdout);
}
您显示的代码片段不可能测试显示 0-returns,因为您从未测试 return 值中的零性。
当 stdin
不是终端时,read
返回 0,因为您位于文件末尾。 read
仅在读取所有可用输入后阻塞,当将来 可能 有更多输入时,这被认为可能用于终端、管道、套接字等,但不适用于常规文件也不是 /dev/null
。 (是的,另一个进程可能会使常规文件变大,但 read
的规范中并未考虑这种可能性。)
忽略其他人指出的有关您的读取循环的各种问题(无论如何您都应该修复 ,因为这将使从用户读取调试命令更加可靠)解决您现在遇到的问题的最简单的代码更改是:在启动时检查 stdin 是否是终端,如果不是,则不要启动调试线程。您可以使用 isatty
函数执行此操作,该函数在 unistd.h
.
#include <stdio.h>
#include <unistd.h>
// ...
int main(void)
{
if (isatty(fileno(stdin)))
start_debug_thread();
// ...
}
(根据您的使用上下文,当 stdin
是管道或套接字时 运行 调试线程也可能有意义,但我个人不会打扰,我会依赖ssh
必要时提供远程(伪)终端。)
如果您从 shell 脚本 在 "background"(如 ./program &
)中启动程序,它的标准输入将从/dev/null
(有一些 exceptions)。
尝试从 /dev/null
读取将始终 return 0 (EOF)。
示例(在 linux 上):
sh -c 'ls -l /proc/self/fd/0 & wait'
... -> /dev/null
sh -c 'dd & wait'
... -> 0 bytes copied, etc
上面 link 中的修复应该也适用于您:
#! /bin/sh
...
exec 3<&0
./your_program <&3 &
...