導航:首頁 > 操作系統 > linuxhandler

linuxhandler

發布時間:2022-12-24 18:13:57

1. 5.2 linux中斷注冊

注冊中斷最常用的函數是request_irq

第 1個參數 irq 為中斷號
第 2 個參數 handler 為要中斷服務函數
第 3 個參數 flags為中斷標志位包含觸發方式,是否共享,是否支持嵌套等
第 4 個參數 name,通常是 設備驅動程序的名稱。該值用在 /proc/interrupt 系統文件上
第 5 個參數 dev 中斷名稱 可作為共享中斷時的中斷區別參數,也可以用來指定中斷服務函數需要參考的數據地址。建議將 設備結構指針作為 dev參數

flags參數定義

注冊中斷的另一個函數是request_threaded_irq
request_threaded_irq是將中斷處理函數線程化執行的介面,其實request_irq也是直接調用的request_threaded_irq,只不過線程化回調thread_fn設置為NULL,不進行中斷處理程序線程化處理。

和request_irq的參數有少許差異
handler:表示中斷服務常式,指向primary handler 和request_irq的中斷處理函數handler類似。中斷發生時優先執行primary handler;
如果primary handler 為NULL,且thread_fn不為NULL,那麼執行默認primary handler = irq_default_primary_handler。
thread_fn:中斷線程化,NULL表示沒有中斷線程化。thread_fn如果該參數不為NULL,內核會為該irq創建一個內核線程,
當中斷發生時,如果handler回調返回值是IRQ_WAKE_THREAD,內核將會激活中斷線程,
在中斷線程中,該回調函數將被調用,所以,該回調函數運行在進程上下文中,允許進行阻塞操作。

其中

其中

2. Oracle SQL Handler文件拷貝到Linux怎麼打開軟體Handler

把程序打成jar包放到Linux上

轉到目錄下執行命令 hadoop jar maprecer.jar /home/clq/export/java/count.jar hdfs://ubuntu:9000/out06/count/

上面一個是本地文件,一個是上傳hdfs位置

成功後出現:列印出來,你所要列印的字元。

package com.clq.hdfs;

import java.io.BufferedInputStream;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URI;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.FSDataOutputStream;
import org.apache.hadoop.fs.FileSystem;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.io.IOUtils;
import org.apache.hadoop.util.Progressable;

public class FileCopyWithProgress {
//********************************
//把本地的一個文件拷貝到hdfs上
//********************************
public static void main(String[] args) throws IOException {
String localSrc = args[0];
String dst = args[1];
InputStream in = new BufferedInputStream(new FileInputStream(localSrc));
Configuration conf = new Configuration();
FileSystem fs = FileSystem.get(URI.create(dst), conf);
FSDataOutputStream out = fs.create(new Path(dst), new Progressable() {
@Override
public void progress() {
System.out.print(".");
}
});
IOUtils.Bytes(in, out, conf, true);
}

}
可能出現異常:

Exception in thread "main" org.apache.hadoop.ipc.RemoteException: java.io.IOException: Cannot create /out06; already exists as a directory

at org.apache.hadoop.hdfs.server.namenode.FSNamesystem.startFileInternal(FSNamesystem.java:1569)

at org.apache.hadoop.hdfs.server.namenode.FSNamesystem.startFile(FSNamesystem.java:1527)

at org.apache.hadoop.hdfs.server.namenode.NameNode.create(NameNode.java:710)

at org.apache.hadoop.hdfs.server.namenode.NameNode.create(NameNode.java:689)

at sun.reflect.GeneratedMethodAccessor7.invoke(Unknown Source)

at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)

at java.lang.reflect.Method.invoke(Method.java:606)

at org.apache.hadoop.ipc.RPC$Server.call(RPC.java:587)

at org.apache.hadoop.ipc.Server$Handler$1.run(Server.java:1432)

at org.apache.hadoop.ipc.Server$Handler$1.run(Server.java:1428)

at java.security.AccessController.doPrivileged(Native Method)

at javax.security.auth.Subject.doAs(Subject.java:415)

說明你這個路徑在hdfs上已經存在,換一個即可。

3. linux輸入子系統是輪詢還是中斷

與Linux設備驅動中中斷處理相關的首先是申請與釋放IRQ的API request_irq()和free_irq(),

request_irq()的原型為:

int request_irq(unsigned int irq,

void (*handler)(int irq, void *dev_id,
struct pt_regs *regs),

unsigned long irqflags,

const char * devname,

void *dev_id);

irq是要申請的硬體中斷號;

handler是向系統登記的中斷處理函數,是一個回調函數,中斷發生時,系統調用這個函數,dev_id參數將被傳遞;

irqflags是中斷處理的屬性,若設置SA_INTERRUPT,標明中斷處理程序是快速處理程序,快速處理程序被調用時屏蔽所有中斷,慢速處理程序不屏蔽;若設置SA_SHIRQ,則多個設備共享中斷,dev_id在中斷共享時會用到,一般設置為這個設備的device結構本身或者NULL。

4. 關於 Handler 的這 20 個問題,你都清楚嗎

