1. 程式人生 > 其它 >淺談linux下程序最大數、最大執行緒數、程序開啟的檔案數

淺談linux下程序最大數、最大執行緒數、程序開啟的檔案數

1、https://zhuanlan.zhihu.com/p/259301109

最大程序數目

LINUX中程序的最大理論數計算

  • 每個程序都要在全域性段描述表GDT中佔據兩個表項

每個程序的區域性段描述表LDT都作為一個獨立的段而存在,在全域性段描述表GDT中要有一個表項指向這個段的起始地址,並說明該段的長度以及其他一些 引數。除上之外,每個程序還有一個TSS結構(任務狀態段)也是一樣。所以,每個程序都要在全域性段描述表GDT中佔據兩個表項。

  • GDT的容量有多大呢?

段暫存器中用作GDT表下標的位段寬度是13位,所以GDT中可以有213=8192213=8192個描述項。

除一些系統的開銷(例如GDT中的第2項和第3項分別用於核心 的程式碼段和資料段,第4項和第5項永遠用於當前程序的程式碼段和資料段,第1項永遠是0,等等)以外,尚有8180個表項可供使用,所以理論上系統中最大的 程序數量是8180/2=40908180/2=4090。

所以系統中理論上最大的程序數是4090

系統中可建立的程序數實際值

linux核心通過程序標識值(process identification value)-PID來標示程序,PID是一個數,型別位pid_t, 實際上就是int型別

為了與老版本的Unix或者Linux相容,PID的最大值預設設定位32768(short int 短整型的最大值)。
  • 檢視

可以使用cat /proc/sys/kernel/pid_max來檢視系統中可建立的程序數實際值

  • 修改
ulimit -u 655351

設定完以後,雖然我們設定戶建立程序數的硬限制和軟限制都是65535,但是我們還不能使用建立65535個程序

我們在Linux還需要設定核心引數kernel.pid_max,這個引數我預設安裝都是32768,

所以即使使用root帳戶,卻不設定這個核心引數,整個系統最多可以建立的程序數就是32768,所以我們需要進行如下設定:

sysctl -w  kernel.pid_max=655351

最大執行緒數

linux 系統中單個程序的最大執行緒數有其最大的限制 PTHREAD_THREADS_MAX

這個限制可以在/usr/include/bits/local_lim.h中檢視
對 linuxthreads 這個值一般是 1024,對於 nptl 則沒有硬性的限制,僅僅受限於系統的資源

這個系統的資源主要就是執行緒的 stack 所佔用的記憶體,用 ulimit -s 可以檢視預設的執行緒棧大小,一般情況下,這個值是8M=8192KB

 

可以寫一段簡單的程式碼驗證最多可以建立多少個執行緒

include <stdio.h>
#include <stdlib.h>#include <pthread.h>void func()
{}int main(void)
{    int i = 0;
    pthread_t thread;    while ( 1 )
    {        if (pthread_create(&thread, NULL, func, NULL) != 0)
        {            return;
        }        i++;        printf("i = %d\n", i);
    }    return EXIT_SUCCESS;
}

試驗顯示,在我們的系統(Ubuntu-14.04-LTS-64bit)中linuxthreads 上最多可以建立 381 個執行緒,之後就會返回 EAGAIN

LINUX中單個程序理論上可以建立的最大執行緒數

而32位系統中,可以穿件381個執行緒,這個值和理論完全相符,因為 32 位 linux 下的程序使用者空間是 3G 的大小,也就是 3072M,用3072M/8M=3843072M/8M=384,但是實際上程式碼段和資料段等還要佔用一些空間,這個值應該向下取整到 383,再減去主執行緒,得到 382。

那為什麼 linuxthreads 上還要少一個執行緒呢?這可太對了,因為 linuxthreads 還需要一個管理執行緒

為了突破記憶體的限制,可以有兩種方法

  • 用ulimit -s 1024減小預設的棧大小
  • 呼叫pthread_create的時候用pthread_attr_getstacksize設定一個較小的棧大小

要注意的是,即使這樣的也無法突破1024 個執行緒的硬限制,除非重新編譯 C 庫

最大開啟檔案數

file-max系統最大開啟檔案描述符數

/proc/sys/fs/file-max中指定了系統範圍內所有程序可開啟的檔案控制代碼的數量限制(系統級別, kernel-level).

The value in file-max denotes the maximum number of file handles that the Linux kernel will allocate).

當收到”Too many open files in system”這樣的錯誤訊息時, 就應該曾加這個值了.

對於2.2的核心, 還需要考慮inode-max, 一般inode-max設定為file-max的4倍. 對於2.4及以後的核心, 沒有inode-max這個檔案了.

檢視實際值

可以使用cat /proc/sys/fs/file-max來檢視當前系統中單程序可開啟的檔案描述符數目
186405

 

設定

  • 臨時性
echo 1000000 > /proc/sys/fs/file-max
  • 永久性:在/etc/sysctl.conf中設定
