Tek komut bağımsız değişken için maksimum boyutu ne tanımlar?


47

Tek bir argümanın maksimum uzunluğunun burada genel argüman dizisinin toplam büyüklüğü ile sınırlı olan ortamın büyüklüğü kadar sorun olmadığı izlenimindeydim ARG_MAX. Böylece, aşağıdakine benzer bir şeyin başarılı olacağını düşündüm:

env_size=$(cat /proc/$$/environ | wc -c)
(( arg_size = $(getconf ARG_MAX) - $env_size - 100 ))
/bin/echo $(tr -dc [:alnum:] </dev/urandom | head -c $arg_size) >/dev/null

Kabuktaki - 100ortamın büyüklüğü ile echoproses arasındaki farkı hesaba katmak için fazlasıyla yeterli olması . Bunun yerine hatayı aldım:

bash: /bin/echo: Argument list too long

Bir süre etrafta oynadıktan sonra, maksimum değerin tam altıgen büyüklüğünde daha küçük olduğunu buldum:

/bin/echo \
  $(tr -dc [:alnum:] </dev/urandom | head -c $(($(getconf ARG_MAX)/16-1))) \
  >/dev/null

Eksi bir çıkarıldığında, hata geri döner. Görünüşte, tek bir argüman için maksimum değer aslında ARG_MAX/16ve -1argüman dizisindeki dizgenin sonuna yerleştirilen boş bayt hesapları.

Başka bir sorun, argüman tekrarlandığında argüman dizisinin toplam boyutunun daha yakın olabileceği ARG_MAX, ancak tam olarak orada olmadığıdır:

args=( $(tr -dc [:alnum:] </dev/urandom | head -c $(($(getconf ARG_MAX)/16-1))) )
for x in {1..14}; do
  args+=( ${args[0]} )
done

/bin/echo "${args[@]}" "${args[0]:6534}" >/dev/null

"${args[0]:6533}"Burada kullanmak son argümanı 1 baytı daha uzun yapar ve Argument list too longhatayı verir . Bu farkın, verilen ortamın büyüklüğü tarafından dikkate alınması muhtemel değildir:

$ cat /proc/$$/environ | wc -c
1045

Sorular:

  1. Bu doğru davranış mı, yoksa bir yerde bir hata mı var?
  2. Olmazsa, bu davranış herhangi bir yerde belgeleniyor mu? Tek bir argüman için maksimum değeri tanımlayan başka bir parametre var mı?
  3. Bu davranış Linux ile sınırlı mı (veya bunun belirli sürümleri)?
  4. Argüman dizisinin asıl maksimum büyüklüğü ile ortamın yaklaşık büyüklüğü arasındaki ek ~ 5KB tutarsızlığı ARG_MAXnedir?

İlave bilgi:

uname -a
Linux graeme-rock 3.13-1-amd64 #1 SMP Debian 3.13.5-1 (2014-03-04) x86_64 GNU/Linux

5
Linux'ta, 32 sayfaya kodlanmış zor (128kiB). Kaynaktaki MAX_ARG_STRLEN bölümüne bakınız.
Stéphane Chazelas


1
En azından makinemde getconf ARG_MAX, akıma bağlı ulimit -s. Sınırsız olarak ayarlayın ve ARG_MAX için inanılmaz bir 4611686018427387903 edinin.
derobert


Neden / proc / $$ / environ yolunu kullanıyorsunuz? linux içindeki procfs, symlink / proc / self komutunu destekler, daha sonra / proc / self / environment kullanabilirsiniz. İşleme atanan tüm yamalar, aynı işlem bunu kontrol ettiğinde / proc / self işaretini gösterir. Aynısı devfs ile, örneğin / dev içinde, device stdout fd / 1 'e sembolik bağlantıdır, ancak fd / self / fd' ye işaret eder. birçok sistem bu davranışı kopyalar.
Znik

Yanıtlar:


47

