使用 LSF/bsub 基础架构运行 Tcl_DoOneEvent 时利用率高 CPU(~100%)

High CPU Utilization(~100%) while runing Tcl_DoOneEvent using LSF/bsub infrastructure

我已经在 Tcl 中创建了自己的事件循环,如下所示。当我 运行 下面的代码交互使用 tclsh 时,CPU 利用率接近 0% 而当我 运行 同样 运行 使用 bsub 时,CPU 利用率下降高达 100%。

我什至尝试使用下面的方法阻止读取调用,但这也无济于事。

int flag = fcntl(0, F_GETFL);
flag = flag & (~O_NONBLOCK);
(void) fcntl(0, F_SETFL, (long)flag);

这是什么原因,我该如何解决这个问题?

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <tcl.h>
#include <errno.h>
#include <fcntl.h>

void fwStdinKeyHandler(ClientData clientData, int mask)
{
  unsigned char c = 0;
  int rc = read(STDIN_FILENO, &c, 1);
  //printf("rc is : %d\n",rc);
  while (rc < 1 && errno == EINTR) {}
}


static void MainLoop(void)
{
  Tcl_CreateFileHandler(STDIN_FILENO, TCL_READABLE, fwStdinKeyHandler,    NULL);
  while (1) {
    Tcl_DoOneEvent(0);
  }
  fprintf(stdout,"Exit MainLoop\n");
  fflush(stdout);
}

static int Hello_Cmd(ClientData cdata, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[])
{
  Tcl_SetMainLoop(MainLoop);
  return TCL_OK;
}

 /*
  * Hello_Init -- Called when Tcl loads your extension.
  */
int DLLEXPORT  Cmd_Init(Tcl_Interp *interp)
{
  if (Tcl_InitStubs(interp, TCL_VERSION, 0) == NULL) {
    return TCL_ERROR;
  }
  /* changed this to check for an error - GPS */
  if (Tcl_PkgProvide(interp, "Hello", "1.0") == TCL_ERROR) {
    return TCL_ERROR;
  }
  Tcl_CreateObjCommand(interp, "doone_loop", Hello_Cmd, NULL, NULL);
  return TCL_OK;
}

如何制作?
1. 文件保存在,比方说,hello.c
2. gcc -fpic -c hello.c -I/usr/local/include
3. gcc -shared hello.o -o libcmd.so

如何运行?
运行我的文件包含:
加载libcmd.so
doone_loop

/usr/bin/tclsh 运行me => CPU 利用率接近 0%
bsub -q interactive -m "/usr/bin/tclsh 运行me" => CPU 利用率接近 100%

我认为问题是当命令在 运行 内部(系统)bsub(与之交谈)时,它是非交互的 运行。特别是,stdin 可能来自一个文件或 /dev/null,这两者都是 always 通知程序可读的(这是一堆低级系统的驯服版本调用)。这意味着您的代码被大量回调,几乎就像它在忙循环一样,产生大量 CPU 用法。

由于这是操作系统级别的行为,您的方法根本行不通。您需要检测自己是否处于这种情况(也许 isatty()?) 如果事件处理程序无法正常工作,则不安装它。

对于非交互式 shell,没有终端,因此也没有标准输入通道,因此读取调用 returns 为零。因此,我们需要在读取 returns 0.

后在 fwStdinkeyhandler 中添加以下代码
if(rc==0) {
   Tcl_DeleteFileHandler(0); 
}