如何绕过Linux“Too Many Arguments”限制

时间:2015-10-10 06:39:02

标签: linux shell amazon-sqs

我必须将256Kb的文本作为参数传递给“aws sqs”命令,但是在命令行中运行的限制大约为140Kb。许多地方已经讨论过it been solved in the Linux kernel as of 2.6.23 kernel

但无法让它发挥作用。我正在使用3.14.48-33.39.amzn1.x86_64

以下是一个简单的测试示例:

#!/bin/bash

SIZE=1000
while [ $SIZE -lt 300000 ]
do
   echo "$SIZE"
   VAR="`head -c $SIZE < /dev/zero | tr '\0' 'a'`"
   ./foo "$VAR"
   let SIZE="( $SIZE * 20 ) / 19"
done

foo脚本只是:

#!/bin/bash
echo -n "$1" | wc -c

我的输出是:

117037
123196
123196
129680
129680
136505
./testCL: line 11: ./foo: Argument list too long
143689
./testCL: line 11: ./foo: Argument list too long
151251
./testCL: line 11: ./foo: Argument list too long
159211

那么,如何修改testCL脚本的问题是它可以传递256Kb的数据?顺便说一下,我已经尝试将ulimit -s 65536添加到脚本中,但没有帮助。

如果这显然是不可能的,我可以处理这个问题,但是你可以从上面的链接中阐明这个引用

  

“虽然Linux不是Plan 9,但在2.6.23中,Linux正在添加变量   参数长度。从理论上讲,你不应经常打“论证   列表太长“错误再次,但这个补丁也限制了最大值   参数长度为最大堆栈限制的25%(ulimit -s)。“

2 个答案:

答案 0 :(得分:3)

<强> 编辑:

我终于能够将&lt; = 256KB作为单个命令行参数传递(参见底部的 edit(4))。但是,请仔细阅读我是如何做到的,并自行决定这是否是您想要的方式。至少你应该能够理解为什么你会被困'&#39;否则就是我发现的。

ARG_MAXulim -s / 4的耦合引入了MAX_ARG_STRLEN作为最大值。论证的长度:

/*
 *  linux/fs/exec.c
 *
 *  Copyright (C) 1991, 1992  Linus Torvalds
 */
  

...

#ifdef CONFIG_MMU
/*
 * The nascent bprm->mm is not visible until exec_mmap() but it can
 * use a lot of memory, account these pages in current->mm temporary
 * for oom_badness()->get_mm_rss(). Once exec succeeds or fails, we
 * change the counter back via acct_arg_size(0).
 */
  

...

static bool valid_arg_len(struct linux_binprm *bprm, long len)
{
 return len <= MAX_ARG_STRLEN;
}
  

...

#else
  

...

static bool valid_arg_len(struct linux_binprm *bprm, long len)
{
  return len <= bprm->p;
}

#endif /* CONFIG_MMU */
  

...

static int copy_strings(int argc, struct user_arg_ptr argv,
      struct linux_binprm *bprm)
{
  

...

    str = get_user_arg_ptr(argv, argc);
  

...

    len = strnlen_user(str, MAX_ARG_STRLEN);
    if (!len)
      goto out;

    ret = -E2BIG;
    if (!valid_arg_len(bprm, len))
      goto out;
  

...

}
  

...

MAX_ARG_STRLEN定义为linux/include/uapi/linux/binfmts.h中页面大小的32倍:

  

...

/*
 * These are the maximum length and maximum number of strings passed to the
 * execve() system call.  MAX_ARG_STRLEN is essentially random but serves to
 * prevent the kernel from being unduly impacted by misaddressed pointers.
 * MAX_ARG_STRINGS is chosen to fit in a signed 32-bit integer.
 */
#define MAX_ARG_STRLEN (PAGE_SIZE * 32)
#define MAX_ARG_STRINGS 0x7FFFFFFF
  

...

默认页面大小为4KB,因此您无法传递超过128KB的参数。

我现在无法尝试,但如果可能,请切换到巨大的页面模式(页面大小4MB)解决此问题。

有关详细信息和参考资料,请参阅this answera similar question on Unix & Linux SE

<强> 编辑:

<强> (1) 根据{{​​3}},可以通过在内核配置中启用x86_64并将CONFIG_TRANSPARENT_HUGEPAGE设置为CONFIG_TRANSPARENT_HUGEPAGE_MADVISE来将n Linux的页面大小更改为1MB。

<强> (2) 使用上面的配置重新编译我的内核后,getconf PAGESIZE仍会返回4096。 根据{{​​3}} CONFIG_HUGETLB_PAGE,我还需要通过CONFIG_HUGETLBFS提取。我现在正在重新编译并将再次测试。

<强> (3) 我在启用CONFIG_HUGETLBFS的情况下重新编译了我的内核,现在/proc/meminfo包含this answer中提到的相应HugePages_*条目。 但是,根据getconf PAGESIZE的页面大小仍未更改。因此,虽然我现在应该可以通过mmap调用请求大页面,但确定MAX_ARG_STRLEN的内核默认页面大小仍然固定为4KB。

<强> (4) 我将linux/include/uapi/linux/binfmts.h修改为#define MAX_ARG_STRLEN (PAGE_SIZE * 64),重新编译了我的内核,现在您的代码生成:

  

...

117037
123196
123196
129680
129680
136505
143689
151251
159211
  

...

227982
227982
239981
239981
252611
252611
265906
./testCL: line 11: ./foo: Argument list too long
279901
./testCL: line 11: ./foo: Argument list too long
294632
./testCL: line 11: ./foo: Argument list too long

所以现在限制从128KB移动到256KB。 我不知道潜在的副作用。 据我所知,我的系统似乎运行得很好。

答案 1 :(得分:2)

只需将参数放入某个文件中,然后修改程序即可接受&#34;参数&#34;从文件。一个常见的约定(特别是GCC和其他几个GNU程序使用)是像remembering last dir这样的参数要求你的程序从文件@/tmp/arglist.txt读取参数,通常每个参数一行

您可能通过长环境变量传递一些数据,但它们也是有限的(内核的限制实际上是/tmp/arglist.txt的大小初始堆栈,包含程序参数和环境)

或者,修改您的程序以通过某个配置文件进行配置,该文件包含您希望通过参数传递的信息。

(如果你可以重新编译你的内核,你可能会尝试增加 - 比你的可用内存小得多的两个更大的功率,例如2097152- main ARG_MAX重新编译内核之前在#define中使用-d

在其他方面,没有办法规避这种限制(参见execve(2)手册页及其参数和环境大小的限制部分) - 一旦提高了堆栈限制(将setrlimit(2)linux-4.*/include/uapi/linux/limits.h一起使用,通常在父shell中内置RLIMIT_STACK。否则你需要处理它。