एक लिनक्स प्रक्रिया के लिए मेमोरी का उपयोग सीमित करें


151

मैं pdftoppmएक उपयोगकर्ता-प्रदत्त पीडीएफ को 300DPI छवि में बदलने के लिए दौड़ रहा हूं । यह बहुत अच्छा काम करता है, सिवाय इसके कि उपयोगकर्ता एक पीडीएफ को बहुत बड़े पृष्ठ आकार के साथ प्रदान करता है। pdftoppmस्मृति में उस आकार की 300DPI छवि रखने के लिए पर्याप्त मेमोरी आवंटित करेगा, जो कि 100 इंच वर्ग पृष्ठ के लिए प्रति पिक्सेल = 3.5GB प्रति 100 * 300 * 100 * 300 * 4 बाइट्स है। एक दुर्भावनापूर्ण उपयोगकर्ता बस मुझे एक मूर्खतापूर्ण-बड़ी पीडीएफ दे सकता है और सभी प्रकार की समस्याओं का कारण बन सकता है।

तो मैं जो करना चाहता हूं, वह एक बच्चे की प्रक्रिया के लिए स्मृति उपयोग पर कुछ हद तक कठिन सीमा है, जो मैं चलाने जा रहा हूं - बस इस प्रक्रिया को मरना होगा यदि यह 500 एमबी से अधिक मेमोरी आवंटित करने की कोशिश करता है। क्या यह संभव है?

मुझे नहीं लगता कि इसके लिए ulimit का उपयोग किया जा सकता है, लेकिन क्या एक-प्रक्रिया समान है?


हो सकता है docker?
श्रीधर सरनोबत

जवाबों:


58

Ulimit के साथ कुछ समस्याएं हैं। यहां विषय पर एक उपयोगी पढ़ा गया है: लिनक्स में एक कार्यक्रम की समय और मेमोरी खपत को सीमित करना , जो टाइमआउट टूल का नेतृत्व करता है, जो आपको समय या मेमोरी खपत द्वारा एक प्रक्रिया (और इसके कांटे) को पिंजरे में डाल देता है।

टाइमआउट टूल को पर्ल 5+ और /procफाइलसिस्टम को माउंट करने की आवश्यकता है। उसके बाद आप टूल को उदाहरण के लिए कॉपी /usr/local/binकरें:

curl https://raw.githubusercontent.com/pshved/timeout/master/timeout | \
  sudo tee /usr/local/bin/timeout && sudo chmod 755 /usr/local/bin/timeout

उसके बाद, आप अपने प्रश्न को स्मृति की खपत द्वारा अपनी प्रक्रिया को 'पिंजरे' में रख सकते हैं जैसे कि आपके प्रश्न में:

timeout -m 500 pdftoppm Sample.pdf

वैकल्पिक रूप से आप उपयोग कर सकते हैं -t <seconds>और -x <hertz>समय या सीपीयू बाधाओं द्वारा प्रक्रिया को क्रमशः सीमित कर सकते हैं ।

जिस तरह से यह उपकरण काम करता है वह प्रति सेकंड कई बार जाँच करता है अगर स्पॉन्डेड प्रक्रिया ने इसकी निर्धारित सीमाओं को ओवरसब्सक्राइब नहीं किया है। इसका मतलब यह है कि वास्तव में एक छोटी सी खिड़की है जहां एक प्रक्रिया संभावित रूप से टाइमआउट नोटिस से पहले की जा सकती है और प्रक्रिया को मार देती है।

एक अधिक सही दृष्टिकोण इसलिए संभवतः cgroups को शामिल करेगा, लेकिन यह सेट अप करने के लिए बहुत अधिक शामिल है, भले ही आप Docker या runC का उपयोग करें, जो चीजों के बीच, cgroups के आसपास अधिक उपयोगकर्ता-अनुकूल अमूर्तता प्रदान करते हैं।


मेरे लिए अब (फिर से?) काम करने लगता है, लेकिन यहाँ Google कैश संस्करण है: webcache.googleusercontent.com/…
kvz

क्या हम टास्कआउट के साथ टाइमआउट का उपयोग कर सकते हैं (हमें मेमोरी और कोर दोनों को सीमित करने की आवश्यकता है)?
रंश

