【问题标题】:How to get around the Linux "Too Many Arguments" limit如何绕过 Linux “Too Many Arguments” 限制
【发布时间】:2015-10-10 06:39:02
【问题描述】:

我必须将 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)。"

【问题讨论】:

    标签: linux shell amazon-sqs


    【解决方案1】:

    编辑:

    我终于能够将 edit (4))。但是,请仔细阅读我是如何做到的,并自行决定这是否是您想要的方式。至少你应该能够从我的发现中理解为什么你被“卡住”了。


    随着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
    

    ...

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

    我现在不能尝试,但如果可能的话,在您的系统上切换到大页面模式(页面大小 4 MB)可能会解决这个问题。

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


    编辑:

    (1) 根据this answer,可以通过在内核配置中启用CONFIG_TRANSPARENT_HUGEPAGE 并将CONFIG_TRANSPARENT_HUGEPAGE_MADVISE 设置为n,将x86_64 Linux 的页面大小更改为1 MB。

    (2) 使用上述配置更改 getconf PAGESIZE 重新编译我的内核后,仍然返回 4096。 根据this answer,还需要CONFIG_HUGETLB_PAGE,我可以通过CONFIG_HUGETLBFS 加入。我现在正在重新编译,将再次测试。

    (3) 我重新编译了启用CONFIG_HUGETLBFS 的内核,现在/proc/meminfo 包含the corresponding section of the kernel documentation 中提到的相应HugePages_* 条目。 但是,根据getconf PAGESIZE 的页面大小仍然没有改变。因此,虽然我现在应该能够通过 mmap 调用请求大页面,但确定 MAX_ARG_STRLEN 的内核默认页面大小仍然固定为 4 KB。

    (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
    

    所以现在限制从 128 KB 移动到了 256 KB,正如预期的那样。 不过我不知道潜在的副作用。 据我所知,我的系统似乎运行良好。

    【讨论】:

    • 谢谢,这正是我所要求的。在我的情况下,这有点意味着它是不可能的(即不能运行自定义内核)但是知道我被卡住并知道为什么非常有用。顺便说一句,错误消息“参数列表太长”具有误导性,特别是因为该部分实际上已修复。问题在于单个论点的大小。
    【解决方案2】:

    只需将参数放入某个文件中,然后修改您的程序以接受来自文件的“参数”。一个常见的约定(特别是 GCC 和其他几个 GNU 程序使用的)是像 @/tmp/arglist.txt 这样的参数要求您的程序从文件 /tmp/arglist.txt 中读取参数,通常每个参数一行

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

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

    (如果您可以重新编译内核,您可能会尝试将 - 增加到比可用 RAM 小得多的 2 的更大功率,例如增加到 2097152 - ARG_MAX#define-d in @ 987654329@ 在重新编译内核之前)

    在其他方面,没有办法规避该限制(请参阅 execve(2) 手册页及其参数大小限制和环境部分) - 一旦您提高了堆栈限制(使用setrlimit(2)RLIMIT_STACK,通常在父 shell 中内置 ulimit)。否则你需要处理它。

    【讨论】:

    • 无法修改foo 程序。我真的在调用 AWS SQS 命令行工具,消息必须作为值传递。
    • @swdev:您需要在问题中指定约束。另一种方法是使用 xargs 将参数传递给 sqs。
    • @tuxdna,问题是单个参数(sqs 消息)很大。我认为xargs 不会有帮助,因为我无法拆分消息。是的,我试图通过一种复制 args 太大问题的方法来简化关于 SQS CLI 的问题。这可能适得其反。只是重新设计呼叫的答案对我来说并没有真正有用。实际上,证明这是不可能的答案很有用,然后我用更小的消息重新设计我的程序。例如,我对上面的引用有什么误解。它说有可能,为什么不可能(即“你被击中了”)
    • 即使您设法增加 CLI 参数大小限制,系统资源也总会受到限制。所以最好找到替代方法来完成你的任务。
    • 也许您可以尝试aws sqs send-message-batch 命令,它类似于send-message,但从文件中获取其输入,包括消息正文。
    猜你喜欢
    • 2022-12-19
    • 2019-11-27
    • 1970-01-01
    • 2020-09-26
    • 2012-11-26
    • 1970-01-01
    • 2021-11-16
    • 2022-08-04
    • 1970-01-01
    相关资源
    最近更新 更多