Argument list too long分析

这两天在Android eclair版本上增加WML浏览功能,编译时总是出现Argument list too long的错误。WebKit里源文件太多,没有WML时还可以编译过去,但加上了WML之后,命令行参数确实很长了,ar后面跟的参数大概都有几百K。奇怪的是,在终端单独运行ar没有问题,但是在Makefile里就是不行。

先尝试去改Android.mk,把它分成几个静态库来编译,但老是编译不过去,试一次要很长时间,真的很烦。后来只好尝试去找出这个长度限制在哪里:

1.这个错误是glibc打出来,所以先到glibc里去找Argument list too long对应错误码。在sysdeps/gnu/errlist.c找到这样一行代码:

  1. [ERR_REMAP (E2BIG)] = N_("Argument list too long"),  



很明显,错误码就是E2BIG。

2.错误码应该是exec系统调用返回的,于是到内核里去查找返回E2BIG的代码,在exec.c里找到几处:

  1. static int count(char __user * __user * argv, int max)   
  2. {   
  3.     int i = 0;   
  4.     
  5.     if (argv != NULL) {   
  6.         for (;;) {   
  7.             char __user * p;   
  8.     
  9.             if (get_user(p, argv))   
  10.                 return -EFAULT;   
  11.             if (!p)   
  12.                 break;   
  13.             argv++;   
  14.             if (i++ >= max)   
  15.                 return -E2BIG;   
  16.             cond_resched();   
  17.         }   
  18.     }   
  19.     return i;   
  20. }   
  21.     
  22.     bprm->argc = count(argv, MAX_ARG_STRINGS);   
  23.     if ((retval = bprm->argc) < 0)   
  24.         goto out;   
  25.     
  26.     bprm->envc = count(envp, MAX_ARG_STRINGS);   
  27.     if ((retval = bprm->envc) < 0)   
  28.         goto out;  


这是对参数个数的限制,MAX_ARG_STRINGS的定义为:

#define MAX_ARG_STRINGS 0x7FFFFFFF

这个值已经大得吓人了,不可能是参数个数超出了。