Cevaplar

  1. Kesinlikle bir hata değil.
  2. Bir argüman için maksimum boyutu tanımlayan parametre şudur MAX_ARG_STRLEN. Bu parametre için, yorumlardan başka hiçbir belge yok binfmts.h:

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

    Gösterildiği gibi, Linux ayrıca bir komuta verilen argümanların sayısında (çok büyük) bir limit vardır.

  3. Tek bir argüman boyutunda (argümanlar ve çevre üzerindeki genel sınırdan farklı) bir limit Linux'a özgü görünüyor. Bu makaleARG_MAX , Unix benzeri sistemler üzerindeki eşdeğerleri ve karşılaştırmasını sunmaktadır . MAX_ARG_STRLENLinux için tartışılmaktadır, ancak diğer sistemlerde herhangi bir eşdeğerden söz edilmemektedir.

    Yukarıdaki makalede ayrıca MAX_ARG_STRLENLinux 2.6.23'te tanıtıldığı ve komut argümanları maksimumları ile ilgili bir takım diğer değişikliklerin de (aşağıda tartışıldığı) belirtildiği belirtildi. Taahhüt için log / diff burada bulunabilir .

  4. Sonuç getconf ARG_MAXve olası maksimum argüman büyüklüğü artı ortam arasındaki ek tutarsızlıktan neyin sorumlu olduğu hala net değil . Stephane Chazelas'ın ilgili cevabı , alanın bir kısmının işaretçiler tarafından argüman / çevre dizgilerinin her birine ait olduğunu göstermektedir. Bununla birlikte, kendi araştırmam, bu işaretçilerin execvesistem çağrısı sırasında, hala E2BIGçağrı işlemine bir hata döndürebildiği zaman yaratılmadığını ileri sürüyor (her argvdize için işaretçiler kesinlikle daha sonra oluşturulsa da).

    Ayrıca, dizeler görebildiğim kadarıyla bellekte bitişik, bu nedenle hiçbir bellek boşluğu nedeniyle hizalama yapılamıyor. Her ne kadar çok büyük olasılıkla ne olursa olsun içinde bir faktör olduğunu mu fazladan bellek kullanır. Ekstra alanı neyin kullandığını anlamak, çekirdeğin belleği nasıl tahsis ettiği hakkında daha ayrıntılı bir bilgi gerektirir (ki bu yararlı bilgidir, bu yüzden daha sonra araştırıp güncelleyeceğim).

ARG_MAX Karışıklık

Linux 2.6.23'ten beri ( bu taahhüdün sonucu olarak ), Linux'un diğer Unix benzeri sistemlerden farklı olmasını sağlayan, maksimum komutların yönetilme biçiminde değişiklikler yapıldı. İlave ek olarak MAX_ARG_STRLENve MAX_ARG_STRINGSbir sonucu getconf ARG_MAXartık yığın büyüklüğüne bağlıdır ve farklı olabilir ARG_MAXolarak limits.h.

Normal olarak bir sonucu getconf ARG_MAXolacaktır 1/4yığın büyüklüğü. Yığın boyutunu almak için bashkullanırken ulimitşunları göz önünde bulundurun :

$ echo $(( $(ulimit -s)*1024 / 4 ))  # ulimit output in KiB
2097152
$ getconf ARG_MAX
2097152

Bununla birlikte, yukarıda davranışı bu şekilde biraz değiştirildi tamamlama (Linux 2.6.25-RC4 ~ 121 eklenir). ARG_MAXiçinde limits.hartık sert sonucuna bağlı düşük değerler olarak hizmet vermektedir getconf ARG_MAX. Yığın boyutu, ayarlanırsa o 1/4yığın boyutu daha az ARG_MAXolarak limits.h, o zaman limits.hdeğeri kullanılır:

$ grep ARG_MAX /usr/include/linux/limits.h 
#define ARG_MAX       131072    /* # bytes of args + environ for exec() */
$ ulimit -s 256
$ echo $(( $(ulimit -s)*1024 / 4 ))
65536
$ getconf ARG_MAX
131072

Ayrıca, yığın boyutu mümkün olandan düşük ayarlanmışsa ARG_MAX, yığının ( RLIMIT_STACK) boyutunun önce E2BIGdöndürülen argüman / ortam boyutunun üst sınırı haline geldiğini unutmayın ( getconf ARG_MAXyine de değeri gösterecektir limits.h).

Unutulmaması gereken son şey, eğer çekirdek olmadan inşa edilmişse CONFIG_MMU(bellek yönetimi donanım desteği), kontrolünün ARG_MAXdevre dışı bırakılması, yani sınırın geçerli olmamasıdır. Her ne kadar MAX_ARG_STRLENve MAX_ARG_STRINGShala geçerli.

Daha fazla okuma


2
Bu iyi bir cevap, kesinlikle benimkinden daha iyi - Ben onu reddettim. Fakat sorduğumuz cevap her zaman almamız gereken cevap değildir - bu yüzden soruyoruz, çünkü bilmiyoruz. Sizi, bu konuyla baş başa getiren iş akışınızdaki sorunu çözmez. Bunun cevabımda nasıl hafifletilebileceğini ve 2mbs üzerindeki tekli kabuk değişkenli dizginin sadece birkaç satırlık kabuk betiği ile yeni yürütülen işlemlere nasıl geçirilebileceğini gösteriyorum.
mikeserv