fs.file-max = 1000000

nr_open是單個程序可分配的最大檔案數

核心支援的最大file handle數量,即一個程序最多使用的file handle數

the maximum number of files that can be opened by process。
A process cannot use more than NR_OPEN file descriptors.
一個程序不能使用超過NR_OPEN檔案描述符。12

 

需要C/C++ Linux伺服器架構師學習資料加qun獲取(資料包括C/C++,Linux,golang技術,Nginx,ZeroMQ,MySQL,Redis,fastdfs,MongoDB,ZK,流媒體,CDN,P2P,K8S,Docker,TCP/IP,協程,DPDK,ffmpeg等),免費分享

nofile程序最大開啟檔案描述符數

檢視實際值

ulimit -n

當然預設檢視的是軟資源限制值soft limit,如果想要檢視系統硬體所能支援的單程序最大開啟檔案描述符號的數目,可以使用ulimit -Hn

設定

  • 臨時性

通過ulimit -Sn設定最大開啟檔案描述符數的soft limit,注意soft limit不能大於hard limit(ulimit -Hn可檢視hard limit)

另外ulimit -n預設檢視的是soft limit,但是ulimit -n 1800000則是同時設定soft limit和hard limit。

對於非root使用者只能設定比原來小的hard limit。

  • 永久性

上面的方法只是臨時性的,登出重新登入就失效了,而且不能增大hard limit,只能在hard limit範圍內修改soft limit。

若要使修改永久有效,則需要在/etc/security/limits.conf中進行設定(需要root許可權),可新增如下兩行,表示使用者chanon最大開啟檔案描述符數的soft limit為1800000,hard limit為2000000。以下設定需要登出之後重新登入才能生效:

chanon           soft    nofile          102400
chanon           hard   nofile          40960012

設定nofile的hard limit還有一點要注意的就是hard limit不能大於/proc/sys/fs/nr_open,假如hard limit大於nr_open,登出後無法正常登入。

可以修改nr_open的值:

echo 2000000 > /proc/sys/fs/nr_open

file-max, nr_open, onfile之間的關係

針對使用者開啟最大檔案數的限制, 在limits.conf對應的nofile,不管是man手冊還是檔案中說明都只是一句話

“maximum number of open files”,

它其實對應是單個程序能開啟的最大檔案數,通常為了省事,我們想取消它的限制

根據man手冊中,“values -1, unlimited or infinity indicating no limit”,-1、unlimited、infinity都是表明不做限制

可是當你實際給nofile設定成這個值,等你重啟就會發現無法登入系統了。

由此可見,nofile是有一個上限的,同時用ulimit測試:

ulimit -n unlimited1
bash: ulimit: open files: cannot modify limit: 不允許的操作

 

寫一個簡單的for迴圈得出:

for  V in `seq  100000  10000000`;do ulimit -n $V;[[ $? != 0 ]]&&break;done1

再執行ulimit -n ,可以看到1048576就是nofile的最大值了,但為什麼是這個值?

1024∗1024=10485761024∗1024=1048576,當然這並沒有什麼卵用。

再跟蹤一下我們就會發現這個值其實是由核心引數nr_open定義的:

cat /proc/sys/fs/nr_open 1

到此我們就要說起nr_open,與file-max了,網上在說到設定最大檔案數時偶爾有些帖子也說到要修改file-max,字面上看file-max確實像是對應最大檔案數,而在linux核心文件中它們兩的解釋是:

  • file-max:
    The value in file-max denotes the maximum number of file- handles that the Linux kernel will allocate. When you get lots of error messages about running out of file handles, you might want to increase this limit
執行:grep -r MemTotal /proc/meminfo | awk ‘{printf(“%d”,$2/10)}’,可以看到與file-max是相近的;

 

 

  • nr_open:
    This denotes the maximum number of file-handles a process can allocate. Default value is 1024*1024 (1048576) which should be enough for most machines. Actual limit depends on RLIMIT_NOFILE resource limit.

file-handles(即檔案控制代碼),然後相比而言在UNIX/LINUX中我們接觸更多是file discriptor(FD,即檔案描述符),似乎file-handle在windows中是一個類似file discrptor的東東,但是我們討論的是linux,再google一下,我們可以精確到c語言中這兩個概念的區別,

據他們的討論file-handle應該是一個高層的物件,使用fopen,fread等函式來呼叫,而FD是底層的一個物件,可以通過open,read等函式來呼叫。

到此,我們應該可以下一個大致的結論了,file-max是核心可分配的最大檔案數,nr_open是單個程序可分配的最大檔案數,所以在我們使用ulimit或limits.conf來設定時,如果要超過預設的1048576值時需要先增大nr_open值(sysctl -w fs.nr_open=100000000或者直接寫入sysctl.conf檔案)。當然百萬級別的單程序最大file-handle開啟數應該也夠用了吧。。

  1. 所有程序開啟的檔案描述符數不能超過/proc/sys/fs/file-max
  2. 單個程序開啟的檔案描述符數不能超過user limit中nofile的soft limit
  3. nofile的soft limit不能超過其hard limit
  4. nofile的hard limit不能超過/proc/sys/fs/nr_open