Android 11 開始,AsyncTask 正式謝幕,變成了不推薦使用的 API。官方建議採用 Kotlin 協程替代,或者自行實現。

事實上,無論是 AsyncTask 還是協程,背後都有 Handler 的功勞。無論從普及原理的角度、還是從自行實現的角度,我們都需要吃透這個 Android 系統所特有的線程間通信方式Handler 機制!

初嘗 Handler 機制的時候,原以為 Handler 類發揮了很大的作用。當你深入了解它的原理之後,會發現 Handler 只是該機制的 調用入口和回調 而已,最重要的東西是 Looper 和 MessagQueue,以及不斷流轉的 Message。

本次針對 Handler 機制常被提及和容易困擾的  20  個問題進行整理和回答,供大家解惑和回顧~

簡述下 Handler 機制的總體原理?

Looper 存在哪?如何可以保證線程獨有?

如何理解 ThreadLocal 的作用?

主線程 Main Looper 和一般 Looper 的異同?

Handler 或者說 Looper 如何切換線程?

Looper 的 loop() 死循環為什麼不卡死?

Looper 的等待是如何能夠准確喚醒的?

Message 如何獲取?為什麼這么設計?

MessageQueue 如何管理 Message?

理解 Message 和 MessageQueue 的異同?

Message 的執行時刻如何管理?

Handler、Mesage 和 Runnable 的關系如何理解?

IdleHandler 空閑 Message 了解過嗎?有什麼用?

非同步 Message 或同步屏障了解過嗎?怎麼用?什麼原理?

Looper 和 MessageQueue、Message 及 Handler 的關系?

Native 側的 NativeMessageQueue 和 Looper 的作用是?

Native 側如何使用 Looper?

Handler 為什麼可能導致內存泄露?如何避免?

Handler 在系統當中的應用

Android 為什麼不允許並發訪問 UI?

1. 簡述下 Handler 機制的總體原理?

Looper 准備和開啟輪循:

尚無 Message 的話,調用 Native 側的 pollOnce() 進入 無限等待

存在 Message,但執行時間 when 尚未滿足的話,調用 pollOnce() 時傳入剩餘時長參數進入 有限等待

Looper#prepare() 初始化線程獨有的 Looper 以及 MessageQueue

Looper#loop() 開啟 死循環 讀取 MessageQueue 中下一個滿足執行時間的 Message

Message 發送、入隊和出隊:

Native 側如果處於無限等待的話:任意線程向 Handler 發送 Message 或 Runnable 後,Message 將按照 when 條件的先後,被插入 Handler 持有的 Looper 實例所對應的 MessageQueue  中 適當的位置 。MessageQueue 發現有合適的 Message 插入後將調用 Native 側的 wake() 喚醒無限等待的線程。這將促使 MessageQueue 的讀取繼續 進入下一次循環 ,此刻 Queue 中已有滿足條件的 Message 則出隊返回給 Looper

Native 側如果處於有限等待的話:在等待指定時長後 epoll_wait 將返回。線程繼續讀取 MessageQueue,此刻因為時長條件將滿足將其出隊

Looper 處理 Message 的實現:

Looper 得到 Message 後回調 Message 的 callback 屬性即 Runnable,或依據 target 屬性即 Handler,去執行 Handler 的回調。

存在 mCallback 屬性的話回調 Handler$Callback

反之,回調 handleMessage()

2. Looper 存在哪?如何可以保證線程獨有?

Looper 實例被管理在靜態屬性 sThreadLocal 中

ThreadLocal 內部通過 ThreadLocalMap 持有 Looper,key 為 ThreadLocal 實例本身,value 即為 Looper 實例

每個 Thread 都有一個自己的 ThreadLocalMap,這樣可以保證每個線程對應一個獨立的 Looper 實例,進而保證 myLooper() 可以獲得線程獨有的 Looper

彩蛋:一個 App 擁有幾個 Looper 實例?幾個 ThreadLocal 實例?幾個 MessageQueue 實例?幾個 Message 實例?幾個 Handler 實例

一個線程只有一個 Looper 實例

一個 Looper 實例只對應著一個 MessageQueue 實例

一個 MessageQueue 實例可對應多個 Message 實例,其從 Message 靜態池裡獲取,存在 50 的上限

一個線程可以擁有多個 Handler 實例,其Handler 只是發送和執行任務邏輯的入口和出口

ThreadLocal 實例是靜態的,整個進程 共用一個實例 。每個 Looper 存放的 ThreadLocalMap 均弱引用它作為 key

3. 如何理解 ThreadLocal 的作用?

首先要明確並非不是用來切換線程的, 只是為了讓每個線程方便程獲取自己的 Looper 實例 ,見 Looper#myLooper()

後續可供 Handler 初始化時 指定其所屬的 Looper 線程

也可用來線程判斷自己是否 是主線程

4. 主線程 Main Looper 和一般 Looper 的異同?

區別:

Main Looper  不可 quit

主線程需要不斷讀取系統消息和用書輸入,是進程的入口,只可被系統直接終止。進而其 Looper 在創建的時候設置了 不可quit的標志 ,而 其他線程的 Looper 則可以也必須手動 quit

