❶ linux 最多支持多少個線程
默認情況下:
主線程+輔助線程 +<253個自己的線程<=255
含主線程和一個輔助線程,最多255個,即一個用戶只能生成253個線程。
Linux最大線程數限制及當前線程數查詢:
1、總結系統限制有:
/proc/sys/kernel/pid_max #查系統支持的最大線程數,一般會很大,相當於理論值
/proc/sys/kernel/thread-max
max_user_process(ulimit -u) #系統限制某用戶下最多可以運行多少進程或線程
/proc/sys/vm/max_map_count
硬體內存大小
2、java虛擬機本身限制:
-Xms #intial java heap size
-Xmx #maximum java heap size
-Xss #the stack size for each thread
3、查詢當前某程序的線程或進程數
pstree -p `ps -e | grep java | awk '{print $1}'` | wc -l 或 pstree -p 3660 | wc -l
4、查詢當前整個系統已用的線程或進程數
pstree -p | wc -l
1、 cat /proc/${pid}/status
2、pstree -p ${pid}
3、top -p ${pid} 再按H 或者直接輸入 top -bH -d 3 -p ${pid}
top -H
手冊中說:-H : Threads toggle
加上這個選項啟動top,top一行顯示一個線程。否則,它一行顯示一個進程。
4、ps xH
手冊中說:H Show threads as if they were processes
這樣可以查看所有存在的線程。
5、ps -mp <PID>
手冊中說:m Show threads after processes
這樣可以查看一個進程起的線程數。
❷ Linux中,shell腳本如何使用信號機制去控制線程的開啟關閉
trap是Linux的內建命令,用於捕捉信號,trap命令可以指定收到某種信號時所執行的命令。trap命令的格式如下:trap command sig1 sig2 ... sigN,當接收到sinN中任意一個信號時,執行command命令,command命令完成後繼續接收到信號前的操作,直到腳本結束。利用trap命令捕捉INT信號(即與Ctrl+c綁定的中斷信號)。trap還可以忽略某些信號,將command用空字元串代替即可,如trap "" TERM INT,忽略kill %n和Ctrl+c發送的信號(kill發送的是TERM信號)。Linux更強勁的殺死進程的命令:kill -9 進程號(或kill -9 %n作業號)等價與kill -KILL 進程號。
舉個例子:
最近小A需要生產2015年全年的KPI數據報表,現在小A已經將生產腳本寫好了,生產腳本一次只能生產指定一天的KPI數據,假設跑一次生產腳本需要5分鍾,那麼:
如果是循環順序執行,那麼需要時間:5 * 365 = 1825 分鍾,約等於 6 天
如果是一次性放到linux後台並發執行,365個後台任務,系統可承受不住哦!
既然不能一次性把365個任務放到linux後台執行,那麼,能不能實現自動地每次將N個任務放到後台並發執行呢?當然是可以的啦。
#!/bin/bash
source/etc/profile;
#-----------------------------
tempfifo=$$.fifo#$$表示當前執行文件的PID
begin_date=$1#開始時間
end_date=$2#結束時間
if[$#-eq2]
then
if["$begin_date">"$end_date"]
then
echo"Error!$begin_dateisgreaterthan$end_date"
exit1;
fi
else
echo"Error!Notenoughparams."
echo"Sample:shloop_kpi2015-12-012015-12-07"
exit2;
fi
#-----------------------------
trap"exec1000>&-;exec1000<&-;exit0"2
mkfifo$tempfifo
exec1000<>$tempfifo
rm-rf$tempfifo
for((i=1;i<=8;i++))
do
echo>&1000
done
while[$begin_date!=$end_date]
do
read-u1000
{
echo$begin_date
hive-fkpi_report.sql--hivevardate=$begin_date
echo>&1000
}&
begin_date=`date-d"+1day$begin_date"+"%Y-%m-%d"`
done
wait
echo"done!!!!!!!!!!"
第6~22行:比如:sh loop_kpi_report.sh 2015-01-01 2015-12-01:
$1表示腳本入參的第一個參數,等於2015-01-01
$2表示腳本入參的第二個參數,等於2015-12-01
$#表示腳本入參的個數,等於2
第13行用於比較傳入的兩個日期的大小,>是轉義
第26行:表示在腳本運行過程中,如果接收到Ctrl+C中斷命令,則關閉文件描述符1000的讀寫,並正常退出
exec 1000>&-;表示關閉文件描述符1000的寫
exec 1000<&-;表示關閉文件描述符1000的讀
trap是捕獲中斷命令
第27~29行:
第27行,創建一個管道文件
第28行,將文件描述符1000與FIFO進行綁定,<讀的綁定,>寫的綁定,<>則標識對文件描述符1000的所有操作等同於對管道文件$tempfifo的操作
第29行,可能會有這樣的疑問:為什麼不直接使用管道文件呢?事實上這並非多此一舉,管道的一個重要特性,就是讀寫必須同時存在,缺失某一個操作,另一個操作就是滯留,而第28行的綁定文件描述符(讀、寫綁定)正好解決了這個問題
第31~34行:對文件描述符1000進行寫入操作。通過循環寫入8個空行,這個8就是我們要定義的後台並發的線程數。為什麼是寫空行而不是寫其它字元?因為管道文件的讀取,是以行為單位的
第37~42行:
第37行,read -u1000的作用就是讀取管道中的一行,在這里就是讀取一個空行;每次讀取管道就會減少一個空行
第39~41行,注意到第42行結尾的&嗎?它表示進程放到linux後台中執行
第41行,執行完後台任務之後,往文件描述符1000中寫入一個空行。這是關鍵所在了,由於read -u1000每次操作,都會導致管道減少一個空行,當linux後台放入了8個任務之後,由於文件描述符1000沒有可讀取的空行,將導致read -u1000一直處於等待。
❸ Linux線程及同步
linux多線程
1.線程概述
線程是一個進程內的基本調度單位,也可以稱為輕量級進程。線程是在共享內存空間中並發的多道執行路徑,它們共享一個進程的資源,如文件描述和信號處理。因此,大大減少了上下文切換的開銷。一個進程可以有多個線程,也就
是有多個線程式控制製表及堆棧寄存器,但卻共享一個用戶地址空間。
2.線程實現
線程創建pthread_create()
所需頭文件#include
<pthread.h>
函數原型int
pthread_create
((pthread_t
*thread,
pthread_attr_t
*attr,
thread:線程標識符
attr:線程屬性設置
start_routine:線程函數的起始地址
arg:傳遞給start_routine的參數
函數返回值
成功:0
出錯:-1
線程退出pthread_exit();
所需頭文件#include
<pthread.h>
函數原型void
pthread_exit(void
*retval)
函數傳入值retval:pthread_exit()調用者線程的返回值,可由其他函數如pthread_join
來檢索獲取
等待線程退出並釋放資源pthread_join()
所需頭文件#include
<pthread.h>
函數原型int
pthread_join
((pthread_t
th,
void
**thread_return))
函數傳入值
th:等待線程的標識符
thread_return:用戶定義的指針,用來存儲被等待線程的返回值(不為NULL時)
函數返回值
成功:0
出錯:-1
代碼舉例
1.
#include<pthread.h>
2.
#include<stdio.h>
3.
#include<errno.h>
4.
5.
/*線程1*/
6.
void
thread1()
7.
{
8.
int
i=0;
9.
10.
while(1)
11.
{
12.
printf(thread1:%d/n,i);
13.
if(i>3)
14.
pthread_exit(0);
15.
i++;
16.
sleep(1);
17.
}
18.
}
19.
20.
/*線程2*/
21.
void
thread2()
22.
{
23.
int
i=0;
24.
25.
while(1)
26.
{
27.
printf(thread2:%d/n,i);
28.
if(i>5)
29.
pthread_exit(0);
30.
i++;
31.
sleep(1);
32.
}
33.
}
34.
35.
int
main()
36.
{
37.
pthread_t
t1,t2;
38.
39.
/*創建線程*/
40.
pthread_create(&t1,NULL,(void
*)thread1,NULL);
41.
pthread_create(&t2,NULL,(void
*)thread2,NULL);
42.
/*等待線程退出*/
43.
pthread_join(t1,NULL);
44.
pthread_join(t2,NULL);
45.
return
0;
46.
}
3同步與互斥
<1>互斥鎖
互斥鎖的操作主要包括以下幾個步驟。
•
互斥鎖初始化:pthread_mutex_init
•
互斥鎖上鎖:pthread_mutex_lock
•
互斥鎖判斷上鎖:pthread_mutex_trylock
•
互斥鎖接鎖:pthread_mutex_unlock
•
消除互斥鎖:pthread_mutex_destroy
1.
#include<pthread.h>
2.
#include<stdio.h>
3.
#include<errno.h>
4.
5.
int
i=0;/*共享變數*/
6.
pthread_mutex_t
mutex=PTHREAD_MUTEX_INITIALIZER;/*互斥鎖*/
7.
8.
void
thread1()
9.
{
10.
int
ret;
11.
while(1)
12.
{
13.
14.
15.
ret=pthread_mutex_trylock(&mutex);/*判斷上鎖*/
16.
17.
if(ret!=EBUSY)
18.
{
19.
pthread_mutex_lock(&mutex);/*上鎖*/
20.
printf(This
is
thread1:%d/n,i);
21.
i++;
22.
pthread_mutex_unlock(&mutex);/*解鎖*/
23.
}
24.
sleep(1);
25.
}
26.
}
27.
28.
void
thread2()
29.
{int
ret;
30.
while(1)
31.
{
32.
33.
ret=pthread_mutex_trylock(&mutex);
34.
if(ret!=EBUSY)
35.
{
36.
pthread_mutex_lock(&mutex);
37.
printf(This
is
thread2:%d/n,i);
38.
i++;
39.
pthread_mutex_unlock(&mutex);
40.
}
41.
sleep(1);
42.
}
43.
}
44.
int
main()
45.
{
46.
pthread_t
t1,t2;
47.
pthread_mutex_init(&mutex,NULL);
48.
pthread_create(&t1,NULL,(void
*)thread1,NULL);
49.
pthread_create(&t2,NULL,(void
*)thread2,NULL);
50.
51.
pthread_join(t1,NULL);
52.
pthread_join(t2,NULL);
53.
54.
pthread_mutex_destroy(&mutex);
55.
return
0;
56.
}
<2>信號量
未進行同步處理的兩個線程
1.
#include<pthread.h>
2.
#include<stdio.h>
3.
#include<errno.h>
4.
5.
int
i=0;
6.
void
thread1()
7.
{
8.
9.
while(1)
10.
{
11.
printf(This
is
thread1:%d/n,i);
12.
i++;
13.
sleep(1);
14.
}
15.
}
16.
17.
18.
void
thread2()
19.
{
20.
21.
while(1)
22.
{
23.
printf(This
is
thread2:%d/n,i);
24.
i++;
25.
sleep(1);
26.
}
27.
}
28.
29.
int
main()
30.
{
31.
pthread_t
t1,t2;
32.
33.
pthread_create(&t1,NULL,(void
*)thread1,NULL);
34.
pthread_create(&t2,NULL,(void
*)thread2,NULL);
❹ Linux 線程同步有哪些方法
一、互斥鎖(mutex)
1.
初始化鎖。在Linux下,線程的互斥量數據類型是pthread_mutex_t。在使用前,要對它進行初始化。
靜態分配:pthread_mutex_t
mutex
=
PTHREAD_MUTEX_INITIALIZER;
動態分配:int
pthread_mutex_init(pthread_mutex_t
*mutex,
const
pthread_mutex_attr_t
*mutexattr);
2.
加鎖。對共享資源的訪問,要對互斥量進行加鎖,如果互斥量已經上了鎖,調用線程會阻塞,直到互斥量被解鎖。
int
pthread_mutex_lock(pthread_mutex
*mutex);
int
pthread_mutex_trylock(pthread_mutex_t
*mutex);
3.
解鎖。在完成了對共享資源的訪問後,要對互斥量進行解鎖。
int
pthread_mutex_unlock(pthread_mutex_t
*mutex);
4.
銷毀鎖。鎖在是使用完成後,需要進行銷毀以釋放資源。
int
pthread_mutex_destroy(pthread_mutex
*mutex);
二、條件變數(cond)
1.
初始化條件變數。
靜態態初始化,pthread_cond_t
cond
=
PTHREAD_COND_INITIALIER;
動態初始化,int
pthread_cond_init(pthread_cond_t
*cond,
pthread_condattr_t
*cond_attr);
2.
等待條件成立。釋放鎖,同時阻塞等待條件變數為真才行。timewait()設置等待時間,仍未signal,返回ETIMEOUT(加鎖保證只有一個線程wait)
int
pthread_cond_wait(pthread_cond_t
*cond,
pthread_mutex_t
*mutex);
int
pthread_cond_timewait(pthread_cond_t
*cond,pthread_mutex
*mutex,const
timespec
*abstime);
3.
激活條件變數。pthread_cond_signal,pthread_cond_broadcast(激活所有等待線程)
int
pthread_cond_signal(pthread_cond_t
*cond);
int
pthread_cond_broadcast(pthread_cond_t
*cond);
//解除所有線程的阻塞
4.
清除條件變數。無線程等待,否則返回EBUSY
int
pthread_cond_destroy(pthread_cond_t
*cond);
三、信號量(sem)
1.
信號量初始化。
int
sem_init
(sem_t
*sem
,
int
pshared,
unsigned
int
value);
這是對由sem指定的信號量進行初始化,設置好它的共享選項(linux
只支持為0,即表示它是當前進程的局部信號量),然後給它一個初始值VALUE。
2.
等待信號量。給信號量減1,然後等待直到信號量的值大於0。
int
sem_wait(sem_t
*sem);
3.
釋放信號量。信號量值加1。並通知其他等待線程。
int
sem_post(sem_t
*sem);
4.
銷毀信號量。我們用完信號量後都它進行清理。歸還佔有的一切資源。
int
sem_destroy(sem_t
*sem);
❺ linux 多線程環境下的幾種鎖機制
NO1
互斥量(Mutex)
互斥量是實現最簡單的鎖類型,因此有一些教科書一般以互斥量為例對鎖原語進行描述。互斥量的釋放並不僅僅依賴於釋放操作,還可以引入一個定皮返時器屬性。如果在釋放操作執行前發生定時器超時,則互斥量也會釋放代碼塊或共享存儲區供其他線程訪問。當有異常發生時,可使用try-finally語句來確保互斥量被釋放。定時器狀態或try-finally語句的使用可以避免產生死鎖。
遞歸鎖(Recursive
Lock)
遞歸鎖是指可以被當前持有該鎖的線程重復獲取,而不會導致該線程產生死鎖的鎖類型。對遞歸鎖而言,只有在當前持有線程的獲取鎖操作都有一個釋放操作與之對應時,其他線程才可以獲取該鎖。因此,在使用遞歸鎖時,必須要用足夠的釋放鎖操作來平衡獲取鎖操作,實現這一目標的最佳方式是在單入口單出口代碼塊的兩頭一一對應地使用獲取、釋放操作,做法和在普通鎖中一樣。遞歸鎖在遞歸函數中最有用。但是,總的來說,遞歸鎖比非遞歸鎖速度要慢。需要注意的是:調用線程獲得幾次遞歸鎖必須釋放幾次遞歸鎖。
以下為一個遞歸鎖的示例:
[cpp] view plain
Recursive_Lock L
void recursiveFunction (int count) {
L->acquire()
if (count > 0) {
count = count - 1;
recursiveFunction(count);
}
L->release();
}
讀寫鎖(Read-Write
lock) 讀寫鎖又稱為共享獨占鎖(shared-exclusive
lock)、多讀單寫鎖(multiple-read/single-write lock)或者非互斥信號量(non-mutual
exclusion
semaphore)。讀寫鎖允許多個線程同時進行讀訪問,但是在某一時刻卻最多隻能由一個線程執行寫操作。對於多個線程需要同時讀共享數據卻並不一定進行寫操作的應用來說,讀寫鎖是一種高效的同步機制。對於較長的共享數據,只為其設置一個讀寫鎖會導致較長的訪問時間,最好將其劃分為多個小段並設置多個讀寫鎖以進行同步。
這個讀寫鎖我們在學習資料庫的時候應該很熟悉的喲!
旋轉鎖(Spin
Lock)
旋轉鎖是一種非阻塞鎖,由某個線程獨占。腔指采伍握配用旋轉鎖時,等待線程並不靜態地阻塞在同步點,而是必須「旋轉」,不斷嘗試直到最終獲得該鎖。旋轉鎖多用於多處理器系統中。這是因為,如果在單核處理器中採用旋轉鎖,當一個線程正在「旋轉」時,將沒有執行資源可供另一釋放鎖的線程使用。旋轉鎖適合於任何鎖持有時間少於將一個線程阻塞和喚醒所需時間的場合。線程式控制制的變更,包括線程上下文的切換和線程數據結構的更新,可能比旋轉鎖需要更多的指令周期。旋轉鎖的持有時間應該限制在線程上下文切換時間的50%到100%之間(Kleiman,1996年)。在線程調用其他子系統時,線程不應持有旋轉鎖。對旋轉鎖的不當使用可能會導致線程餓死,因此需謹慎使用這種鎖機制。旋轉鎖導致的餓死問題可使用排隊技術來解決,即每個等待線程按照先進先出的順序或者隊列結構在一個獨立的局部標識上進行旋轉。
學習了這些,果然受益匪淺,在今後的coding中,我得挨個試試咯。
❻ linux進程、線程及調度演算法(三)
調度策略值得是大家都在ready時,並且CPU已經被調度時,決定誰來運行,誰來被調度。
兩者之間有一定矛盾。
響應的優化,意味著高優先順序會搶占優先順序,會花時間在上下文切換,會影響吞吐。
上下文切換的時間是很短的,幾微妙就能搞定。上下文切換本身對吞吐並多大影響, 重要的是,切換後引起的cpu 的 cache miss.
每次切換APP, 數據都要重新load一次。
Linux 會盡可能的在響應與吞吐之間尋找平衡。比如在編譯linux的時候,會讓你選擇 kernal features -> Preemption model.
搶占模型會影響linux的調度演算法。
所以 ARM 的架構都是big+LITTLE, 一個很猛CPU+ 多個 性能較差的 CPU, 那麼可以把I/O型任務的調度 放在 LITTLE CPU上。需要計算的放在big上。
早期2.6 內核將優先順序劃分了 0-139 bit的優先順序。數值越低,優先順序越高。0-99優先順序 都是 RT(即時響應)的 ,100-139都是非RT的,即normal。
調度的時候 看哪個bitmap 中的 優先順序上有任務ready。可能多個任務哦。
在普通優先順序線程調度中,高優先順序並不代表對低優先順序的絕對優勢。會在不同優先順序進行輪轉。
100 就是比101高,101也會比102高,但100 不會堵著101。
眾屌絲進程在輪轉時,優先順序高的:
初始設置nice值為0,linux 會探測 你是喜歡睡眠,還是幹活。越喜歡睡,linux 越獎勵你,優先順序上升(nice值減少)。越喜歡幹活,優先順序下降(nice值增加)。所以一個進程在linux中,干著干著 優先順序越低,睡著睡著 優先順序越高。
後期linux補丁中
紅黑樹,數據結構, 左邊節點小於右邊節點
同時兼顧了 CPU/IO 和 nice。
數值代表著 進程運行到目前為止的virtual runtime 時間。
(pyhsical runtime) / weight * 1024(系數)。
優先調度 節點值(vruntime)最小的線程。權重weight 其實有nice 來控制。
一個線程一旦被調度到,則物理運行時間增加,vruntime增加,往左邊走。
weight的增加,也導致vruntime減小,往右邊走。
總之 CFS讓線程 從左滾到右,從右滾到左。即照顧了I/O(喜歡睡,分子小) 也 照顧了 nice值低(分母高).所以 由喜歡睡,nice值又低的線程,最容易被調度到。
自動調整,無需向nice一樣做出獎勵懲罰動作,個人理解權重其實相當於nice
但是 此時 來一個 0-99的線程,進行RT調度,都可以瞬間秒殺你!因為人家不是普通的,是RT的!
一個多線程的進程中,每個線程的調度的策略 如 fifo rr normal, 都可以不同。每一個的優先順序都可以不一樣。
實驗舉例, 創建2個線程,同時開2個:
運行2次,創建兩個進程
sudo renice -n -5(nice -5級別) -g(global), 會明顯看到 一個進程的CPU佔用率是另一個的 3倍。
為什麼cpu都已經達到200%,為什麼系統不覺得卡呢?因為,我們的線程在未設置優先順序時,是normal調度模式,且是 CPU消耗型 調度級別其實不高。
利用chrt工具,可以將進程 調整為 50 從normal的調度策略 升為RT (fifo)級別的調度策略,會出現:
chrt , nice renice 的調度策略 都是以線程為單位的,以上 設置的將進程下的所有線程進行設置nice值
線程是調度單位,進程不是,進程是資源封裝單位!
兩個同樣死循環的normal優先順序線程,其中一個nice值降低,該線程的CPU 利用率就會比另一個CPU的利用率高。