歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
Linux教程網 >> Linux基礎 >> 關於Linux >> linux的ulimit各種限制之深入分析

linux的ulimit各種限制之深入分析

日期:2017/3/1 14:56:11   编辑:關於Linux
linux的ulimit各種限制之深入分析
一般可以通過ulimit命令或編輯/etc/security/limits.conf重新加載的方式使之生效 通過ulimit比較直接,但只在當前的session有效,limits.conf中可以根據用戶和限制項使用戶在下次登錄中生效. 對於limits.conf的設定是通過pam_limits.so的加載生效的,比如/etc/pam.d/sshd,這樣通過ssh登錄時會加載limit. 又或者在/etc/pam.d/login加載生效. 下面將對各種限制進行分析 core file size (blocks, -c) 0 data seg size (kbytes, -d) unlimited scheduling priority (-e) 20 a file size (blocks, -f) unlimited a pending signals (-i) 16382 max locked memory (kbytes, -l) 64 a max memory size (kbytes, -m) unlimited a open files (-n) 1024 a pipe size (512 bytes, -p) 8 POSIX message queues (bytes, -q) 819200 real-time priority (-r) 0 stack size (kbytes, -s) 8192 cpu time (seconds, -t) unlimited max user processes (-u) unlimited virtual memory (kbytes, -v) unlimited file locks (-x) unlimited 一)限制進程產生的文件大小(file size) 先來說說ulimit的硬限制和軟限制 硬限制用-H參數,軟限制用-S參數. ulimit -a看到的是軟限制,通過ulimit -a -H可以看到硬限制. 如果ulimit不限定使用-H或-S,此時它會同時把兩類限制都改掉的. 軟限制可以限制用戶/組對資源的使用,硬限制的作用是控制軟限制. 超級用戶和普通用戶都可以擴大硬限制,但超級用戶可以縮小硬限制,普通用戶則不能縮小硬限制. 硬限制設定後,設定軟限制時只能是小於或等於硬限制. 下面的測試應用於硬限制和軟限制. 1)軟限制不能超過硬限制 在超級用戶下,同時修改硬/軟限制,使當前會話只能建100KB的文件 ulimit -f 100 查看當前創建文件大小的硬限制為100KB ulimit -H -f 100 此時限制當前會話的軟限制為1000KB,出現不能修改的報錯 ulimit -S -f 1000 -bash: ulimit: file size: cannot modify limit: Invalid argument 2)硬限制不能小於軟限制 在超級用戶下,用戶查看當前的軟限制,此時為unlmiited ulimit -S -f unlimited 此時修改當前會話創建文件大小的硬限制為1000KB,出現不能修改的報錯,說明硬限制不能小於軟限制 ulimit -H -f 1000 -bash: ulimit: file size: cannot modify limit: Invalid argument 如果我們把創建文件大小的軟限制改為900KB,此後就可以修改它的硬限制了 ulimit -S -f 900 ulimit -H -f 1000 3)普通用戶只能縮小硬限制,超級用戶可以擴大硬限制 用普通用戶進入系統 su - test 查看創建文件大小的硬限制 ulimit -H -f unlimited 此時可以縮小該硬限制 ulimit -H -f 1000 但不能擴大該硬限制 ulimit -H -f 10000 4)硬限制控制軟限制,軟限制來限制用戶對資源的使用 用軟限制限制創建文件的大小為1000KB ulimit -S -f 1000 用硬限制限制創建文件的大小為2000KB ulimit -H -f 2000 創建3MB大小的文件 dd if=/dev/zero of=/tmp/test bs=3M count=1 File size limit exceeded 查看/tmp/test的大小為1000KB,說明軟限制對資源的控制是起決定性作用的. ls -lh /tmp/test -rw-r--r-- 1 root root 1000K 2010-10-15 23:04 /tmp/test file size單位是KB. 二)關於進程優先級的限制(scheduling priority) 這裡的優先級指NICE值 這個值只對普通用戶起作用,對超級用戶不起作用,這個問題是由於CAP_SYS_NICE造成的. 例如調整普通用戶可以使用的nice值為-10到20之間. 硬限制nice的限制為-15到20之間. ulimit -H -e 35 軟限制nice的限制為-10到20之間 ulimit -S -e 30 用nice命令,使執行ls的nice值為-10 nice -n -10 ls /tmp ssh-BossiP2810 ssh-KITFTp2620 ssh-vIQDXV3333 用nice命令,使執行ls的nice值為-11,此時超過了ulimit對nice的軟限制,出現了異常. nice -n -11 ls /tmp nice: cannot set niceness: Permission denied 三)內存鎖定值的限制(max locked memory) 這個值只對普通用戶起作用,對超級用戶不起作用,這個問題是由於CAP_IPC_LOCK造成的. linux對內存是分頁管理的,這意味著有不需要時,在物理內存的數據會被換到交換區或磁盤上. 有需要時會被交換到物理內存,而將數據鎖定到物理內存可以避免數據的換入/換出. 采用鎖定內存有兩個理由: 1)由於程序設計上需要,比如oracle等軟件,就需要將數據鎖定到物理內存. 2)主要是安全上的需要,比如用戶名和密碼等等,被交換到swap或磁盤,有洩密的可能,所以一直將其鎖定到物理內存. 鎖定內存的動作由mlock()函數來完成 mlock的原型如下: int mlock(const void *addr,size_t len); 測試程序如下: #include <stdio.h> #include <sys/mman.h> int main(int argc, char* argv[]) { int array[2048]; if (mlock((const void *)array, sizeof(array)) == -1) { perror("mlock: "); return -1; } printf("success to lock stack mem at: %p, len=%zd\n", array, sizeof(array)); if (munlock((const void *)array, sizeof(array)) == -1) { perror("munlock: "); return -1; } printf("success to unlock stack mem at: %p, len=%zd\n", array, sizeof(array)); return 0; } gcc mlock_test.c -o mlock_test 上面這個程序,鎖定2KB的數據到物理內存中,我們調整ulimit的max locked memory. ulimit -H -l 4 ulimit -S -l 1 ./mlock_test mlock: : Cannot allocate memory 我們放大max locked memory的限制到4KB,可以執行上面的程序了. ulimit -S -l 4 ./mlock_test success to lock stack mem at: 0x7fff1f039500, len=2048 success to unlock stack mem at: 0x7fff1f039500, len=2048 注意:如果調整到3KB也不能執行上面的程序,原因是除了這段代碼外,我們還會用其它動態鏈接庫. 四)進程打開文件的限制(open files) 這個值針對所有用戶,表示可以在進程中打開的文件數. 例如我們將open files的值改為3 ulimit -n 3 此時打開/etc/passwd文件時失敗了. cat /etc/passwd -bash: start_pipeline: pgrp pipe: Too many open files -bash: /bin/cat: Too many open files 五)信號可以被掛起的最大數(pending signals) 這個值針對所有用戶,表示可以被掛起/阻塞的最大信號數量 我們用以下的程序進行測試,源程序如下: #include <stdio.h> #include <string.h> #include <stdlib.h> #include <signal.h> #include <unistd.h> volatile int done = 0; void handler (int sig) { const char *str = "handled...\n"; write (1, str, strlen(str)); done = 1; } void child(void) { int i; for (i = 0; i < 3; i++){ kill(getppid(), SIGRTMIN); printf("child - BANG!\n"); } exit (0); } int main (int argc, char *argv[]) { signal (SIGRTMIN, handler); sigset_t newset, oldset; sigfillset(&newset); sigprocmask(SIG_BLOCK, &newset, &oldset); pid_t pid = fork(); if (pid == 0) child(); printf("parent sleeping \n"); int r = sleep(3); printf("woke up! r=%d\n", r); sigprocmask(SIG_SETMASK, &oldset, NULL); while (!done){ }; printf("exiting\n"); exit(0); } 編譯源程序: gcc test.c -o test 執行程序test,這時子程序發送了三次SIGRTMIN信號,父程序在過3秒後,接收並處理該信號. ./test parent sleeping child - BANG! child - BANG! child - BANG! woke up! r=0 handled... handled... handled... exiting 注意:這裡有采用的是發送實時信號(SIGRTMIN),如:kill(getppid(), SIGRTMIN); 如果不是實時信號,則只能接收一次. 如果我們將pending signals值改為2,這裡將只能保證掛起兩個信號,第三個信號將被忽略.如下: ulimit -i 2 ./test parent sleeping child - BANG! child - BANG! child - BANG! woke up! r=0 handled... handled... exiting 六)可以創建使用POSIX消息隊列的最大值,單位為bytes.(POSIX message queues) 我們用下面的程序對POSIX消息隊列的限制進行測試,如下: #include <stdio.h> #include <string.h> #include <stdlib.h> #include <unistd.h> #include <mqueue.h> #include <sys/stat.h> #include <sys/wait.h> struct message{ char mtext[128]; }; int send_msg(int qid, int pri, const char text[]) { int r = mq_send(qid, text, strlen(text) + 1,pri); if (r == -1){ perror("mq_send"); } return r; } void producer(mqd_t qid) { send_msg(qid, 1, "This is my first message."); send_msg(qid, 1, "This is my second message."); send_msg(qid, 3, "No more messages."); } void consumer(mqd_t qid) { struct mq_attr mattr; do{ u_int pri; struct message msg; ssize_t len; len = mq_receive(qid, (char *)&msg, sizeof(msg), &pri); if (len == -1){ perror("mq_receive"); break; } printf("got pri %d '%s' len=%d\n", pri, msg.mtext, len); int r = mq_getattr(qid, &mattr); if (r == -1){ perror("mq_getattr"); break; } }while(mattr.mq_curmsgs); } int main (int argc, char *argv[]) { struct mq_attr mattr = { .mq_maxmsg = 10, .mq_msgsize = sizeof(struct message) }; mqd_t mqid = mq_open("/myq", O_CREAT|O_RDWR, S_IREAD|S_IWRITE, &mattr); if (mqid == (mqd_t) -1){ perror("mq_open"); exit (1); } pid_t pid = fork(); if (pid == 0){ producer(mqid); mq_close(mqid); exit(0); } else { int status; wait(&status); consumer(mqid); mq_close(mqid); } mq_unlink("/myq"); return 0; } 編譯: gcc test.c -o test 限制POSIX消息隊列的最大值為1000個字節 ulimit -q 1000 這裡我們執行test程序 ./test mq_open: Cannot allocate memory 程序報告無法分配內存. 用strace來跟蹤test的運行過程,在下面一條語句時報錯. mq_open("myq", O_RDWR|O_CREAT, 0600, {mq_maxmsg=10, mq_msgsize=128}) = -1 ENOMEM (Cannot allocate memory) {mq_maxmsg=10, mq_msgsize=128}即128*10=1280個字節,說明已經超過了1000個字節的POSIX消息隊列限制. 我們將POSIX消息隊列的最大值調整為1360時,程序可以運行. ulimit -q 1360 ./test got pri 3 'No more messages.' len=18 got pri 1 'This is my first message.' len=26 got pri 1 'This is my second message.' len=27 七)程序占用CPU的時間,單位是秒(cpu time) 我們用下面的代碼對程序占用CPU時間的限制進行測試 源程序如下: # include <stdio.h> # include <math.h> int main (void) { double pi=M_PI; double pisqrt; long i; while(1){ pisqrt=sqrt(pi); } return 0; } 編譯: gcc test.c -o test -lm 運行程序test,程序會一直循環下去,只有通過CTRL+C中斷. ./test ^C 用ulimit將程序占用CPU的時間改為2秒,再運行程序. ulimit -t 2 ./test Killed 程序最後被kill掉了. 八)限制程序實時優先級的范圍,只針對普通用戶.(real-time priority) 我們用下面的代碼對程序實時優先級的范圍進行測試 源程序如下: # include <stdio.h> int main (void) { int i; for (i=0;i<6;i++) { printf ("%d\n",i); sleep(1); } return 0; } 編譯: gcc test.c -o test 切換到普通用戶進行測試 su - ckhitler 用實時優先級20運行test程序 chrt -f 20 ./test chrt: failed to set pid 0's policy: Operation not permitted 我們用root將ulimit的實時優先級調整為20.再進行測試. su - root ulimit -r 20 切換到普通用戶,用實時優先級20運行程序,可以運行這個程序了. su - ckhitler chrt -r 20 ./test 0 1 2 3 4 5 以實時優先級50運行程序,還是報錯,說明ulimit的限制起了作用. chrt -r 50 ./test chrt: failed to set pid 0's policy: Operation not permitted 九)限制程序可以fork的進程數,只對普通用戶有效(max user processes) 我們用下面的代碼對程序的fork進程數的范圍進行測試 源程序如下: #include <unistd.h> #include <stdio.h> int main(void) { pid_t pid; int count=0; while (count<3){ pid=fork(); count++; printf("count= %d\n",count); } return 0; } 編譯: gcc test.c -o test count= 1 count= 2 count= 3 count= 2 count= 3 count= 1 count= 3 count= 2 count= 3 count= 3 count= 3 count= 2 count= 3 count= 3 程序fork的進程數成倍的增加,這裡是14個進程的輸出.除自身外,其它13個進程都是test程序fork出來的. 我們將fork的限定到12,如下: ulimit -u 12 再次執行test程序,這裡只有12個進程的輸出. ./test count= 1 count= 2 count= 3 count= 1 count= 2 count= 3 count= 2 count= 3 count= 3 count= 2 count= 3 count= 3 count= 3 十)限制core文件的大小(core file size) 我們用下面的代碼對程序生成core的大小進行測試 源代碼: #include <stdio.h> static void sub(void); int main(void) { sub(); return 0; } static void sub(void) { int *p = NULL; printf("%d", *p); } 編譯: gcc -g test.c -o test 運行程序test,出現段錯誤. ./test Segmentation fault (core dumped) 如果在當前目錄下沒有core文件,我們應該調整ulimit對core的大小進行限制,如果core文件大小在這裡指定為0,將不會產生core文件. 這裡設定core文件大小為10個blocks.注:一個blocks在這裡為1024個字節. ulimit -c 10 再次運行這個程序 ./test Segmentation fault (core dumped) 查看core文件的大小 ls -lh core -rw------- 1 root root 12K 2011-03-08 13:54 core 我們設定10個blocks應該是10*1024也不是10KB,為什麼它是12KB呢,因為它的遞增是4KB. 如果調整到14個blocks,我們將最大產生16KB的core文件. 十一)限制進程使用數據段的大小(data seg size) 一般來說這個限制會影響程序調用brk(系統調用)和sbrk(庫函數) 調用malloc時,如果發現vm不夠了就會用brk去內核申請. 限制可以使用最大為1KB的數據段 ulimit -d 1 用norff打開/etc/passwd文件 nroff /etc/passwd Segmentation fault 可以用strace來跟蹤程序的運行. strace nroff /etc/passwd 打印出如下的結果,證明程序在分配內存時不夠用時,調用brk申請新的內存,而由於ulimit的限制,導致申請失敗. munmap(0x7fc2abf00000, 104420) = 0 rt_sigprocmask(SIG_BLOCK, NULL, [], 8) = 0 open("/dev/tty", O_RDWR|O_NONBLOCK) = 3 close(3) = 0 brk(0) = 0xf5b000 brk(0xf5c000) = 0xf5b000 brk(0xf5c000) = 0xf5b000 brk(0xf5c000) = 0xf5b000 --- SIGSEGV (Segmentation fault) @ 0 (0) --- +++ killed by SIGSEGV +++ Segmentation fault 我們這裡用一個測試程序對data segment的限制進行測試. 源程序如下: #include <stdio.h> int main() { int start,end; start = sbrk(0); (char *)malloc(32*1024); end = sbrk(0); printf("hello I used %d vmemory\n",end - start); return 0; } gcc test.c -o test ./test hello I used 0 vmemory 通過ulimit將限制改為170KB 再次運行程序 ./test hello I used 167936 vmemory 十二)限制進程使用堆棧段的大小 我們用ulimit將堆棧段的大小調整為16,即16*1024. ulimit -s 16 再運行命令: ls -l /etc/ Segmentation fault (core dumped) 這時用strace跟蹤命令的運行過程 strace ls -l /etc/ 發現它調用getrlimit,這裡的限制是16*1024,不夠程序運行時用到的堆棧. getrlimit(RLIMIT_STACK, {rlim_cur=16*1024, rlim_max=16*1024}) = 0 注:在2.6.32系統上ls -l /etc/並不會出現堆棧不夠用的情況,這時可以用expect來觸發這個問題. 如: expect Tcl_Init failed: out of stack space (infinite loop?) 十三)限制進程使用虛擬內存的大小 我們用ulimit將虛擬內存調整為8192KB ulimit -v 8192 運行ls ls ls: error while loading shared libraries: libc.so.6: failed to map segment from shared object: Cannot allocate memory ls在加載libc.so.6動態庫的時候報了錯,提示內存不足. 用strace跟蹤ls的運行過程,看到下面的輸出,說明在做mmap映射出內存時,出現內存不夠用. mmap(NULL, 3680296, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = -1 ENOMEM (Cannot allocate memory) close(3) = 0 writev(2, [{"ls", 2}, {": ", 2}, {"error while loading shared libra"..., 36}, {": ", 2}, {"libc.so.6", 9}, {": ", 2}, {"failed to map segment from share"..., 40}, {": ", 2}, {"Cannot allocate memory", 22}, {"\n", 1}], 10ls: error while loading shared libraries: libc.so.6: failed to map segment from shared object: Cannot allocate memory 十四)剩下的三種ulimit限制說明(file locks/max memory size/pipe size) 文件鎖的限制只在2.4內核之前有用. 駐留內存的限制在很多系統裡也沒有作用. 管道的緩存不能改變,只能是8*512(bytes),也就是4096個字節.
Copyright © Linux教程網 All Rights Reserved