Main Looper 實例還被 靜態緩存

為了便於每個線程獲得主線程 Looper 實例,見 Looper#getMainLooper(),Main Looper 實例還作為 sMainLooper 屬性緩存到了 Looper 類中。

相同點:

都是通過 Looper#prepare() 間接調用 Looper 構造函數創建的實例

都被靜態實例 ThreadLocal 管理,方便每個線程獲取自己的 Looper 實例

彩蛋:主線程為什麼不用初始化 Looper?

App 的入口並非 MainActivity,也不是 Application,而是 ActivityThread。

其為了 Application、ContentProvider、Activity 等組件的運行,必須事先啟動不停接受輸入的 Looper 機制,所以在 main() 執行的最後將調用 prepareMainLooper() 創建 Looper 並調用 loop() 輪循。

不需要我們調用,也不可能有我們調用。

可以說如果主線程沒有創建 Looper 的話,我們的組件也不可能運行得到!

5. Handler 或者說 Looper 如何切換線程?

Handler 創建的時候指定了其所屬線程的 Looper,進而持有了 Looper 獨有的 MessageQueue

Looper#loop() 會持續讀取 MessageQueue 中合適的 Message,沒有 Message 的時候進入等待

當向 Handler 發送 Message 或 Runnable 後,會向持有的 MessageQueue 中插入 Message

Message 抵達並滿足條件後會喚醒 MessageQueue 所屬的線程,並將 Message 返回給 Looper

Looper 接著回調 Message 所指向的 Handler Callback 或 Runnable,達到線程切換的目的

簡言之,向 Handler 發送 Message 其實是向 Handler 所屬線程的獨有 MessageQueue 插入 Message。而線程獨有的 Looper 又會持續讀取該 MessageQueue。所以向其他線程的 Handler 發送完 Message,該線程的 Looper 將自動響應。

6. Looper 的 loop() 死循環為什麼不卡死?

為了讓主線程持續處理用戶的輸入,loop() 是 死循環 ,持續調用 MessageQueue#next() 讀取合適的 Message。

但當沒有 Message 的時候,會調用 pollOnce() 並通過 Linux 的 epoll 機制進入等待並釋放資源。同時 eventFd 會監聽 Message 抵達的寫入事件並進行喚醒。

這樣可以 空閑時釋放資源、不卡死線程,同時能持續接收輸入的目的 。

彩蛋1:loop() 後的處理為什麼不可執行

因為 loop() 是死循環,直到 quit 前後面的處理無法得到執行,所以避免將處理放在 loop() 的後面。

彩蛋2:Looper 等待的時候線程到底是什麼狀態?

調用 Linux 的 epoll 機制進入 等待 ,事實上 Java 側列印該線程的狀態,你會發現線程處於 Runnable 狀態,只不過 CPU 資源被暫時釋放。

7. Looper 的等待是如何能夠准確喚醒的?

讀取合適 Message 的 MessageQueue#next() 會因為 Message 尚無或執行條件尚未滿足進行兩種等的等待:

無限等待

尚無 Message(隊列中沒有 Message 或建立了同步屏障但尚無非同步 Message)的時候,調用 Natvie 側的 pollOnce() 會傳入參數  -1 。

Linux 執行 epoll_wait() 將進入無限等待,其等待合適的 Message 插入後調用 Native 側的 wake() 向喚醒 fd 寫入事件觸發喚醒 MessageQueue 讀取的下一次循環

有限等待

有限等待的場合將下一個 Message  剩餘時長作為參數 交給 epoll_wait(),epoll 將等待一段時間之後 自動返回 ,接著回到 MessageQueue 讀取的下一次循環

8. Message 如何獲取?為什麼這么設計?

享元設計模式:通過 Message 的靜態方法 obatin() 獲取,因為該方法不是無腦地 new,而是 從單鏈表池子里獲取實例 ,並在 recycle() 後將其放回池子

好處在於復用 Message 實例,滿足頻繁使用 Message 的場景,更加高效

當然,緩存池存在上限  50 ,因為沒必要無限制地緩存,這本身也是一種浪費

需要留意緩存池是靜態的,也就是整個進程共用一個緩存池

9. MessageQueue 如何管理 Message?

MessageQueue 通過單鏈表管理 Message,不同於進程共用的 Message Pool,其是線程獨有的

通過 Message 的執行時刻 when 對 Message 進行排隊和出隊

MessageQueue 除了管理 Message,還要管理空閑 Handler 和 同步屏障

10. 理解 Message 和 MessageQueue 的異同?

相同點:都是通過 單鏈表來管理  Message 實例;

Message 通過  obtain() 和 recycle()  向單鏈表獲取插入節點

MessageQueue 通過  enqueueMessage() 和 next()  向單鏈表獲取和插入節點

區別:

Message 單鏈表是 靜態的,供進程使用的緩存池

MessageQueue 單鏈表 非靜態,只供 Looper 線程使用

11. Message 的執行時刻如何管理?

發送的 Message 都是按照執行時刻 when 屬性的先後管理在 MessageQueue 里

