Fork Process / Read通过管道SLOW写入

时间:2012-09-18 07:59:44

标签: c linux tcp pipe lame

ANSWER

https://stackoverflow.com/a/12507520/962890

它太琐碎了.. args!但收到了很多好的信息。谢谢大家。

修改

链接到github:https://github.com/MarkusPfundstein/stream_lame_testing

原始帖子

我通过管道对IPC有一些疑问。我的目标是每个TCP / IP流接收MP3数据,通过LAME管道将其解码为wav,做一些数学运算并将其存储在磁盘上(作为wav)。我正在使用非阻塞IO。 让我感到恼火的是,tcp / ip读取的速度比管道跛足的速度快。当我发送~3 MB的mp3时,文件会在几秒钟内在客户端读取。在开始时,我也可以写入跛脚过程的标准输入,而不是停止写入,它会读取其余的mp3,如果完成,我可以再次写入跛脚。 4096字节大约需要1秒钟(从跛脚写入和读取)。这很慢,因为我想解码我的wav min 128kbs。

操作系统是这台微型计算机上的debian 2.6内核:

https://www.olimex.com/dev/imx233-olinuxino-maxi.html

65 MB RAM 400 MhZ

ulimit -n | grep管道返回512 x 8,表示4096即可。它是一个32位系统。

奇怪的是

my_process | lame --decode --mp3input - output.wav

非常快。

这是我的fork_lame代码(它必须将我的进程的stout连接到lame的stdin,反之亦然)

static char * const k_lame_args[] = {
  "--decode",
  "--mp3input",
  "-",
  "-",
  NULL
};

static int
fork_lame()
{
  int outfd[2];
  int infd[2];
  int npid;
  pipe(outfd); /* Where the parent is going to write to */
  pipe(infd); /* From where parent is going to read */
  npid = fork();
  if (npid == 0) {
    close(STDOUT_FILENO);
    close(STDIN_FILENO);
    dup2(outfd[0], STDIN_FILENO);
    dup2(infd[1], STDOUT_FILENO);
    close(outfd[0]); /* Not required for the child */
    close(outfd[1]);
    close(infd[0]);
    close(infd[1]);
    if (execv("/usr/local/bin/lame", k_lame_args) == -1) {
      perror("execv");
      return 1;
    }
  } else {
    s_lame_pid = npid;
    close(outfd[0]); /* These are being used by the child */
    close(infd[1]);
    s_lame_fds[WRITE] = outfd[1];
    s_lame_fds[READ] = infd[0];
  }
  return 0;
}

这是读写功能。请不要写在write_lame_in中。当我写入stderr而不是s_lame_fds [WRITE]时,输出几乎是即时的,所以它绝对是通过跛脚的管道。但为什么呢?

static int
read_lame_out() 
{
  char buffer[READ_SIZE];
  memset(buffer, 0, sizeof(buffer));
  int i;
  int br = read(s_lame_fds[READ], buffer, sizeof(buffer) - 1);
  fprintf(stderr, "read %d bytes from lame out\n", br);
  return br;
}

static int
write_lame_in()
{
  int bytes_written;
  //bytes_written = write(2, s_data_buf, s_data_len);
  bytes_written = write(s_lame_fds[WRITE], s_data_buf, s_data_len);
  if (bytes_written > 0) {
    //fprintf(stderr, "%d bytes written\n", bytes_written);
    s_data_len -= bytes_written;
    fprintf(stderr, "data_len write: %d\n", s_data_len);
    memmove(s_data_buf, s_data_buf + bytes_written, s_data_len);
    if (s_data_len == 0) {
      fprintf(stderr, "finished\n");
    }
  } 

  return bytes_written;
}

static int
read_tcp_socket(struct connection_s *connection)
{
  char buffer[READ_SIZE];
  int bytes_read;
  bytes_read = connection_read(connection, buffer, sizeof(buffer)-1);
  if (bytes_read > 0) {
    //fprintf(stderr, "read %d bytes\n", bytes_read);
    if (s_data_len + bytes_read > sizeof(s_data_buf)) {
      fprintf(stderr, "BUFFER OVERFLOW\n");
      return -1;
    } else {
      memcpy(s_data_buf + s_data_len,
             buffer,
             bytes_read);
      s_data_len += bytes_read;
    }
    fprintf(stderr, "data_len: %d\n", s_data_len);
  }
  return bytes_read;
}

选择的东西是非常基本的选择逻辑。当然,所有街区都是非阻塞的。

任何人都有任何想法?我真的很感激任何帮助; - )

3 个答案:

答案 0 :(得分:3)

糟糕!你检查了LAME输出了吗?

查看您的代码,特别是

static char * const k_lame_args[] = {
  "--decode",
  "--mp3input",
  "-",
  "-",
  NULL
};

if (execv("/usr/local/bin/lame", k_lame_args) == -1) {

表示您不小心忽略了--decode标志,因为LAME将是argv[0],而不是第一个参数(argv[1])。你应该使用

static char * const k_lame_args[] = {
  /* argv[0] */  "lame",
  /* argv[1] */  "--decode",
  /* argv[2] */  "--mp3input",
  /* argv[3] */  "-",
  /* argv[4] */  "-",
                 NULL
};

代替。

我认为你看到了减速因为你不小心重新压缩了MP3音频。 (我在一分钟之前就注意到了这一点,所以如果你省略--decode标志,那么LAME是否会这样做,但我相信它会这样做。)

答案 1 :(得分:2)

有可能存在某种阻塞问题。非阻塞管道(实际上并非非阻塞),导致您的结束阻塞,直到LAME消耗数据。

你能尝试一种替代方法吗?使用普通,阻塞管道和单独的线程(使用pthreads),其唯一目的是将数据从循环缓冲区写入LAME。然后,您的主线程将继续从TCP / IP连接填充循环缓冲区,并且还可以轻松跟踪和报告缓冲区级别 - 在开发和调试期间非常有用。一般而言,阻塞管道和螺纹比非阻塞管道更成功。

在Linux中,线程确实没有那么大的开销,所以即使在嵌入式架构上,你也应该习惯使用它们。你必须掌握的唯一技巧是为工作线程指定一个合理的堆栈大小 - 在这种情况下16384字节很可能就足够了 - 因为只有给进程的初始堆栈会自动增长并且线程堆栈默认是固定的大。

您需要示例代码吗?

编辑添加:

您的程序可能以稳定的速率从TCP / IP连接接收数据。但是,LAME以大块的形式使用数据。换句话说,情况就像一辆汽车被牵引,拖车猛拉并停下来,每次都有人扯进去:你的过程和LAME大部分时间都在等待另一个接收/发送更多数据。

答案 2 :(得分:1)

首先,这两个关闭不是必需的(实际上,你不应该这样做),因为后面的两个dup2会自动

close(STDOUT_FILENO);
close(STDIN_FILENO);