Varsayılan Linux'ta 32 * 4KB sayfa = 128 KB ortam değişkenlerinin sınırını gösteren bir Python betiği hazırladım .
nh2

0

İçinde eglibc-2.18/NEWS

* ARG_MAX is not anymore constant on Linux.  Use sysconf(_SC_ARG_MAX).
Implemented by Ulrich Drepper.

İçinde eglibc-2.18/debian/patches/kfreebsd/local-sysdeps.diff

+      case _SC_ARG_MAX:
+   request[0] = CTL_KERN;
+   request[1] = KERN_ARGMAX;
+   if (__sysctl(request, 2, &value, &len, NULL, 0) == -1)
+       return ARG_MAX;
+   return (long)value;

İçinde linux/include/uapi/linux/limits.h

#define ARG_MAX       131072    /* # bytes of args + environ for exec() */

Ve 131072senin olduğunu $(getconf ARG_MAX)/16-1belki 0'dan başlamalı.

Glibc ve Linux ile uğraşıyorsunuz. ARG_MAXDöndürülen "doğru" değeri almak için getconf yamasını düzeltmek iyi olur .

Düzenle:

Birazcık temizlemek için (kısa ama sıcak tartışmalardan sonra)

ARG_MAXTanımlanan sabit limits.hexec ile geçmiş bir argüman maksimum uzunluğu verir.

getconf ARG_MAXKomut exec geçirilen kümülatif argümanlar boyutu ve çevre boyutunun maksimum değerini verir.


2
Bu ARG_MAX, arg + env boyut sınırı için garanti edilen en düşük değerdir, tek bir argümanın maksimum boyutu değildir (bu MAX_ARG_STRLEN ile aynı değerde olsa da)
Stéphane Chazelas 20:14

eglibc-2.18/NEWSSnippet'in için bir randevun var mı? Bunu belirli bir çekirdek sürümüne bağlamak iyi olurdu.
Graeme

@StephaneChazelas: Parçayı bulmak için çok tembelim, ancak arg maksimum değeri aşarsa, env boyutunu bulmak için gerekli değildir.

@Graeme: Getconf değerinin 131072'yi gösterdiği bazı eski linux'lar da var. Bunun eglibc> ile daha yeni linux'lere ait olduğunu düşünüyorum. sadece. Tebrikler, bir hata BTW buldun.