延時 Message 的 when 等於調用的當前時刻和 delay 之和

非延時 Message 的 when 等於當前時刻(delay 為 0)

插隊 Message 的 when 固定為 0,便於插入隊列的 head

之後 MessageQueue 會根據 讀取的時刻和 when 進行比較

將 when 已抵達的出隊,

尚未抵達的計算出 當前時刻和目標 when 的插值 ,交由 Native 等待對應的時長,時間到了自動喚醒繼續進行 Message 的讀取

事實上,無論上述哪種 Message 都不能保證在其對應的 when 時刻執行,往往都會延遲一些!因為必須等當前執行的 Message 處理完了才有機會讀取隊列的下一個 Message。

比如發送了非延時 Message,when 即為發送的時刻,可它們不會立即執行。都要等主線程現有的任務(Message)走完才能有機會出隊,而當這些任務執行完 when 的時刻已經過了。假使隊列的前面還有其他 Message 的話,延遲會更加明顯!

彩蛋:. onCreate() 里向 Handler 發送大量 Message 會導致主線程卡頓嗎?

不會,發送的大量 Message 並非立即執行,只是先放到隊列當中而已。

onCreate() 以及之後同步調用的 onStart() 和 onResume() 處理,本質上也是 Message。等這個 Message 執行完之後,才會進行讀取 Message 的下一次循環,這時候才能回調 onCreate 里發送的 Message。

需要說明的是,如果發送的是 FrontOfQueue 將 Message 插入隊首也不會立即先執行,因為 onStart 和 onResume 是 onCreate 之後同步調用的,本質上是同一個 Message 的作業周期

12. Handler、Mesage 和 Runnable 的關系如何理解?

作為使用 Handler 機制的入口, Handler 是發送 Message 或 Runnable 的起點

發送的  Runnable 本質上也是 Message ,只不過作為 callback 屬性被持有

Handler 作為 target 屬性被持有在 Mesage 中 ,在 Message 執行條件滿足的時候供 Looper 回調

事實上,Handler 只是供 App 使用 Handler 機制的 API,實質來說,Message 是更為重要的載體。

13. IdleHandler 空閑 Message 了解過嗎?有什麼用?

適用於期望 空閑時候執行,但不影響主線程操作 的任務

系統應用:

Activity destroy 回調就放在了 IdleHandler 中

ActivityThread 中 GCHandler 使用了 IdleHandler,在空閑的時候執行  GC  操作

App 應用:

發送一個返回 true 的 IdleHandler,在裡面讓某個 View 不停閃爍,這樣當用戶發呆時就可以誘導用戶點擊這個 View

將某部分初始化放在 IdleHandler 里不影響 Activity 的啟動

14. 非同步 Message 或同步屏障了解過嗎?怎麼用?什麼原理?

非同步 Message:設置了 isAsync 屬性的 Message 實例

可以用非同步 Handler 發送

也可以調用 Message#setAsynchronous() 直接設置為非同步 Message

同步屏障:在 MessageQueue 的 某個位置放一個 target 屬性為 null 的 Message ,確保此後的非非同步 Message 無法執行,只能執行非同步 Message

原理:當 MessageQueue 輪循 Message 時候 發現建立了同步屏障的時候,會去跳過其他 Message,讀取下個 async 的 Message 並執行,屏障移除之前同步 Message 都會被阻塞

應用:比如 屏幕刷新 Choreographer 就使用到了同步屏障 ,確保屏幕刷新事件不會因為隊列負荷影響屏幕及時刷新。

注意: 同步屏障的添加或移除 API 並未對外公開,App 需要使用的話需要依賴反射機制

15. Looper 和 MessageQueue、Message 及 Handler 的關系?

Message 是承載任務的載體,在 Handler 機制中貫穿始終

Handler 則是對外公開的 API,負責發送 Message 和處理任務的回調,是  Message 的生產者

MessagQueue 負責管理待處理 Message 的入隊和出隊,是  Message 的容器

Looper 負責輪循 MessageQueue,保持線程持續運行任務,是  Message 的消費者

彩蛋:如何保證 MessageQueue 並發訪問安全?

任何線程都可以通過 Handler 生產 Message 並放入 MessageQueue 中,可 Queue 所屬的 Looper 在持續地讀取並嘗試消費 Message。如何保證兩者不產生死鎖?

Looper 在消費 Message 之前要先拿到 MessageQueue  的鎖, 只不過沒有 Message 或 Message 尚未滿足條件的進行等待前會事先釋放鎖 ,具體在於 nativePollOnce() 的調用在 synchronized 方法塊的外側。

Message 入隊前也需先拿到 MessageQueue  的鎖,而這時 Looper 線程正在等待且不持有鎖,可以確保 Message 的成功入隊。入隊後執行喚醒後釋放鎖,Native 收到 event 寫入後恢復 MessagQueue 的讀取並可以拿到鎖,成功出隊。

這樣一種在沒有 Message 可以消費時執行等待同時不佔著鎖的機制,避免了生產和消費的死鎖。

16. Native 側的 NativeMessageQueue 和 Looper 的作用是?