7
यह ध्यान दिया जाना चाहिए कि यह उत्तर coreutilsउसी नाम के लिनक्स मानक उपयोगिता की बात नहीं कर रहा है ! इस प्रकार, उत्तर संभावित रूप से खतरनाक है यदि आपके सिस्टम पर कहीं भी, कुछ पैकेज में लिपि timeoutमानक coreutilsपैकेज होने की उम्मीद में एक स्क्रिप्ट है ! मैं इस उपकरण से अनजान हूं जैसे डिबियन के वितरण के लिए पैक किया जा रहा है।
user1404316

क्या -t <seconds>बाधा कई सेकंड के बाद प्रक्रिया को मार देती है?
xxx374562

116

इसे सीमित करने का एक और तरीका है लिनक्स के नियंत्रण समूहों का उपयोग करना। यह विशेष रूप से उपयोगी है यदि आप एक प्रक्रिया (या प्रक्रियाओं का समूह) को सीमित करना चाहते हैं भौतिक स्मृति का आवंटन वर्चुअल मेमोरी से अलग है। उदाहरण के लिए:

cgcreate -g memory:myGroup
echo 500M > /sys/fs/cgroup/memory/myGroup/memory.limit_in_bytes
echo 5G > /sys/fs/cgroup/memory/myGroup/memory.memsw.limit_in_bytes

एक नियंत्रण समूह बनाया जाएगा जिसका नाम होगा myGroup, 500 एमबी की भौतिक मेमोरी तक और 5000 एमबी की स्वैप तक माईग्रुप के तहत चलने वाली प्रक्रियाओं का सेट। नियंत्रण समूह के तहत एक प्रक्रिया चलाने के लिए:

cgexec -g memory:myGroup pdftoppm

ध्यान दें कि आधुनिक उबंटू वितरण पर इस उदाहरण को बदलने के लिए cgroup-binपैकेज और संपादन स्थापित करना होगा:/etc/default/grubGRUB_CMDLINE_LINUX_DEFAULT

GRUB_CMDLINE_LINUX_DEFAULT="cgroup_enable=memory swapaccount=1"

और फिर चल रहा है sudo update-grubऔर नए कर्नेल बूट मापदंडों के साथ बूट करने के लिए रीबूट कर रहा है ।


3
firejailकार्यक्रम भी आप मेमरी सीमा के साथ एक प्रक्रिया शुरू (सिर्फ यादों से ज्यादा सीमित करने के लिए cgroups और नामस्थान का उपयोग) करने देगा। अपने सिस्टम पर मुझे काम करने के लिए कर्नेल कमांड लाइन को बदलना नहीं पड़ा!
नेड 64

1
क्या आपको GRUB_CMDLINE_LINUX_DEFAULTसेटिंग को लगातार बनाने के लिए संशोधन की आवश्यकता है ? मुझे यहाँ लगातार इसे बनाने का एक और तरीका मिला ।
स्टासन


इस उत्तर में यह ध्यान रखना उपयोगी होगा कि कुछ वितरणों (जैसे Ubuntu) पर sudo को cgcreate के लिए आवश्यक है, और बाद के आदेशों को भी जब तक कि वर्तमान उपयोगकर्ता को अनुमति नहीं दी जाती है। यह पाठक को यह जानकारी कहीं और खोजने से बचाएगा (उदाहरण के लिए askubuntu.com/questions/345055 )। मैंने इस आशय को संपादित करने का सुझाव दिया था लेकिन इसे अस्वीकार कर दिया गया था।
स्टूजैसिक

77

यदि आपकी प्रक्रिया में अधिक बच्चे नहीं हैं जो सबसे अधिक मेमोरी का उपभोग करते हैं, तो आप setrlimitफ़ंक्शन का उपयोग कर सकते हैं । ulimitशेल के कमांड का उपयोग करने के लिए अधिक सामान्य यूजर इंटरफेस :

$ ulimit -Sv 500000     # Set ~500 mb limit
$ pdftoppm ...

यह आपकी प्रक्रिया की "आभासी" मेमोरी को सीमित कर देगा, खाते में ले जाने और सीमित करने वाली मेमोरी - यह प्रक्रिया अन्य प्रक्रियाओं के साथ साझा किए जा रहे शेयर, और स्मृति मैप की गई लेकिन आरक्षित नहीं है (उदाहरण के लिए, जावा का बड़ा ढेर)। फिर भी, वर्चुअल मेमोरी उन प्रक्रियाओं के लिए निकटतम सन्निकटन है जो वास्तव में बड़ी हो जाती हैं, जिससे उक्त त्रुटियां महत्वहीन हो जाती हैं।