2
Glibc koduna bakıyorsunuz, bu konu dışı. Libc, hangi boyutta argümanları geçtiğiniz umrunda değil. Alıntı yaptığınız kod, kullanıcılara bir çalıştırmaya (2) geçirilen argv + env'nin maksimum boyutu hakkında bir fikir veren bir API olan sysconf ile ilgilidir. Execve () sistem çağrısı boyunca geçen arg ve env listesini kabul edip etmeyen çekirdek. Bu getconf ARG_MAXarg + env'nin kümülatif boyutuyla ilgilidir (son Linux'ta değişken, bakınız ulimit -sve bağladığım diğer soru), sysconf / getconf sorgusu olmayan tek bir argümanın maksimum uzunluğu ile ilgili değildir.
Stéphane Chazelas

-1

Yani @StephaneChazelas aşağıdaki yorumlarda beni haklı olarak düzeltir - kabuğun kendisi herhangi bir şekilde sisteminizin izin verdiği maksimum argüman boyutunu belirlemez, ancak çekirdeğiniz tarafından belirlenir.

Diğer bazılarının daha önce de söylediği gibi, çekirdek ilk çalıştırırken başka bir işlemden yeni bir işleme verebileceğiniz maksimum argüman büyüklüğünün 128kb ile sınırlı olduğu görülüyor. Bu sorunu özellikle $(command substitution)yerinde çalışması ve çıktısının tamamını birinden diğerine vermesi gereken iç içe geçmiş alt kabuklar nedeniyle yaşarsınız .

Ve bu biraz çılgınca bir tahmin, ancak ~ 5kb tutarsızlığı standart sistem sayfa boyutuna çok yakın göründüğü için kuşkularım, sonuçta çıktısını almak bashiçin $(command substitution)ihtiyaç duyduğunuz alt kabuğu işlemek için kullandığı sayfanın tahsis edilmesi ve / veya array tableverilerinizi ilişkilendirmek için kullandığı işlev yığını . Sadece ikisinin de ücretsiz olmadığını varsayabilirim.

Aşağıda, biraz zor olsa da, çok büyük kabuk değişken değerlerini başlatma sırasında yeni işlemlere aktarmanın mümkün olduğunu, akışını yönetebildiğiniz sürece gösteriyorum.

Bunu yapmak için öncelikle boruları kullandım. Ancak, kabuk dizisini aşağıdaki Sonuçlara here-documentişaret ederek de değerlendirdim cat's stdin..

Ancak son bir not - taşınabilir koda özel bir gereksiniminiz yoksa mapfile, kabuk işlerinizi biraz daha kolaylaştırabilecek olan beni vurur .

time bash <<-\CMD
    ( for arg in `seq 1 6533` ; do
        printf 'args+=(' ; printf b%.0b `seq 1 6533` ; echo ')'
    done ;
    for arg in `seq 1 6533` ; do
        printf %s\\n printf\ '%s\\n'\ \""\${args[$arg]}"\" ;
    done ) | . /dev/stdin >&2
CMD
bash <<<''  66.19s user 3.75s system 84% cpu 1:22.65 total

Muhtemelen bunu iki katına çıkarır ve sonra tekrar akış halinde yaparsan - tekrar bulmak için yeteri kadar hastalıklı değilim - ama akışını yaparsan kesinlikle işe yarar.

printfİkinci satırdaki jeneratör kısmını değiştirmeyi denedim :

printf \ b%.0b

Aynı zamanda çalışır:

bash <<<''  123.78s user 5.42s system 91% cpu 2:20.53 total

Yani belki biraz hastalıklıyım. Kullanıyorum zero padding hereve önceki "$arg"değeri geçerli "$arg"değere ekliyorum . 6500'ün ötesine geçiyorum ...

time bash <<-\CMD
    ( for arg in `seq 1 33` ; do
        echo $arg >&2
        printf 'args+=('"${args[$((a=arg-1))]}$(printf "%0${arg}0d" \
            `seq 1 6533` ; printf $((arg-1)))"')\n'
    done ;
    for arg in `seq 1 33` ; do
        printf '/usr/bin/cat <<HERE\n%s\nHERE\n' "\${args[$arg]}"
    done ) | . /dev/stdin >&2
CMD

bash <<<''  14.08s user 2.45s system 94% cpu 17.492 total

Ve catçizgiyi böyle görünecek şekilde değiştirirsem :

printf '/usr/bin/cat <<HERE | { printf '$arg'\  ; wc -c ;}
    %s\nHERE\n' "\${args[$arg]}"

wc.Bunlardan bayt sayıları alabilirim. Bunların argsdizideki her bir anahtarın boyutları olduğunu unutmayın . Dizinin toplam boyutu tüm bu değerlerin toplamıdır.

1 130662
2 195992
3 261322
4 326652
5 391982
6 457312
7 522642
8 587972
9 653302
10 718633
11 783963
12 849293
13 914623
14 979953
15 1045283
16 1110613
17 1175943
18 1241273
19 1306603
20 1371933
21 1437263
22 1502593
23 1567923
24 1633253
25 1698583
26 1763913
27 1829243
28 1894573
29 1959903
30 2025233
31 2090563
32 2155893
33 2221223

2
Hayır, kabukla ilgisi yok, tek bir argüman 128kiB'nin üzerinde olduğunda E2BIG döndüren execve (2) sistem çağrısı.
Stéphane Chazelas

Ayrıca, kabuk yerleşimlerinin sınırlandırılmadığını göz önünde bulundurun - echo $(tr -dc [:alnum:] </dev/urandom | head -c $(($(getconf ARG_MAX)*10))) >/dev/nulliyi çalışacaktır. Sadece harici bir komut kullandığınızda bir sorun vardır.
Graeme

@Graeme Peki, bunu da kedi ile yaptım - sorun değil. Değişken sonunda bir heredokta değerlendirilir. Son düzenlememe bakın. Toplam sayımı 33'e düşürdüm, çünkü her seferinde son değeri ekliyorum. Ve sıfır dolgu ...
mikeserv

@StephaneChazelas - peki heredoc akışındaki argümanı değerlendirerek bunu çözebilir miyim? Yoksa bir şekilde bashsıkıştırmak mı?
mikeserv

1
@mikeserv, kodunuzun içinde hiçbir yerde göremiyorum, herhangi bir örneğinizi büyük bir argüman listesiyle çalıştırıyorsunuz. printfbir yerleşiktir, yani yürütülmez ve AFAICT, size catherhangi bir argüman vermez.
Stéphane Chazelas
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.