NativeMessageQueue 負責連接 Java 側的 MessageQueue,進行後續的 wait 和 wake,後續將創建 wake 的FD,並通過 epoll 機制等待或喚醒。 但並不參與管理 Java 的 Message

Native 側也需要 Looper 機制,等待和喚醒的需求是同樣的,所以將這部分實現都封裝到了 JNI 的NativeMessageQueue  和  Native 的 Looper 中, 供 Java 和 Native 一起使用

17. Native 側如何使用 Looper?

Looper Native 部分承擔了 Java 側 Looper 的等待和喚醒,除此之外其還提供了 Message、MessageHandler 或 WeakMessageHandler、LooperCallback 或 SimpleLooperCallback 等 API

這些部分可供 Looper 被 Native 側直接調用,比如 InputFlinger 廣泛使用了 Looper

主要方法是調用 Looper 構造函數或 prepare 創建 Looper,然後通過 poll 開始輪詢,接著 sendMessage 或 addEventFd,等待 Looper 的喚醒。 使用過程和 Java 的調用思路類似

18. Handler 為什麼可能導致內存泄露?如何避免?

持有 Activity 實例的內名內部類或內部類的 生命周期 應當和 Activity 保持一致,否則產生內存泄露的風險。

如果 Handler 使用不當,將造成不一致,表現為:匿名內部類或內部類寫法的 Handler、Handler$Callback、Runnable,或者Activity 結束時仍有活躍的 Thread 線程或 Looper 子線程

具體在於:非同步任務仍然活躍或通過發送的 Message 尚未處理完畢,將使得內部類實例的 生命周期被錯誤地延長 。造成本該回收的 Activity 實例 被別的 Thread 或 Main Looper 占據而無法及時回收 (活躍的 Thread 或 靜態屬性 sMainLooper 是 GC Root 對象)

建議的做法:

無論是 Handler、Handler$Callback 還是 Runnable,盡量採用 靜態內部類 + 弱引用 的寫法,確保盡管發生不當引用的時候也可以因為弱引用能清楚持有關系

另外在 Activity 銷毀的時候及時地 終止 Thread、停止子線程的 Looper 或清空 Message ,確保徹底切斷 Activity 經由 Message 抵達 GC Root 的引用源頭(Message 清空後會其與 Handler 的引用關系,Thread 的終止將結束其 GC Root 的源頭)

注意:靜態的 sThreadLocal 實例不持有存放 Looper 實例的 ThreadLocalMap,而是由 Thread 持有。從這個角度上來講,Looper 會被活躍的 GC Root Thread 持有,進而也可能導致內存泄露。

彩蛋:網傳的 Handler$Callback 方案能否解決內存泄露?

不能。

Callback 採用內部類或匿名內部類寫法的話,默認持有 Activity 的引用,而 Callback 被 Handler 持有。這最終將導致 Message -> Handler -> Callback -> Activity 的鏈條仍然存在。

19. Handler 在系統當中的應用

特別廣泛,比如:

Activity 生命周期的管理

屏幕刷新

HandlerThread、IntentService

AsyncTask 等。

主要利用 Handler 的切換線程、主線程非同步 Message 的重要特性。注意:Binder 線程非主線程,但很多操作比如生命周期的管理都要回到主線程,所以很多 Binder 調用過來後都要通過 Handler 切換回主線程執行後續任務,比如 ActviityThread$H 就是 extends Handler。

20. Android 為什麼不允許並發訪問 UI?

Android 中 UI 非線程安全,並發訪問的話會造成數據和顯示錯亂。

但此限制的檢查始於ViewRootImpl#checkThread(),其會在刷新等多個訪問 UI 的時機被調用,去檢查當前線程,非主線程的話拋出異常。

而 ViewRootImpl 的創建在 onResume() 之後,也就是說如果在 onResume() 執行前啟動線程訪問 UI 的話是不會報錯的,這點需要留意!

彩蛋:onCreate() 里子線程更新 UI 有問題嗎?為什麼?

不會。

因為異常的檢測處理在 ViewRootImpl 中,該實例的創建和檢測在 onResume() 之後進行。

5. Handler消息機制(一):Linux的epoll機制

在linux 沒有實現epoll事件驅動機制之前,我們一般選擇用select或者poll等IO多路復用的方法來實現並發服務程序。在linux新的內核中,有了一種替換它的機制,就是epoll。

相比select模型, poll使用鏈表保存文件描述符,因此沒有了監視文件數量的限制 ,但其他三個缺點依然存在。

假設我們的伺服器需要支持100萬的並發連接,則在__FD_SETSIZE 為1024的情況下,則我們至少需要開辟1k個進程才能實現100萬的並發連接。除了進程間上下文切換的時間消耗外,從內核/用戶空間大量的無腦內存拷貝、數組輪詢等,是系統難以承受的。因此,基於select模型的伺服器程序,要達到10萬級別的並發訪問,是一個很難完成的任務。

由於epoll的實現機制與select/poll機制完全不同,上面所說的 select的缺點在epoll上不復存在。

