[轉]linux的ulimit各類限制之深刻分析

這是一篇很是好的文章,對ulimit的各個限制參數講得很是透徹。原文連接:http://home.lupaworld.com/home-space-uid-56821-do-blog-id-232810.htmlhtml

通常能夠經過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個字節.
linux

做者:yuandianlws 發表於2013-8-26 12:37:01 原文連接
閱讀:90 評論:0 查看評論
相關文章
相關標籤/搜索