其他

2.4核心與2.6核心的主要區別

在2.4核心的典型系統上(AS3/RH9),執行緒是用輕量程序實現的,每個執行緒要佔用一個程序ID,在伺服器程式上,如果遇到高點選率訪問,會造成程序表溢位,系統為了維護溢位的程序表,會有間歇的暫停服務現象,而2.6核心就不會發生由於大量執行緒的建立和銷燬導致程序表溢位的問題

執行緒結束必須釋放執行緒堆疊

就是說,執行緒函式必須呼叫pthread_exit()結束,否則直到主程序函式退出才釋放,特別是2.6核心環境,執行緒建立速度飛快,一不小心立刻記憶體被吃光,這一點反倒是2.4核心環境好,因為2.4核心建立的是程序,而且執行緒建立速度比2.6核心慢幾個數量級。特別提醒,在64位CPU,2.6核心建立執行緒的速度更加瘋狂,要是太快的話,加上usleep ()暫停一點點時間比較好

不要編需要鎖的執行緒應用

只有那些不需要互斥量的程式才能最大限度的利用執行緒程式設計帶來的好處,否則只會更慢,2.6核心是搶佔式核心,執行緒間共享衝突發生的機率遠比2.4核心環境高,尤其要注意執行緒安全,否則就算是單CPU也會發生莫名其妙的記憶體不同步(CPU的快取記憶體和主存內容不一致),Intel的新CPU為了效能使用NUMA架構,線上程程式設計中一定要注意揚長避短。

單程序伺服器最大併發執行緒數與記憶體

很有趣,在預設的ulimit引數下,不修改核心標頭檔案
AS3 512M記憶體最多1000併發持續連線
CentOS4.3 512M記憶體最多300併發持續連線
似乎是CentOS不如AS3,這裡主要原因是ulimit的配置造成,兩個系統預設的配置差距很大,要想單程序維持更多執行緒接收併發連線,就要儘量縮小 ulimit -s的引數,插更多的記憶體條,單程序伺服器上2000併發一點都不難,POSIX預設的限制是每程序64執行緒,但NTPL並非純正POSIX,不必理會這個限制,2.6核心下真正的限制是記憶體條的插槽數目(也許還有買記憶體的錢數)
最近幾天的程式設計中,注意到在32位x86平臺上2.6核心單程序建立最大執行緒數=VIRT上限/stack,與總記憶體數關係不大,32位x86系統預設的VIRT上限是3G(記憶體分配的3G+1G方式),預設 stack大小是10240K,因此單程序建立執行緒預設上限也就300(3072M / 10240K),用ulimit -s 修改stack到1024K則使上限升到大約3050。我手頭沒有64位系統,不知道2.6核心在64位上單程序建立執行緒上限(實際上是本人懶得在同事的機器上裝fc4_x86_64)。
前些天買了一套廉價的64位x86系統(64位賽楊+雜牌915主機板),安裝了CentOS4.3的x86_64版本,跑了一遍下面的小程式,得到的結果是:在ulimit -s 4096的情況下,單程序最大執行緒數在16000多一點,用top看
VIRT 的上限是64G,也就是36位, cat /proc/cpuinfo的結果是:address sizes : 36 bits physical, 48 bits virtual, 和我想象的標準64位系統不同, 我一直以為64位系統的記憶體空間也是64位的

附註1

單位裡某BSD FANS用AMD64筆記本跑小程式測試執行緒建立速度(執行緒建立後立即phread_detach()然後緊跟著pthread_exit(),共計 100萬個執行緒),同樣原始碼OpenBSD竟然比FreeBSD快了3倍,什麼時候OpenBSD也變得瘋狂起來了?

附註2

測試單程序建立執行緒上限C原始碼(test.c)

#include <stdio.h>
#include <string.h>
#include <stdlib.h>#include <unistd.h>#include <pthread.h>void * thread_null(void);int main(int argc, char *argv[])
{    unsigned int    i;    int             rc;    pthread_t       pool_id[65536]; //執行緒ID
    sleep(1);
    //建立執行緒    for(i = 0; i < 65536; i++)
    {        rc = pthread_create(pool_id + i, 0, (void *)thread_null, NULL);
        if (rc != 0)
        {            fprintf(stderr, "pthread_create() failure\r\nMax pthread num is %d\r\n", i);
            exit(-1);
        }    }    fprintf(stdout, "Max pthread num is 65536\r\nYour system is power_full\r\n");
    exit(0);
}void * thread_null(void){    pthread_detach(pthread_self());    sleep(60);
    pthread_exit(NULL);}
 

2、

3、