設想一下如下場景:有100萬個客戶端同時與一個伺服器進程保持著TCP連接。而每一時刻,通常只有幾百上千個TCP連接是活躍的(事實上大部分場景都是這種情況)。如何實現這樣的高並發?

在select/poll時代,伺服器進程每次都把這100萬個連接告訴操作系統(從用戶態復制句柄數據結構到內核態),讓操作系統內核去查詢這些套接字上是否有事件發生,輪詢完後,再將句柄數據復制到用戶態,讓伺服器應用程序輪詢處理已發生的網路事件,這一過程資源消耗較大,因此,select/poll一般只能處理幾千的並發連接。

epoll的設計和實現與select完全不同。epoll通過在Linux內核中申請一個簡易的文件系統(文件系統一般用什麼數據結構實現?B+樹)。把原先的select/poll調用分成了3個部分:

1)調用epoll_create()建立一個epoll對象(在epoll文件系統中為這個句柄對象分配資源)

2)調用epoll_ctl向epoll對象中添加這100萬個連接的套接字

3)調用epoll_wait收集發生的事件的連接

如此一來,要實現上面說是的場景,只需要在進程啟動時建立一個epoll對象,然後在需要的時候向這個epoll對象中添加或者刪除連接。同時,epoll_wait的效率也非常高,因為調用epoll_wait時,並沒有一股腦的向操作系統復制這100萬個連接的句柄數據,內核也不需要去遍歷全部的連接。

當某一進程調用epoll_create方法時,Linux內核會創建一個eventpoll結構體,這個結構體中有兩個成員與epoll的使用方式密切相關。eventpoll結構體如下所示:

每一個epoll對象都有一個獨立的eventpoll結構體,用於存放通過epoll_ctl方法向epoll對象中添加進來的事件。這些事件都會掛載在紅黑樹中,如此,重復添加的事件就可以通過紅黑樹而高效的識別出來(紅黑樹的插入時間效率是lgn,其中n為樹的高度)。

而所有 添加到epoll中的事件都會與設備(網卡)驅動程序建立回調關系,也就是說,當相應的事件發生時會調用這個回調方法 。這個回調方法在內核中叫ep_poll_callback,它會將發生的事件添加到rdlist雙鏈表中。

在epoll中,對於每一個事件,都會建立一個epitem結構體,如下所示:

當調用epoll_wait檢查是否有事件發生時,只需要檢查eventpoll對象中的rdlist雙鏈表中是否有epitem元素即可。如果rdlist不為空,則把發生的事件復制到用戶態,同時將事件數量返回給用戶。

epoll結構示意圖

通過紅黑樹和雙鏈表數據結構,並結合回調機制,造就了epoll的高效。

events可以是以下幾個宏的集合:
EPOLLIN:觸發該事件,表示對應的文件描述符上有可讀數據。(包括對端SOCKET正常關閉);
EPOLLOUT:觸發該事件,表示對應的文件描述符上可以寫數據;
EPOLLPRI:表示對應的文件描述符有緊急的數據可讀(這里應該表示有帶外數據到來);
EPOLLERR:表示對應的文件描述符發生錯誤;
EPOLLHUP: 表示對應的文件描述符被掛斷;
EPOLLET:將EPOLL設為邊緣觸發(EdgeTriggered)模式,這是相對於水平觸發(Level Triggered)來說的。
EPOLLONESHOT: 只監聽一次事件,當監聽完這次事件之後,如果還需要繼續監聽這個socket的話,需要再次把這個socket加入到EPOLL隊列里。
示例:

ET(EdgeTriggered) :高速工作模式,只支持no_block(非阻塞模式)。在此模式下,當描述符從未就緒變為就緒時,內核通過epoll告知。然後它會假設用戶知道文件描述符已經就緒,並且不會再為那個文件描述符發送更多的就緒通知,直到某些操作導致那個文件描述符不再為就緒狀態了。(觸發模式只在數據就緒時通知一次,若數據沒有讀完,下一次不會通知,直到有新的就緒數據)

LT(LevelTriggered) :預設工作方式,支持blocksocket和no_blocksocket。在LT模式下內核會告知一個文件描述符是否就緒了,然後可以對這個就緒的fd進行IO操作。如果不作任何操作,內核還是會繼續通知!若數據沒有讀完,內核也會繼續通知,直至設備數據為空為止!

1.我們已經把一個用來從管道中讀取數據的文件句柄(RFD)添加到epoll描述符
2. 這個時候從管道的另一端被寫入了2KB的數據
3. 調用epoll_wait(2),並且它會返回RFD,說明它已經准備好讀取操作
4. 然後我們讀取了1KB的數據
5. 調用epoll_wait(2)……

ET工作模式:
如果我們在第1步將RFD添加到epoll描述符的時候使用了EPOLLET標志,在第2步執行了一個寫操作,第三步epoll_wait會返回同時通知的事件會銷毀。因為第4步的讀取操作沒有讀空文件輸入緩沖區內的數據,因此我們在第5步調用epoll_wait(2)完成後,是否掛起是不確定的。epoll工作在ET模式的時候,必須使用非阻塞套介面,以避免由於一個文件句柄的阻塞讀/阻塞寫操作把處理多個文件描述符的任務餓死。