यदि आपका कार्यक्रम बच्चों को जन्म देता है, और यह वह है जो स्मृति आवंटित करता है, तो यह अधिक जटिल हो जाता है, और आपको अपने नियंत्रण में प्रक्रियाओं को चलाने के लिए सहायक स्क्रिप्ट लिखना चाहिए। मैंने अपने ब्लॉग में लिखा, क्यों और कैसे


2
setrlimitअधिक बच्चों के लिए अधिक जटिल क्यों है ? man setrlimitमुझे बताता है कि "कांटा (2) के माध्यम से बनाई गई एक बच्चा प्रक्रिया अपने माता-पिता की संसाधन सीमाओं को विरासत में देती है। संसाधन सीमाएं निष्पादित होती हैं (2)"
अकीरा

6
क्योंकि कर्नेल सभी बच्चे प्रक्रियाओं के लिए vm आकार का योग नहीं करता है; अगर यह किया तो यह वैसे भी गलत होगा। सीमा प्रति-प्रक्रिया है, और वर्चुअल एड्रेस स्पेस है, मेमोरी उपयोग नहीं। मेमोरी का उपयोग मापना कठिन है।
मार्क 17

1
अगर मैं प्रश्न को सही ढंग से समझता हूं तो ओपी प्रति उपप्रकार (बच्चे) की सीमा क्या है .. कुल में नहीं।
अकीरा

@ मार्कर, वैसे भी, वर्चुअल एड्रेस स्पेस इस्तेमाल की गई मेमोरी के लिए एक अच्छा अनुमान है, खासकर यदि आप एक प्रोग्राम चलाते हैं जो एक वर्चुअल मशीन द्वारा नियंत्रित नहीं होता है (जैसे, जावा)। कम से कम मुझे कोई बेहतर मीट्रिक पता नहीं है।

2
बस धन्यवाद करना चाहता हूं - इस ulimitदृष्टिकोण के साथ मुझे मदद मिली firefoxहै बग 622,816 - एक बड़ी छवि लोड हो रहा है फ़ायरफ़ॉक्स "फ्रीज" कर सकते हैं, या प्रणाली दुर्घटना ; जो USB बूट पर (RAM से) ओएस को फ्रीज करने के लिए जाता है, जिसके लिए कड़ी मेहनत की आवश्यकता होती है; अब कम से कम firefoxखुद को क्रैश करता है, ओएस को जीवित छोड़ देता है ... चीयर्स!
सदाऊ

8

मैं नीचे स्क्रिप्ट का उपयोग कर रहा हूं, जो महान काम करता है। यह के माध्यम से cgroups का उपयोग करता है cgmanager अद्यतन: यह अब से कमांड का उपयोग करता है cgroup-toolsइस स्क्रिप्ट को नाम दें limitmemऔर इसे अपने $ PATH में डालें और आप इसका उपयोग कर सकते हैं limitmem 100M bash। यह मेमोरी और स्वैप उपयोग दोनों को सीमित कर देगा। सिर्फ मेमोरी को सीमित करने के लिए लाइन को हटा दें memory.memsw.limit_in_bytes

संपादित करें: डिफ़ॉल्ट लिनक्स इंस्टॉलेशन पर यह केवल मेमोरी उपयोग को सीमित करता है, स्वैप उपयोग को नहीं। स्वैप उपयोग को सीमित करने के लिए, आपको अपने लिनक्स सिस्टम पर स्वैप अकाउंटिंग को सक्षम करना होगा। कि स्थापित करने / जोड़कर है swapaccount=1में /etc/default/grubतो यह तरह दिखता है

GRUB_CMDLINE_LINUX="swapaccount=1"

फिर दौड़ो sudo update-grubऔर रिबूट करो।

अस्वीकरण: मुझे आश्चर्य नहीं होगा अगर cgroup-toolsभविष्य में भी टूट जाता है। सही समाधान cgroup प्रबंधन के लिए सिस्टम एपीआई का उपयोग करना होगा, लेकिन उस एटीएम के लिए कोई कमांड लाइन उपकरण नहीं हैं

#!/bin/sh

# This script uses commands from the cgroup-tools package. The cgroup-tools commands access the cgroup filesystem directly which is against the (new-ish) kernel's requirement that cgroups are managed by a single entity (which usually will be systemd). Additionally there is a v2 cgroup api in development which will probably replace the existing api at some point. So expect this script to break in the future. The correct way forward would be to use systemd's apis to create the cgroups, but afaik systemd currently (feb 2018) only exposes dbus apis for which there are no command line tools yet, and I didn't feel like writing those.