另外一处是:

  1. static int copy_strings(int argc, char __user * __user * argv,   
  2.             struct linux_binprm *bprm)   
  3. {   
  4.     struct page *kmapped_page = NULL;   
  5.     char *kaddr = NULL;   
  6.     unsigned long kpos = 0;   
  7.     int ret;   
  8.     
  9.     while (argc-- > 0) {   
  10.         char __user *str;   
  11.         int len;   
  12.         unsigned long pos;   
  13.     
  14.         if (get_user(str, argv+argc) ||   
  15.                 !(len = strnlen_user(str, MAX_ARG_STRLEN))) {   
  16.             ret = -EFAULT;   
  17.             goto out;   
  18.         }   
  19.     
  20.         if (!valid_arg_len(bprm, len)) {   
  21.             ret = -E2BIG;   
  22.             goto out;   
  23.         }   
  24. ...   
  25. }   
  26.     
  27. #ifdef CONFIG_MMU   
  28. static bool valid_arg_len(struct linux_binprm *bprm, long len)   
  29. {   
  30.     return len <= MAX_ARG_STRLEN;   
  31. }   
  32. static bool valid_arg_len(struct linux_binprm *bprm, long len)   
  33. {   
  34.     return len <= bprm->p;   
  35. }   
  36.     
  37. #endif /* CONFIG_MMU */  



valid_arg_len是用来限制单个参数长度的,PC上肯定是定义了CONFIG_MMU的,所以单个参数的长度应该是MAX_ARG_STRLEN,即:

#define MAX_ARG_STRLEN (PAGE_SIZE * 32)

对于4K的page大小来说,单个参数长度限制是128K, 这也够大了。不过如果前面所说的整个ar命令,在Makefile里被当作一个参数的话,那肯定是超长了。这可以解释在终端单独运行ar没有问题,在 Makefile调用不行的疑问。回头再研究一下Makefile里是怎么调用的吧。

 

 

 

 

 

 

 

 

 

 

起因:假如你使用的是某个linux发行版,当某个目录下有很多文件时,差不多一万多吧,在执行ls *, mv * ,rm * 等等命令时,终端提示如下的错误:
bash: /bin/ls: Argument list too long
如果你使用的是中文环境,那么报的错是:
bash: /bin/ls: 参数列表太长

这个时候,你会怎么办?google?找以前看过的文档?问熟悉linux的人?从源代码中去寻找答案?......
时间是上述选择的代价,那么最后放弃任何的选择解决问题,也许是最好的办法之一,如果这是一份工作的话,也许会推给别人,也许搁置、搪塞.......

以下是我的一个思路:
google了一把,发现已经有人解决了,从中选出最佳答案,那就是本文首行的链接地址。
以作者的思路,确实可以解决实际问题,并且看到许多的实践者的反馈。
就这样的以解决问题为终结?也许我以前会这样就放弃了,但是这次不会,就像是 ext3固定分区后可以设置inode的数量最大为多少?
我这次同样也不会放弃。在开始之前需要准备三个软件,分别是:coreutils-6.12.tar.gz,bash-3.2.tar.gz,以及glibc-2.4.tar.gz.
思路是这样的,ls是coreutils中的程序,而报的错误是bash,但是bash并没有确定的错误信息是参数,而是属于posix标准的一个错误,那只有glibc了。
http://www.kernel.org/doc/man-pages/online/pages/man3/errno.3.html
我们可以看到如下一行内容:

 E2BIG Argument list too long (POSIX.1)
在glibc中的sysdeps/gnu/errlist.c中可以看到如下语句:
/*
TRANS Argument list too long; used when the arguments passed to a new program
TRANS being executed with one of the @code{exec} functions (@pxref{Executing a
TRANS File}) occupy too much memory space. This condition never arises in the
TRANS GNU system. */
[ERR_REMAP (E2BIG)] = N_("Argument list too long"),
# if E2BIG > ERR_MAX
# undef ERR_MAX
# define ERR_MAX E2BIG
# endif
#endif
#ifdef ENOEXEC
/*
好了,报错的信息找到根源了,是什么在使用glibc了呢?由上述参考得一定是kernel所报出的。
然后,就是到kernel-source中,寻找了。
make menuconfig找选项,或是grep .config均不能发现此项。
依然从错误的信息来查找kernel文件,则在include/asm-generic/errno-base.h:#define E2BIG 7 /* Argument list too long */
会看这么一行内容。
到此,由于本身功夫的欠缺,我已经无法查下去了,想象用户态陷入内核态,内核返回错误,这一系列复杂的过程。
只好跟着作者去读源码了:
include/linux/binfmt.h
struct pt_regs;

/*
* 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

/* sizeof(linux_binprm->buf) */
#define BINPRM_BUF_SIZE 128

#ifdef __KERNEL__

#define CORENAME_MAX_SIZE 128

/*
* This structure is used to hold the arguments that are used when loading binaries.
*/
struct linux_binprm{
char buf[BINPRM_BUF_SIZE];
#ifdef CONFIG_MMU
struct vm_area_struct *vma;
#else
# define MAX_ARG_PAGES 32
struct page *page[MAX_ARG_PAGES];
#endif
struct mm_struct *mm;
unsigned long p; /* current top of mem */
int sh_bang;
struct file * file;
int e_uid, e_gid;
kernel_cap_t cap_inheritable, cap_permitted;
红色部分即是限制参数的缓冲空间大小的参数,我们可以调节MAX_ARG_PAGES、BINPRM_BUF_SIZE 128
来设置具体的值。
更改内核确实是最直接、有效的方法,但是注意:
缺点也很明显,如果分配给命令行的内存大于可用的系统内存
,可能导致对系统自身的拒绝服务攻击( DoS attack ),引起系统崩溃。
尤其是对于多用户系统,即使增加很小
的内存分配都会有很大影响,因为每个用户都被分配到额外内存。
因此一定要充分测试来决定是否你的系统可以
使用此方法。

更改内核太过于危险,波及的应用程序太多。建议转换另外的思路。
比如使用find+xargs等构建复杂的命令行。
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值