只有當read(2)或者write(2)返回EAGAIN時(認為讀完)才需要掛起,等待。但這並不是說每次read()時都需要循環讀,直到讀到產生一個EAGAIN才認為此次事件處理完成,當read()返回的讀到的數據長度小於請求的數據長度時(即小於sizeof(buf)),就可以確定此時緩沖中已沒有數據了,也就可以認為此事讀事件已處理完成。

LT工作模式:
LT方式調用epoll介面的時候,它就相當於一個速度比較快的poll(2),並且無論後面的數據是否被使用,因此他們具有同樣的職能。

當調用 epoll_wait檢查是否有發生事件的連接時,只是檢查 eventpoll對象中的 rdllist雙向鏈表是否有 epitem元素而已,如果 rdllist鏈表不為空,則把這里的事件復制到用戶態內存中,同時將事件數量返回給用戶。因此,epoll_wait的效率非常高。epoll_ctl在向 epoll對象中添加、修改、刪除事件時,從 rbr紅黑樹中查找事件也非常快,也就是說,epoll是非常高效的,它可以輕易地處理百萬級別的並發連接。

1.減少用戶態和內核態之間的文件句柄拷貝;

2.減少對可讀可寫文件句柄的遍歷。

https://cloud.tencent.com/developer/information/linux%20epoll%E6%9C%BA%E5%88%B6
https://blog.csdn.net/u010657219/article/details/44061629
https://jiahao..com/s?id=1609322251459722004&wfr=spider&for=pc

6. 在 linux中如何結束由一個父進程產生的所有子進程

父進程未結束,子進程先結束,會產生僵屍進程。
子進程在調用exit命令結束自己的生命的時候,其實它並沒有真正的被銷毀,而是留下一個稱為僵屍進程(Zombie)的數據結構(系統調用 exit,它的作用是使進程退出,但也僅僅限於將一個正常的進程變成一個僵屍進程,並不能將其完全銷毀)。
即使是root身份kill -9也不能殺死僵屍進程。補救辦法是殺死僵屍進程的父進程(僵屍進程的父進程必然存在),僵屍進程成為"孤兒進程",過繼給1號進程init,init始終會負責清理僵屍進程。
僵屍進程的避免:
(1) 父進程通過wait和waitpid等函數等待子進程結束,這會導致父進程掛起。
(2) 如果父進程很忙,那麼可以用signal函數為SIGCHLD安裝handler,因為子進程結束後, 父
進程會收到該信號,可以在handler中調用wait回收。
(3) 如果父進程不關心子進程什麼時候結束,那麼可以用signal(SIGCHLD, SIG_IGN) 通知內
核,自己對子進程的結束不感興趣,那麼子進程結束後,內核會回收, 並不再給父進程發送
信號。
(4) 還有一些技巧,就是fork兩次,父進程fork一個子進程,然後繼續工作,子進程fork一 個孫進
程後退出,那麼孫進程被init接管,孫進程結束後, init會回收。不過子進程的回收 還要自己
做。
建議你使用第三種方法,父進程直接忽略子進程的結束,留給內核作回收處理。這樣就不
會產生僵屍進程。

7. linux源代碼中有很多變數以handler結尾,代表什麼意思和window裡面的句柄有什麼區別

這里發估計沒幾個人都回答你,因為涉及到linux源代碼。這個handler和句柄完全不是一回事,句柄的英文是handle:)
handler是處理程序的意思,比如中斷有相應中斷的內核中斷處理程序 - interrupt handler;信號有信號處理程序 signal handler

我只能盡我最大力量回答你,我沒看過內核代碼,不過對內核還懂些。希望即使沒幫到你 也啟發了你

8. 如何在linux環境下實現進程之間的通信

linux環境下實現進程之間的通信主要有以下幾種方式:

# 管道( pipe ):管道是一種半雙工的通信方式,數據只能單向流動,而且只能在具有親緣關系的進程間使用。進程的親緣關系通常是指父子進程關系。
# 有名管道 (named pipe) : 有名管道也是半雙工的通信方式,但是它允許無親緣關系進程間的通信。
# 信號量( semophore ) : 信號量是一個計數器,可以用來控制多個進程對共享資源的訪問。它常作為一種鎖機制,防止某進程正在訪問共享資源時,其他進程也訪問該資源。因此,主要作為進程間以及同一進程內不同線程之間的同步手段。
# 消息隊列( message queue ) : 消息隊列是由消息的鏈表,存放在內核中並由消息隊列標識符標識。消息隊列克服了信號傳遞信息少、管道只能承載無格式位元組流以及緩沖區大小受限等缺點。
# 信號 ( sinal ) : 信號是一種比較復雜的通信方式,用於通知接收進程某個事件已經發生。
#共享內存( shared memory):共享內存就是映射一段能被其他進程所訪問的內存,這段共享內存由一個進程創建,但多個進程都可以訪問。共享內存是最快的IPC方式,它是針對其他進程間通信方式運行效率低而專門設計的。它往往與其他通信機制,如信號量,配合使用,來實現進程間的同步和通信。
# 套接字( socket ) : 套解口也是一種進程間通信機制,與其他通信機制不同的是,它可用於不同及其間的進程通信。