# strict mode: error if commands fail or if unset variables are used
set -eu

if [ "$#" -lt 2 ]
then
    echo Usage: `basename $0` "<limit> <command>..."
    echo or: `basename $0` "<memlimit> -s <swaplimit> <command>..."
    exit 1
fi

cgname="limitmem_$$"

# parse command line args and find limits

limit="$1"
swaplimit="$limit"
shift

if [ "$1" = "-s" ]
then
    shift
    swaplimit="$1"
    shift
fi

if [ "$1" = -- ]
then
    shift
fi

if [ "$limit" = "$swaplimit" ]
then
    memsw=0
    echo "limiting memory to $limit (cgroup $cgname) for command $@" >&2
else
    memsw=1
    echo "limiting memory to $limit and total virtual memory to $swaplimit (cgroup $cgname) for command $@" >&2
fi

# create cgroup
sudo cgcreate -g "memory:$cgname"
sudo cgset -r memory.limit_in_bytes="$limit" "$cgname"
bytes_limit=`cgget -g "memory:$cgname" | grep memory.limit_in_bytes | cut -d\  -f2`

# try also limiting swap usage, but this fails if the system has no swap
if sudo cgset -r memory.memsw.limit_in_bytes="$swaplimit" "$cgname"
then
    bytes_swap_limit=`cgget -g "memory:$cgname" | grep memory.memsw.limit_in_bytes | cut -d\  -f2`
else
    echo "failed to limit swap"
    memsw=0
fi

# create a waiting sudo'd process that will delete the cgroup once we're done. This prevents the user needing to enter their password to sudo again after the main command exists, which may take longer than sudo's timeout.
tmpdir=${XDG_RUNTIME_DIR:-$TMPDIR}
tmpdir=${tmpdir:-/tmp}
fifo="$tmpdir/limitmem_$$_cgroup_closer"
mkfifo --mode=u=rw,go= "$fifo"
sudo -b sh -c "head -c1 '$fifo' >/dev/null ; cgdelete -g 'memory:$cgname'"

# spawn subshell to run in the cgroup. If the command fails we still want to remove the cgroup so unset '-e'.
set +e
(
set -e
# move subshell into cgroup
sudo cgclassify -g "memory:$cgname" --sticky `sh -c 'echo $PPID'`  # $$ returns the main shell's pid, not this subshell's.
exec "$@"
)

# grab exit code 
exitcode=$?

set -e

# show memory usage summary

peak_mem=`cgget -g "memory:$cgname" | grep memory.max_usage_in_bytes | cut -d\  -f2`
failcount=`cgget -g "memory:$cgname" | grep memory.failcnt | cut -d\  -f2`
percent=`expr "$peak_mem" / \( "$bytes_limit" / 100 \)`

echo "peak memory used: $peak_mem ($percent%); exceeded limit $failcount times" >&2

if [ "$memsw" = 1 ]
then
    peak_swap=`cgget -g "memory:$cgname" | grep memory.memsw.max_usage_in_bytes | cut -d\  -f2`
    swap_failcount=`cgget -g "memory:$cgname" |grep memory.memsw.failcnt | cut -d\  -f2`
    swap_percent=`expr "$peak_swap" / \( "$bytes_swap_limit" / 100 \)`

    echo "peak virtual memory used: $peak_swap ($swap_percent%); exceeded limit $swap_failcount times" >&2
fi

# remove cgroup by sending a byte through the pipe
echo 1 > "$fifo"
rm "$fifo"

exit $exitcode

1
call to cgmanager_create_sync failed: invalid requestहर प्रक्रिया के लिए मैं साथ चलने की कोशिश करता हूं limitmem 100M processname। मैं Xubuntu 16.04 LTS पर हूं और यह पैकेज स्थापित है।
आरोन फ्रेंके

अप्स, मुझे यह त्रुटि संदेश मिला: $ limitmem 400M rstudio limiting memory to 400M (cgroup limitmem_24575) for command rstudio Error org.freedesktop.DBus.Error.InvalidArgs: invalid request कोई विचार?
R Kiselev

