Trochę o ulimit oraz prlimit
Napisał: Patryk Krawaczyński
19/04/2019 w Administracja Brak komentarzy. (artykuł nr 690, ilość słów: 1548)
W
systemach operacyjnych każdy proces ma określony zestaw zasobów dostępnych w czasie jego życia. W przypadku Linuksa obejmują one takie rzeczy jak: ilość otwartych plików, rozmiar pliku z zrzutem pamięci, liczbę wątków, czy rozmiar stosu. Każdy zasób ma dwie granice: miękką (ang. soft) oraz twardą (ang. hard). Wartość graniczna zasobów może wahać się pomiędzy [miękką, a twardą] granicą. Pierwszą można uznać za wartość domyślną, a drugą za wartość maksymalnego pułapu. Zadaniem jądra jest upewnienie się, że te ograniczenia są egzekwowane.
W jaki sposób? Jądro ustawiając limity na zasoby per proces śledzi ich użycie, a nawet zabija dane procesy, gdy zachodzi taka konieczność (gdy te próbują przekroczyć twarde granice). To nasuwa pytanie: czy istnieje sposób na zmianę soft i hard limitów? Jeśli, tak to czy zwykły użytkownik jest zobligowany do tego? Jądro ma pewne reguły dotyczące zmiany limitu zasobów. Zapytanie lub ustawienie limitów zasobów procesów może być wykonane tylko przez proces wywołujący sam siebie lub przez proces, który jest jego właścicielem. Każdy inny proces chcący odpytać się lub ustawić limity zasobów innemu procesowi musi posiadać bit możliwości CAP_SYS_RESOURCE
. Tak, więc:
- Odpytanie o limity: każdy może odpytać o aktualne limity zasobów miękkich i twardych procesu, którego jest właścicielem.
- Ustawienie limitów: Hard limit raz ustawiony nie może być ponownie podniesiony (w aktualnej sesji). Soft limit może zostać podniesiony tylko do wartości twardego limitu, a więc poruszać się w zakresie [0, hard limit]. Jeśli do zmiany limitów użyjemy polecenia
ulimit
(bez przełączników -S / -H) to system wewnętrznie ustawi zarówno soft, jak i hard limit, co może mieć swoje nieodwracalne konsekwencje.
Uprawnienia do ustawienia limitów zasobów przedstawiają się następująco:
- Uprzywilejowany proces – (taki jak administrator [root] systemu lub posiadający bit
CAP_SYS_RESOURCE
) może zwiększać i zmniejszać miękkie i twarde limity. - Nieuprzywilejowany proces – może zwiększyć wartość soft limitu do wartości hard lub nieodwracalnie zmniejszyć twardy limit (raz zredukowany, może być tylko dalej zmniejszany) do wartości większej lub równej soft limitowi.
Wyjątek od reguły:Każda dobra reguła ma swój wyjątek: nieuprzywilejowany użytkownik może zmniejszyć i/lub zwiększyć rozmiar pliku z zrzutem pamięci (ang. core file size). Furtka ta została zostawiona dla programistów, aby mogli generować zrzuty pamięci i później analizować je np. za pomocą GDB.
Szybki test pokazujący, że reguły te obowiązują dla zwykłych użytkowników. Zmanipulujmy limit dotyczący ilości otwartych plików, który często jest kojarzony z błędem “Too many open files“:
agresor@darkstar:~$ ulimit -n 1024 agresor@darkstar:~$ ulimit -aS | grep 'open files' open files (-n) 1024 agresor@darkstar:~$ ulimit -aH | grep 'open files' open files (-n) 1048576 agresor@darkstar:~$ ulimit -n 3000 agresor@darkstar:~$ ulimit -aS | grep 'open files' open files (-n) 3000 agresor@darkstar:~$ ulimit -aH | grep 'open files' open files (-n) 3000 agresor@darkstar:~$ ulimit -n 3001 -bash: ulimit: open files: cannot modify limit: Operation not permitted agresor@darkstar:~$ ulimit -n 2000 agresor@darkstar:~$ ulimit -n 2000 agresor@darkstar:~$ ulimit -aS | grep 'open files' open files (-n) 2000 agresor@darkstar:~$ ulimit -aH | grep 'open files' open files (-n) 2000 agresor@darkstar:~$ ulimit -n 3000 -bash: ulimit: open files: cannot modify limit: Operation not permitted agresor@darkstar:~$
Powyższe polecenia możemy wytłumaczyć w następujący sposób:
- Aktualny miękki limit wynosi (standardowo) 1024,
- Miękki limit wynosi 1024, ale twardy już 1048576,
- Poprzez użycie polecenia
ulimit
(bez -S / -H) zmieniliśmy obydwa limity na wartość 3000, - Próba podniesienia limitów do wartości 3001 kończy się niepowodzeniem,
- Próba obniżenia limitów do wartości 2000 kończy się sukcesem,
- Upewnienie się, że soft i hard limit posiadają wartość 2000,
- Próba podniesienia limitów do poprzedniej wartości 3000 kończy się niepowodzeniem, ponieważ nową granicą jest [0,2000].
Przetestowanie tego samego scenariusza dla konta administratora pozostawiam Czytelnikowi jako ćwiczenie. Tradycyjnie polecenie ulimit
zostało użyte do ustawienia i wyświetlenia zasobów procesu. Program ten ma jednak dwa podstawowe problemy: działa tylko z powłoką bash i co ważniejsze – nie może być używany dla dowolnego ID procesu – PID – tylko dla procesów powłoki i jej dzieci. Skoro podniesienie limitu wymaga podwyższonych uprawnień wykorzystajmy dla przykładu sudo
do podbicia limitu otwartych plików z 1024 do 2000. Niestety polecenie: sudo ulimit -n 2000
nie zadziała. Dlaczego? Cóż, jeśli uruchamiamy sudo
oczekuje ono, że ulimit
jest plikiem binarnym i szuka go w ramach ścieżek zdefiniowanych w zmiennej $PATH
. Niestety ulimit
jest wbudowanym poleceniem powłoki poleceń dlatego sudo
nie wykona tej komendy. Możemy za to spróbować w ten sposób:
agresor@darkstar:~$ ulimit -n 1024 agresor@darkstar:~$ sudo bash -c "ulimit -n 2000 && ulimit -n" [sudo] password for agresor: ***** 2000
prlimit
Oprócz ulimit
istnieje jeszcze inny interfejs do odpytywania i ustawiania zasobów – prlimit
. Jego możliwość używania istnieje od wersji jądra 2.6.36, a sama binarka dostarczana jest wraz z paczką util-linux v2.21:
agresor@darkstar:~$ prlimit RESOURCE DESCRIPTION SOFT HARD UNITS AS address space limit unlimited unlimited bytes CORE max core file size 0 unlimited bytes CPU CPU time unlimited unlimited seconds DATA max data size unlimited unlimited bytes FSIZE max file size unlimited unlimited bytes LOCKS max number of file locks held unlimited unlimited locks MEMLOCK max locked-in-memory address space 16777216 16777216 bytes MSGQUEUE max bytes in POSIX mqueues 819200 819200 bytes NICE max nice prio allowed to raise 0 0 NOFILE max number of open files 1024 1048576 files NPROC max number of processes 7719 7719 processes RSS max resident set size unlimited unlimited bytes RTPRIO max real-time priority 0 0 RTTIME timeout for real-time tasks unlimited unlimited microsecs SIGPENDING max number of pending signals 7719 7719 signals STACK max stack size 8388608 unlimited bytes
Polecenie to może zostać użyte do modyfikacji limitów zgodnie z wymaganiami już uruchomionego procesu:
agresor@darkstar:~$ ps x PID TTY STAT TIME COMMAND 9480 ? Ss 0:00 /lib/systemd/systemd --user 9563 pts/0 Ss 0:00 -bash 9574 pts/0 R+ 0:00 ps x agresor@darkstar:~$ prlimit --pid 9563 --nofile=1024:3000 agresor@darkstar:~$ prlimit --pid 9563 --nofile RESOURCE DESCRIPTION SOFT HARD UNITS NOFILE max number of open files 1024 3000 files
lub uruchomieniu programu, który je odziedziczy:
agresor@darkstar:~$ prlimit --cpu=2 tar -pcvzf archive.tar.gz archive tar: archive.tar.gz: Wrote only 8192 of 10240 bytes tar: Child died with signal 9 tar: Error is not recoverable: exiting now
Kiedy proces przekroczył nowe ustawienia czasu procesora (dwie sekundy w powyższym przykładzie) został zabity przez jądro systemu. Od wersji jądra 2.6.36 możemy również podglądać limity zasobów danego procesu za pomocą systemu plików proc
: cat /proc/$PID/limits
.
Więcej informacji: prlimit: control resource limits, man bash, man prlimit, Ograniczenia z poziomu powłoki, Bash fork() bomb – rozwidlanie procesów