管道的主要局限性正體現在它的特點上:
只支持單向數據流;
只能用於具有親緣關系的進程之間;
沒有名字;
管道的緩沖區是有限的(管道制存在於內存中,在管道創建時,為緩沖區分配一個頁面大小);
管道所傳送的是無格式位元組流,這就要求管道的讀出方和寫入方必須事先約定好數據的格式,比如多少位元組算作一個消息(或命令、或記錄)等等;

9. Linux下通過哪個命令怎麼查看中斷

與Linux設備驅動中中斷處理相關的首先是申請與釋放IRQ的API request_irq()和free_irq()。

C++是一種面向對象的計算機程序設計語言,由美國AT&T貝爾實驗室的本賈尼·斯特勞斯特盧普博士在20世紀80年代初期發明並實現,最初它被稱作「C with Classes」(包含類的C語言)。

它是一種靜態數據類型檢查的、支持多重編程範式的通用程序設計語言,支持過程化程序設計、數據抽象、面向對象程序設計、泛型程序設計等多種程序設計風格。

在C基礎上,一九八三年又由貝爾實驗室的Bjarne Strou-strup推出了C++,C++進一步擴充和完善了C語言,成為一種面向 對象的程序設計語言。

C++目前流行的編譯器最新版本是Borland C++ 4.5,Symantec C++ 6.1,和Microsoft Visual C++ 2012。

10. Linux內核中斷之中斷申請介面

本文基於 RockPI 4A 單板Linux4.4內核介紹中斷申請的常用介面函數。

1、文件

2、定義

說明:

1)、 irq :要申請的中斷號,可通過 platform_get_irq() 獲取,見「Linux內核中斷之獲取中斷號」。

2)、 handler :中斷處理函數,發生中斷時,先處理中斷處理函數,然後返回 IRQ_WAKE_THREAD 喚醒中斷處理線程。中斷處理函數盡可能簡單。

中斷處理函數定義: typedef irqreturn_t (*irq_handler_t)(int, void *);

中斷返回值如下:

3)、 thread_fn :中斷處理線程,該參數可為NULL。類似於中斷處理函數的下半部分。

4)、 irqflags :中斷類型標志。

定義文件: include/linux/interrupt.h ,內容如下:

5)、 devname :中斷名稱,可使用 cat /proc/interrupts 命令查看。

6)、 dev_id :設備ID,該值唯一。

在使用共享中斷時(即設置 IRQF_SHARED ),必須傳入 dev_id ,在中斷處理和釋放函數中都會使用該參數。

註:

1、 request_threaded_irq() 函數可替代 request_irq 加 tasklet 或 workqueue 的方式。

2、對應的中斷釋放函數為: void free_irq(unsigned int, void *) ,需要和中斷申請函數成對出現。

1、文件

2、定義

說明:

1)、 __must_check :指調用函數一定要處理函數的返回值,否則編譯器會給出警告。

2)、 request_irq() 函數本質上是中斷處理線程 thread_fn 為空的 request_threaded_irq() 函數。

對應的中斷釋放函數為: void free_irq(unsigned int, void *) ,需要和中斷申請函數成對出現。

1、文件

2、定義

說明

devm_request_threaded_irq() 本質上還是使用 request_threaded_irq() 函數實現中斷申請。

兩者區別:

1)多了一個 dev 參數;

2)在設備驅動卸載時,中斷會自動釋放;

3)如果想單獨釋放中斷,可使用 void devm_free_irq(struct device *dev, unsigned int irq, void *dev_id) 函數。

1、文件

2、定義

devm_request_irq() 函數本質上是中斷處理線程 thread_fn 為空的 devm_request_threaded_irq() 函數。

1、獲取中斷號

2、申請中斷

3、中斷處理函數

4、中斷處理線程

5、查看中斷

閱讀全文

與linuxhandler相關的資料

熱點內容
linuxoracle命令行登錄 瀏覽:224
android深度休眠 瀏覽:169
php微信開發例子 瀏覽:843
醫得app登錄密碼是什麼 瀏覽:140
spring開發伺服器地址 瀏覽:411
伺服器上如何查看伺服器的埠 瀏覽:678
單片機伺服器編譯 瀏覽:770
單口usb列印機伺服器是什麼 瀏覽:859
戰地五開伺服器要什麼條件 瀏覽:956
在word中壓縮圖片大小 瀏覽:255
javatomcat圖片 瀏覽:419
程序員生產智能創意 瀏覽:67
匯和銀行app怎麼登錄 瀏覽:383
騰訊伺服器如何上傳源碼 瀏覽:747
單片機的原理概述 瀏覽:512
火控pdf 瀏覽:269
如何復制雲伺服器centos環境 瀏覽:986
債權pdf 瀏覽:305
紅色番字的app怎麼下載 瀏覽:876
雲伺服器流程教課 瀏覽:704