@Riseise cgmanager अब पदावनत है, और उबंटू 17.10 में भी उपलब्ध नहीं है। सिस्टमड आपी जो इसका उपयोग करता है उसे किसी बिंदु पर बदल दिया गया था, इसलिए शायद यही कारण है। मैंने cgroup-tools कमांड का उपयोग करने के लिए स्क्रिप्ट को अपडेट किया है।
JanKanis

यदि percentशून्य में परिणाम के लिए गणना , exprस्थिति कोड 1 है, और यह स्क्रिप्ट समय से पहले निकल जाती है। करने के लिए लाइन बदलने का सुझाव देते: percent=$(( "$peak_mem" / $(( "$bytes_limit" / 100 )) )): (संदर्भ unix.stackexchange.com/questions/63166/... )
विल्ली Ballenthin

अगर मैं सीमा से ऊपर जाऊं तो मैं अपनी प्रक्रिया को मारने के लिए cgroup को कैसे कॉन्फ़िगर कर सकता हूं?
d9ngle

7

daemontoolsमार्क जॉनसन द्वारा सुझाए गए औजारों के अलावा , आप इस पर भी विचार कर सकते हैं कि chpstकौन सा पाया जाता है runit। रनिट में ही बंडल किया गया है busybox, इसलिए आप पहले से ही इसे स्थापित कर सकते हैं।

मैन ऑफ द पेजchpst विकल्प दिखाता है:

-m बाइट्स मेमोरी को सीमित करता है। डेटा सेगमेंट, स्टैक सेगमेंट, लॉक्ड फिजिकल पेज और सभी सेगमेंट की कुल प्रक्रिया को बाइट्स बाइट्स तक सीमित करें।


3

मैं उबंटू 18.04.2 एलटीएस चला रहा हूं और जनकानी स्क्रिप्ट मेरे लिए काम नहीं करती है क्योंकि वह सुझाव देता है। अनलिमिटेड स्वैप के limitmem 100M scriptसाथ रनिंग 100MB RAM को सीमित कर रहा है ।

limitmem 100M -s 100M scriptजैसा cgget -g "memory:$cgname"कि नाम का कोई पैरामीटर नहीं है, रनिंग चुपचाप विफल रहता है memory.memsw.limit_in_bytes

इसलिए मैंने स्वैप अक्षम कर दिया:

# create cgroup
sudo cgcreate -g "memory:$cgname"
sudo cgset -r memory.limit_in_bytes="$limit" "$cgname"
sudo cgset -r memory.swappiness=0 "$cgname"
bytes_limit=`cgget -g "memory:$cgname" | grep memory.limit_in_bytes | cut -d\  -f2`

@sourcejedi ने इसे जोड़ा :)
d9ngle

2
सही है, मैंने अपना उत्तर संपादित किया। स्वैप सीमा को सक्षम करने के लिए आपको अपने सिस्टम पर स्वैप अकाउंटिंग को सक्षम करना होगा। वहाँ एक छोटे से रन ओवरहेड है, इसलिए यह उबंटू पर डिफ़ॉल्ट रूप से सक्षम नहीं है। मेरा संपादन देखें।
जनकानी

2

किसी भी सिस्टम-आधारित डिस्ट्रो पर आप सिस्टम-रन के माध्यम से परोक्ष रूप से cgroups का उपयोग कर सकते हैं। pdftoppm500M RAM तक सीमित करने के आपके मामले के लिए, उदाहरण के लिए :

systemd-run --scope -p MemoryLimit=500M pdftoppm

नोट: यह आपसे पासवर्ड मांगता है, लेकिन ऐप आपके उपयोगकर्ता के रूप में लॉन्च हो जाता है। इसे आपको यह सोचकर बहकने न दें कि कमांड की जरूरत है sudo, क्योंकि यह कमांड को रूट के तहत चलाने का कारण होगा, जो शायद ही आपका इरादा था।

यदि आप पासवर्ड दर्ज नहीं करना चाहते हैं (आखिरकार, एक उपयोगकर्ता के रूप में आप अपनी मेमोरी के मालिक हैं, तो आपको इसे सीमित करने के लिए पासवर्ड की आवश्यकता क्यों होगी) , आप --userविकल्प का उपयोग कर सकते हैं , हालांकि इसके लिए आपको काम करने की आवश्यकता है cgroupsv2 समर्थन सक्षम होना चाहिए, जो सही है अब कर्नेल पैरामीटर के साथ बूट करने की आवश्यकता हैsystemd.unified_cgroup_hierarchy

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.