導航:首頁 > 數據處理 > 內核是如何知道哪個fd有數據

內核是如何知道哪個fd有數據

發布時間:2022-03-06 03:28:47

① 如何快速找到系統調用的內核源碼

$ grep SYSCALL_DEFINE -R | grep getpid

② 如何查看Linux內存中的程序所有堆的地址

linux 下面查看內存有多種渠道,比如通過命令 ps ,top,free 等,比如通過/proc系統,一般需要比較詳細和精確地知道整機內存/某個進程內存的使用情況,最好通過/proc 系統,下面介紹/proc系統下內存相關的幾個文件

單個進程的內存查看 cat /proc/[pid] 下面有幾個文件: maps , smaps, status

maps 文件可以查看某個進程的代碼段、棧區、堆區、動態庫、內核區對應的虛擬地址,如果你還不了解linux進程的內存空間,可以參考這里。

下圖是maps文件內存示例

Develop>cat /proc/self/maps
00400000-0040b000 r-xp 00000000 fd:00 48 /mnt/cf/orig/root/bin/cat
0060a000-0060b000 r--p 0000a000 fd:00 48 /mnt/cf/orig/root/bin/cat
0060b000-0060c000 rw-p 0000b000 fd:00 48 /mnt/cf/orig/root/bin/cat 代碼段
0060c000-0062d000 rw-p 00000000 00:00 0 [heap] 堆區
7f1fff43b000-7f1fff5d4000 r-xp 00000000 fd:00 861 /mnt/cf/orig/root/lib64/libc-2.15.so
7f1fff5d4000-7f1fff7d3000 ---p 00199000 fd:00 861 /mnt/cf/orig/root/lib64/libc-2.15.so
7f1fff7d3000-7f1fff7d7000 r--p 00198000 fd:00 861 /mnt/cf/orig/root/lib64/libc-2.15.so
7f1fff7d7000-7f1fff7d9000 rw-p 0019c000 fd:00 861 /mnt/cf/orig/root/lib64/libc-2.15.so
7f1fff7d9000-7f1fff7dd000 rw-p 00000000 00:00 0
7f1fff7dd000-7f1fff7fe000 r-xp 00000000 fd:00 2554 /mnt/cf/orig/root/lib64/ld-2.15.so
7f1fff9f9000-7f1fff9fd000 rw-p 00000000 00:00 0
7f1fff9fd000-7f1fff9fe000 r--p 00020000 fd:00 2554 /mnt/cf/orig/root/lib64/ld-2.15.so
7f1fff9fe000-7f1fff9ff000 rw-p 00021000 fd:00 2554 /mnt/cf/orig/root/lib64/ld-2.15.so
7f1fff9ff000-7f1fffa00000 rw-p 00000000 00:00 0
7fff443de000-7fff443ff000 rw-p 00000000 00:00 0 [stack] 用戶態棧區
7fff443ff000-7fff44400000 r-xp 00000000 00:00 0 [vdso]
ffffffffff600000-ffffffffff601000 r-xp 00000000 00:00 0 [vsyscall] 內核區

有時候可以通過不斷查看某個進程的maps文件,通過查看其虛擬內存(堆區)是否不停增長來簡單判斷進程是否發生了內存溢出。

maps文件只能顯示簡單的分區,smap文件可以顯示每個分區的更詳細的內存佔用數據

下圖是smaps文件內存示例, 實際顯示內容會將每一個區都顯示出來,下面我只拷貝了代碼段和堆區,

每一個區顯示的內容項目是一樣的,smaps文件各項含義可以參考這里

Develop>cat /proc/self/smaps
00400000-0040b000 r-xp 00000000 fd:00 48 /mnt/cf/orig/root/bin/cat
Size: 44 kB 虛擬內存大小
Rss: 28 kB 實際使用物理內存大小
Pss: 28 kB
Shared_Clean: 0 kB 頁面被改,則是dirty,否則是clean,頁面引用計數>1,是shared,否則是private
Shared_Dirty: 0 kB
Private_Clean: 28 kB
Private_Dirty: 0 kB
Referenced: 28 kB
Anonymous: 0 kB
AnonHugePages: 0 kB
Swap: 0 kB 處於交換區的頁面大小
KernelPageSize: 4 kB 操作系統一個頁面大小
MMUPageSize: 4 kB 體系結構MMU一個頁面大小
Locked: 0 kB

0060c000-0062d000 rw-p 00000000 00:00 0 [heap]
Size: 132 kB
Rss: 8 kB
Pss: 8 kB
Shared_Clean: 0 kB
Shared_Dirty: 0 kB
Private_Clean: 0 kB
Private_Dirty: 8 kB
Referenced: 8 kB
Anonymous: 8 kB
AnonHugePages: 0 kB
Swap: 0 kB
KernelPageSize: 4 kB
MMUPageSize: 4 kB
Locked: 0 kB

下圖是status文件內存示例, 加粗部分是內存相關的統計,

Develop>cat /proc/24475/status
Name: netio 可執行程序的名字
State: R (running) 任務狀態,運行/睡眠/僵死
Tgid: 24475 線程組號
Pid: 24475 進程id
PPid: 19635 父進程id
TracerPid: 0
Uid: 0 0 0 0
Gid: 0 0 0 0
FDSize: 256 該進程最大文件描述符個數
Groups: 0
VmPeak: 6330708 kB 內存使用峰值
VmSize: 268876 kB 進程虛擬地址空間大小
VmLck: 0 kB 進程鎖住的物理內存大小,鎖住的物理內存無法交換到硬碟

VmHWM: 16656 kB
VmRSS: 11420 kB 進程正在使用的物理內存大小
VmData: 230844 kB 進程數據段大小
VmStk: 136 kB 進程用戶態棧大小
VmExe: 760 kB 進程代碼段大小
VmLib: 7772 kB 進程使用的庫映射到虛擬內存空間的大小
VmPTE: 120 kB 進程頁表大小
VmSwap: 0 kB
Threads: 5
SigQ: 0/63346
SigPnd: 0000000000000000
ShdPnd: 0000000000000000
SigBlk: 0000000000000000
SigIgn: 0000000001000000
SigCgt: 0000000180000000
CapInh: 0000000000000000
CapPrm: ffffffffffffffff
CapEff: ffffffffffffffff
CapBnd: ffffffffffffffff
Cpus_allowed: 01
Cpus_allowed_list: 0
Mems_allowed: 01
Mems_allowed_list: 0
voluntary_ctxt_switches: 201
nonvoluntary_ctxt_switches: 909

可以看到,linux下內存佔用是一個比較復雜的概念,不能

簡單通過一個單一指標就判斷某個程序「內存消耗」大小,原因有下面2點:

進程所申請的內存不一定真正會被用到(malloc或mmap的實現)
真正用到了的內存也不一定是只有該進程自己在用 (比如動態共享庫)

關於內存的使用分析及本文幾個命令的說明也可以參考這里

下面是查看整機內存使用情況的文件 /proc/meminfo

Develop>cat /proc/meminfo
MemTotal: 8112280 kB 所有可用RAM大小 (即物理內存減去一些預留位和內核的二進制代碼大小)
MemFree: 4188636 kB LowFree與HighFree的總和,被系統留著未使用的內存
Buffers: 34728 kB 用來給文件做緩沖大小
Cached: 289740 kB 被高速緩沖存儲器(cache memory)用的內存的大小
(等於 diskcache minus SwapCache )
SwapCached: 0 kB 被高速緩沖存儲器(cache memory)用的交換空間的大小
已經被交換出來的內存,但仍然被存放在swapfile中。
用來在需要的時候很快的被替換而不需要再次打開I/O埠
Active: 435240 kB 在活躍使用中的緩沖或高速緩沖存儲器頁面文件的大小,
除非非常必要否則不會被移作他用
Inactive: 231512 kB 在不經常使用中的緩沖或高速緩沖存儲器頁面文件的大小,可能被用於其他途徑.
Active(anon): 361252 kB
Inactive(anon): 120688 kB
Active(file): 73988 kB
Inactive(file): 110824 kB
Unevictable: 0 kB
Mlocked: 0 kB
SwapTotal: 0 kB 交換空間的總大小
SwapFree: 0 kB 未被使用交換空間的大小
Dirty: 0 kB 等待被寫回到磁碟的內存大小
Writeback: 0 kB 正在被寫回到磁碟的內存大小
AnonPages: 348408 kB 未映射頁的內存大小
Mapped: 33600 kB 已經被設備和文件等映射的大小
Shmem: 133536 kB
Slab: 55984 kB 內核數據結構緩存的大小,可以減少申請和釋放內存帶來的消耗
SReclaimable: 25028 kB 可收回Slab的大小
SUnreclaim: 30956 kB 不可收回Slab的大小(SUnreclaim+SReclaimable=Slab)
KernelStack: 1896 kB 內核棧區大小
PageTables: 8156 kB 管理內存分頁頁面的索引表的大小
NFS_Unstable: 0 kB 不穩定頁表的大小
Bounce: 0 kB
WritebackTmp: 0 kB
CommitLimit: 2483276 kB
Committed_AS: 1804104 kB
VmallocTotal: 34359738367 kB 可以vmalloc虛擬內存大小
VmallocUsed: 565680 kB 已經被使用的虛擬內存大小
VmallocChunk: 34359162876 kB
HardwareCorrupted: 0 kB
HugePages_Total: 1536 大頁面數目
HugePages_Free: 0 空閑大頁面數目
HugePages_Rsvd: 0
HugePages_Surp: 0
Hugepagesize: 2048 kB 大頁面一頁大小
DirectMap4k: 10240 kB
DirectMap2M: 8302592 kB

③ 怎樣在內核中訪問某一個進程的內存並作修改

比如,標准 VGA 16 色模式的實模式地址是 A000:0000,而線性地址則是 A00000。設定顯存大小為 0x10000,則可以如下操作。
mem_fd = open ("/dev/mem", O_RDWR);
vga_mem = mmap (0, 0x10000, PROT_READ PROT_WRITE, MAP_SHARED, mem_fd, 0xA00000);
close (mem_fd);
然後直接對 vga_mem 進行訪問,就可以了。當然,如果是操作 VGA 顯卡,還要獲得 I/O 埠的訪問許可權,以便進行直接的 I/O 操作,用來設置模式、調色板、選擇位面等等。
在工控領域中還有一種常用的方法,用來在內核和應用程序之間高效傳遞數據:
1. 假定系統有 64M 物理內存,則可以通過 lilo 通知內核只使用 63M,而 保留 1M 物理內存作為數據交換使用(使用 mem=63M 標記)。 2. 然後打開 /dev/mem 設備,並將 63M 開始的 1M 地址空間映射到進程 的地址空間。(出處:清風軟體下載學院)

④ 如何獲取epoll監控的事件類型

epoll的介面非常簡單,一共就三個函數:
1. int epoll_create(int size);
創建一個epoll的句柄,size用來告訴內核這個監聽的數目一共有多大。這個參數不同於select()中的第一個參數,給出最大監聽的fd+1的值。需要注意的是,當創建好epoll句柄後,它就是會佔用一個fd值,在linux下如果查看/proc/進程id/fd/,是能夠看到這個fd的,所以在使用完epoll後,必須調用close()關閉,否則可能導致fd被耗盡。
2. int epoll_ctl(int epfd, int op, int fd, struct epoll_event* event);
epoll的事件注冊函數,即注冊要監聽的事件類型。
第一個參數是epoll_create()的返回值,
第二個參數表示動作,用三個宏來表示:
EPOLL_CTL_ADD:注冊新的fd到epfd中;
EPOLL_CTL_MOD:修改已經注冊的fd的監聽事件;
EPOLL_CTL_DEL:從epfd中刪除一個fd;
第三個參數是需要監聽的fd,
第四個參數是告訴內核需要監聽什麼事,struct epoll_event結構如下:

struct epoll_event {
__uint32_t events;
epoll_data_t data;
};

typedef union epoll_data {
void *ptr;
int fd;
__uint32_t u32;
__uint64_t u64;
} epoll_data_t;

events可以是以下幾個宏的集合:
EPOLLIN : 表示對應的文件描述符可以讀(包括對端SOCKET正常關閉);
EPOLLOUT: 表示對應的文件描述符可以寫;
EPOLLPRI: 表示對應的文件描述符有緊急的數據可讀(這里應該表示有帶外數據到來);
EPOLLERR: 表示對應的文件描述符發生錯誤;
EPOLLHUP: 表示對應的文件描述符被掛斷;
EPOLLET: 將 EPOLL設為邊緣觸發(Edge Triggered)模式(默認為水平觸發),這是相對於水平觸發(Level Triggered)來說的。
EPOLLONESHOT: 只監聽一次事件,當監聽完這次事件之後,如果還需要繼續監聽這個socket的話,需要再次把這個socket加入到EPOLL隊列里
3. int epoll_wait(int epfd, struct epoll_event *events, int maxevents, int timeout);
等待事件的產生。參數events 用來從內核得到事件的集合,maxevents 告之內核這個events 有多大,這個maxevents 的值不能大於創建epoll_create()時的size,參數timeout是超時時間(毫秒,0會立即返回,-1將不確定,也有說法說是永久阻塞)。該函數返回需要處理的事件數目,如返回0表示已超時。
4. EPOLL事件有兩種模型:
Edge Triggered (ET) 邊緣觸發 只有數據到來,才觸發,不管緩存區中是否還有數據。
Level Triggered (LT) 水平觸發 只要有數據都會觸發。
假如有這樣一個例子:
1. 我們已經把一個用來從管道中讀取數據的文件句柄(RFD)添加到epoll描述符
2. 這個時候從管道的另一端被寫入了2KB的數據
3. 調用epoll_wait(2),並且它會返回RFD,說明它已經准備好讀取操作
4. 然後我們讀取了1KB的數據
5. 調用epoll_wait(2)......
Edge Triggered 工作模式:
如果我們在第1步將RFD添加到epoll描述符的時候使用了EPOLLET標志,那麼在第5步調用epoll_wait(2)之後將有可能會掛起,因為剩餘的數據還存在於文件的輸入緩沖區內,而且數據發出端還在等待一個針對已經發出數據的反饋信息。只有在監視的文件句柄上發生了某個事件的時候 ET 工作模式才會匯報事件。因此在第5步的時候,調用者可能會放棄等待仍在存在於文件輸入緩沖區內的剩餘數據。在上面的例子中,會有一個事件產生在RFD句柄上,因為在第2步執行了一個寫操作,然後,事件將會在第3步被銷毀。因為第4 步的讀取操作沒有讀空文件輸入緩沖區內的數據,因此我們在第5 步調用epoll_wait(2)完成後,是否掛起是不確定的。epoll工作在ET模式的時候,必須使用非阻塞套介面,以避免由於一個文件句柄的阻塞讀/阻塞寫操作把處理多個文件描述符的任務餓死。最好以下面的方式調用ET模式的epoll介面,在後面會介紹避免可能的缺陷。
i 基於非阻塞文件句柄
ii 只有當read(2)或者write(2)返回EAGAIN時才需要掛起,等待。但這並不是說每次read()時都需要循環讀,直到讀到產生一個EAGAIN 才認為此次事件處理完成,當read()返回的讀到的數據長度小於請求的數據長度時,就可以確定此時緩沖中已沒有數據了,也就可以認為此事讀事件已處理完成。Level Triggered 工作模式相反的,以LT方式調用epoll介面的時候,它就相當於一個速度比較快的poll(2),並且無論後面的數據是否被使用,因此他們具有同樣的職能。因為即使使用ET模式的epoll,在收到多個chunk 的數據的時候仍然會產生多個事件。調用者可以設定EPOLLONESHOT標志,在 epoll_wait(2)收到事件後epoll會與事件關聯的文件句柄從epoll描述符中禁止掉。因此當EPOLLONESHOT設定後,使用帶有 EPOLL_CTL_MOD標志的epoll_ctl(2)處理文件句柄就成為調用者必須作的事情。
然後詳細解釋ET, LT:
LT(level triggered)是預設的工作方式,並且同時支持block 和no-blocksocket.在這種做法中,內核告訴你一個文件描述符是否就緒了,然後你可以對這個就緒的fd進行IO操作。如果你不作任何操作,內核還是會繼續通知你的,所以,這種模式編程出錯誤可能性要小一點。傳統的select/poll都是這種模型的代表.
ET(edge-triggered)是高速工作方式,只支持no-block socket。在這種模式下,當描述符從未就緒變為就緒時,內核通過epoll告訴你。然後它會假設你知道文件描述符已經就緒,並且不會再為那個文件描述符發送更多的就緒通知,直到你做了某些操作導致那個文件描述符不再為就緒狀態了(比如,你在發送,接收或者接收請求,或者發送接收的數據少於一定量時導致了一個EWOULDBLOCK 錯誤)。但是請注意,如果一直不對這個fd作IO操作(從而導致它再次變成未就緒),內核不會發送更多的通知(only once),不過在TCP協議中,ET模式的加速效用仍需要更多的benchmark確認(這句話不理解)。
在許多測試中我們會看到如果沒有大量的idle -connection 或者deadconnection,epoll 的效率並不會比select/poll 高很多,但是當我們遇到大量的idleconnection(例如WAN 環境中存在大量的慢速連接),就會發現epoll 的效率大大高於select/poll。(未測試)
另外,當使用epoll的ET模型來工作時,當產生了一個EPOLLIN事件後,讀數據的時候需要考慮的是當recv()返回的大小如果等於請求的大小,那麼很有可能是緩沖區還有數據未讀完,也意味著該次事件還沒有處理完,所以還需要再次讀取:

while(rs) {
buflen = recv(activeevents[i].data.fd, buf, sizeof(buf), 0);
if(buflen < 0) {
// 由於是非阻塞的模式,所以當errno為EAGAIN時,表示當前緩沖區已無數據可讀
// 在這里就當作是該次事件已處理處.
if(errno == EAGAIN) {
break;
} else {
return;
}
} else if(buflen == 0) {
// 這里表示對端的socket已正常關閉.
}
if(buflen == sizeof(buf)) {
rs = 1; // 需要再次讀取
} else {
rs = 0;
}
}

還有,假如發送端流量大於接收端的流量(意思是epoll所在的程序讀比轉發的socket要快),由於是非阻塞的socket,那麼send()函數雖然返回,但實際緩沖區的數據並未真正發給接收端,這樣不斷的讀和發,當緩沖區滿後會產生EAGAIN錯誤(參考man send),同時,不理會這次請求發送的數據.所以,需要封裝socket_send()的函數用來處理這種情況,該函數會盡量將數據寫完再返回,返回-1 表示出錯。在socket_send()內部,當寫緩沖已滿(send()返回-1,且errno為EAGAIN),那麼會等待後再重試.這種方式並不很完美,在理論上可能會長時間的阻塞在socket_send()內部,但暫沒有更好的辦法.

ssize_t socket_send(int sockfd, const char* buffer, size_t buflen) {
ssize_t tmp;
size_t total = buflen;
const char *p = buffer;
while(1) {
tmp = send(sockfd, p, total, 0);
if(tmp < 0) {
// 當send收到信號時,可以繼續寫,但這里返回-1.
if(errno == EINTR)
return -1;
// 當socket是非阻塞時,如返回此錯誤,表示寫緩沖隊列已滿,
// 在這里做延時後再重試.
if(errno == EAGAIN) {
usleep(1000);
continue;
}
return -1;
}
if((size_t)tmp == total)
return buflen;
total -= tmp;
p += tmp;
}

return tmp;
}

⑤ 怎麼查看瀏覽器內核

1、首先找打手機桌面上的內置瀏覽器快捷方式。

⑥ linux應用層通過ioctl向內核傳送數據,ioctl的fd參數如何使用

ioctl()和write()等函數使用相似,比如使用ioctl前會定義一個文件描述符fd:
char *fd="/dev/led"; //就是路徑。貌似有句話叫:linux萬物皆為文件

當你要用ioctl()向內核傳遞數據,就要使用fd,不然怎麼知道傳數據到哪去呢!
如: ioctl(fd,xxxxx,xxxxx); //xxxxx為要傳遞的值,具體網路吧,我也不是很清楚

⑦ 請教select函數FD

select()的機制中提供一fd_set的數據結構,實際上是一long類型的數組, 每一個數組元素都能與一打開的文件句柄(不管是Socket句柄,還是其他 文件或命名管道或設備句柄)建立聯系,建立聯系的工作由程序員完成, 當調用select()時,由內核根據IO狀態修改fd_set的內容,由此來通知執 行了select()的進程哪一Socket或文件可讀或可寫。主要用於Socket通信當中!

⑧ select什麼時候比epoll效率高

一、問題引出 聯系區別

問題的引出,當需要讀兩個以上的I/O的時候,如果使用阻塞式的I/O,那麼可能長時間的阻塞在一個描述符上面,另外的描述符雖然有數據但是不能讀出來,這樣實時性不能滿足要求,大概的解決方案有以下幾種:

1.使用多進程或者多線程,但是這種方法會造成程序的復雜,而且對與進程與線程的創建維護也需要很多的開銷。(Apache伺服器是用的子進程的方式,優點可以隔離用戶)

2.用一個進程,但是使用非阻塞的I/O讀取數據,當一個I/O不可讀的時候立刻返回,檢查下一個是否可讀,這種形式的循環為輪詢(polling),這種方法比較浪費CPU時間,因為大多數時間是不可讀,但是仍花費時間不斷反復執行read系統調用。

3.非同步I/O(asynchronous I/O),當一個描述符准備好的時候用一個信號告訴進程,但是由於信號個數有限,多個描述符時不適用。

4.一種較好的方式為I/O多路轉接(I/O
multiplexing)(貌似也翻譯多路復用),先構造一張有關描述符的列表(epoll中為隊列),然後調用一個函數,直到這些描述符中的一個准備
好時才返回,返回時告訴進程哪些I/O就緒。select和epoll這兩個機制都是多路I/O機制的解決方案,select為POSIX標准中的,而
epoll為Linux所特有的。

區別(epoll相對select優點)主要有三:

1.select的句柄數目受限,在linux/posix_types.h頭文件有這樣的聲明:#define __FD_SETSIZE 1024 表示select最多同時監聽1024個fd。而epoll沒有,它的限制是最大的打開文件句柄數目。

2.epoll的最大好處是不會隨著FD的數目增長而降低效率,在selec中採用輪詢處理,其中的數據結構類似一個數組的數據結構,而epoll
是維護一個隊列,直接看隊列是不是空就可以了。epoll只會對"活躍"的socket進行操作---這是因為在內核實現中epoll是根據每個fd上面
的callback函數實現的。那麼,只有"活躍"的socket才會主動的去調用
callback函數(把這個句柄加入隊列),其他idle狀態句柄則不會,在這點上,epoll實現了一個"偽"AIO。但是如果絕大部分的I/O都是
逗活躍的地,每個I/O埠使用率很高的話,epoll效率不一定比select高(可能是要維護隊列復雜)。

3.使用mmap加速內核與用戶空間的消息傳遞。無論是select,poll還是epoll都需要內核把FD消息通知給用戶空間,如何避免不必要的內存拷貝就很重要,在這點上,epoll是通過內核於用戶空間mmap同一塊內存實現的。

二、介面

1)select

1. int select(int maxfdp1, fd_set *restrict readfds, fd_set *restrict
writefds, fd_set *restrict exceptfds, struct timeval *restrict tvptr);

struct timeval{

long tv_sec;

long tv_usec;

}

有三種情況:tvptr == NULL 永遠等待;tvptr->tv_sec == 0 &&
tvptr->tv_usec == 0
完全不等待;不等於0的時候為等待的時間。select的三個指針都可以為空,這時候select提供了一種比sleep更精確的定時器。注意
select的第一個參數maxfdp1並不是描述符的個數,而是最大的描述符加1,一是起限製作用,防止出錯,二來可以給內核輪詢的時候提供一個上屆,
提高效率。select返回-1表示出錯,0表示超時,返回正值是所有的已經准備好的描述符個數(同一個描述符如果讀和寫都准備好,對結果影響是+2)。

2.int FD_ISSET(int fd, fd_set *fdset); fd在描述符集合中非0,否則返回0

3.int FD_CLR(int fd, fd_set *fd_set); int FD_SET(int fd, fd_set *fdset) ;int FD_ZERO(fd_set *fdset);

用一段linux 中man里的話逗FD_ZERO() clears a set.FD_SET() and FD_CLR()
respectively add and remove a given file descriptor from a set.
FD_ISSET() tests to see if a file descriptor is part of the set; this is
useful after select() returns.地這幾個函數與描述符的0和1沒關系,只是添加刪除檢測描述符是否在set中。

2)epoll

1.int epoll_create(int size);
創建一個epoll的句柄,size用來告訴內核這個監聽的數目一共有多大。這個參數不同於select()中的第一個參數,給出最大監聽的fd+1的
值。需要注意的是,當創建好epoll句柄後,它就是會佔用一個fd值,在linux下如果查看/proc/進程id/fd/,是能夠看到這個fd的,所
以在使用完epoll後,必須調用close()關閉,否則可能導致fd被耗盡。

2. int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);
epoll的事件注冊函數,它不同與select()是在監聽事件時告訴內核要監聽什麼類型的事件,而是在這里先注冊要監聽的事件類型。第一個參數是epoll_create()的返回值,第二個參數表示動作,用三個宏來表示:
EPOLL_CTL_ADD:注冊新的fd到epfd中;
EPOLL_CTL_MOD:修改已經注冊的fd的監聽事件;
EPOLL_CTL_DEL:從epfd中刪除一個fd;
第三個參數是需要監聽的fd,第四個參數是告訴內核需要監聽什麼事,struct epoll_event結構如下:
struct epoll_event {
__uint32_t events; /* Epoll events */
epoll_data_t data; /* User data variable */
};

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

關於epoll工作模式ET,LT

LT(level triggered)是預設的工作方式,並且同時支持block和no-block
socket.在這種做法中,內核告訴你一個文件描述符是否就緒了,然後你可以對這個就緒的fd進行IO操作。如果你不作任何操作,內核還是會繼續通知你
的,所以,這種模式編程出錯誤可能性要小一點。傳統的select/poll都是這種模型的代表.
ET (edge-triggered)是高速工作方式,只支持no-block
socket。在這種模式下,當描述符從未就緒變為就緒時,內核通過epoll告訴你。然後它會假設你知道文件描述符已經就緒,並且不會再為那個文件描述
符發送更多的就緒通知,直到你做了某些操作導致那個文件描述符不再為就緒狀態了,但是請注意,如果一直不對這個fd作IO操作(從而導致它再次變成未就
緒),內核不會發送更多的通知(only once)

3. int epoll_wait(int epfd, struct epoll_event * events, int maxevents, int timeout)
等待事件的產生,類似於select()調用。參數events用來從內核得到事件的集合,maxevents告之內核這個events有多大,這個
maxevents的值不能大於創建epoll_create()時的size,參數timeout是超時時間(毫秒,0會立即返回,-1永久阻塞)。該
函數返回需要處理的事件數目,如返回0表示已超時。

⑨ 如何獲得kernel command line 參數

Linux內核在啟動的時候,能接收某些命令行選項或啟動時參數。當內核不能識別某些硬體進而不能設置硬體參數或者為了避免內核更改某些參數的值,可以通過這種方式手動將這些參數傳遞給內核。
如果不使用啟動管理器,比如直接從BIOS或者把內核文件用「cp zImage /dev/fd0」等方法直接從設備啟動,就不能給內核傳遞參數或選項--這也許是我們使用引導管理器比如LILO的好處之一吧。
Linux的內核參數是以空格分開的一個字元串列表,通常具有如下形式:
name[=value_1][,value_2]...[,value_10]
「name」是關鍵字,內核用它來識別應該把「關鍵字」後面的值傳遞給誰,也就是如何處理這個值,是傳遞給處理常式還是作為環境變數或者拋給「init」。值的個數限制為10,你可以通過再次使用該關鍵字使用超過10個的參數。
首先,內核檢查關鍵字是不是 `root=',`nfsroot=', `nfsaddrs=', `ro', `rw', `debug'或 `init',然後內核在bootsetups數組里搜索於該關鍵字相關聯的已注冊的處理函數,如果找到相關的已注冊的處理函數,則調用這些函數並把關鍵字後面的值作為參數傳遞給這些函數。比如你在啟動時設置參數name=a,b,c,d,內核搜索bootsetups數組,如果發現「name」已注冊,則調用「name」的設置函數如name_setup(),並把a,b,c,d傳遞給name_setup()執行。
所有型如「name=value」參數,如果沒有被上面所述的設置函數接收,將被解釋為系統啟動後的環境變數,比如「TERM=vt100」就會被作為一個啟動時參數。
所有沒有被內核設置函數接收也沒又被設置成環境變數的參數都將留給init進程處理,比如「single」。
常用的設備無關啟動時參數。
1、init=...
設置內核執行的初始化進程名,如果該項沒有設置,內核會按順序嘗試/etc/init,
/bin/init,/sbin/init, /bin/sh,如果所有的都沒找到,內核會拋出 kernel panic:的錯誤。
2、nfsaddrs=...
設置從網路啟動時NFS的啟動地址,已字元串的形式給出。
3、nfsroot=...
設置網路啟動時的NFS根名字,如果該字元串不是以 "/"、","、"."開始,默認指向「/tftp-boot」。
以上2、3在無盤站中很有用處。
4、no387
該選項僅當定義了CONFIG_BUGi386時才能用,某些i387協處理器晶元使用32位的保護模式時會有BUG,比如一些浮點運算,使用這個參數可以讓內核忽略387協處理器。
5、no-hlt
該選項僅當定義了CONFIG_BUGi386時才能用,一些早期的i486DX-100晶元在處理「hlt」指令時會有問題,執行該指令後不能可靠的返回操作系統,使用該選項,可以讓linux系統在CPU空閑的時候不要掛起CPU。
6、root=...
該參數告訴內核啟動時使用哪個設備作為根文件系統。比如可以指定根文件為hda8:root=/dev/hda8。
7、ro和rw
ro參數告訴內核以只讀方式載入根文件系統,以便進行文件系統完整性檢查,比如運行fsck;rw參數告訴內核以讀寫方式載入根文件系統,這是默認值。
8、reserve=...
保留埠號。格式:reserve=iobase,extent[,iobase,extent]...,用來保護一定區域的I/O埠不被設備驅動程序自動探測。在某些機器上,自動探測會失敗,或者設備探測錯誤或者不想讓內核初始化設備時會用到該參數;比如: reserve=0x300,32 device=0x300,除device=0x300外所有設備驅動不探測 0x300-0x31f范圍的I/O埠。
9、mem=...
限制內核使用的內存數量。早期BIOS設計為只能識別64M以下的內存,如果你的內存數量大於64M,你可以指明,如果你指明的數量超過了實際安裝的內存數量,系統崩潰是遲早的事情。如:mem=0x1000000意味著有16M內存,如果是mem=0x6000000,就是96M內存了。
注意:很多機型把部分內存作為BIOS的映射,所以你在指定內存大小的時候一定要預留空間。你也可以在 pentium或者更新的CPU上使用mem=nopentium關閉4M的頁表,這要在內核配置時申明。
10、panic=N
默認情況,內核崩潰--kernel panic 後會宕機而不會重啟,你可以設置宕機多少秒之後重啟機器;也可以在/proc/sys/kernel/panic文件里設置。
11、reboot=[warmcold][,[bioshard]]
該選項僅當定義了CONFIG_BUGi386時才能用。2.0.22的內核重啟默認為cool reboot,warm reboot 更快,使用"reboot=bios"可以繼承bios的設置。
12、nosmp 和 maxcpus=N
僅當定義了 __SMP__,該選項才可用。可以用來禁用多CPU或者指明最多支持的CPU個數。
內核開發和調試的啟動時參數
這些參數主要用在內核的開發和調試上,如果你不進行類似的工作,你可以簡單的跳過本小節。
1、debug
linux的日誌級別比較多(詳細信息可以參看linux/kernel.h),一般地,日誌的守護進程klogd只把比DEBUG級別高的日誌寫進磁碟;如果使用該選項,klogd也把內核的DEBUG信息寫進日誌。
2、profile=N
在做內核開發的時候,如果想清楚的知道內核在什麼地方耗用了多少CPU的時鍾周期,可以使用核心的分析函數設置變數prof_shift為非0值,有兩種方式可以實現:一種是在編譯時指定,另一種就是通過「profile=」來指定; 他給出了一個相當於最小單位--即時鍾周期;系統在執行內核代碼的時候, profile[address >;>; prof_shift]的值就會累加,你也可以從 /proc/profile得到關於它的一些信息。
3、swap=N1,N2,N3,N4,N5,N6,N7,N8
設置內核交換演算法的八個參數:max_page_age, page_advance, page_decline,page_initial_age, age_cluster_fract, age_cluster_min, pageout_weight,bufferout_weight。
4、buff=N1,N2,N3,N4,N5,N6
設置內核緩沖內存管理的六個參數:max_buff_age, buff_advance, buff_decline,buff_initial_age, bufferout_weight, buffermem_grace。
使用 RAMDISK的參數
(僅當內核配置並編譯了 CONFIG_BLK_DEV_RAM)。一般的來說,使用ramdisk並不是一件好事,系統自己會更加有效的使用可用的內存;但是,在啟動或者製作啟動盤時,使用ramdisk可以很方便的裝載軟盤等設備上的映象(尤其是安裝程序、啟動過程中),因為在正真使用物理磁碟之前,必須要載入一些必要的模塊,比如文件系統模塊,scsi驅動等(可以參見我的initrd-x.x.x.img文件分析-製作安裝程序不支持的根文件系統)。
早期的ramdisk(比如1.3.48的核心)是靜態分配的,必須以ramdisk=N來指定ramdisk的大小;現在ramdisk可以動態增加。一共有四個參數,兩個布爾型,兩個整形。
1、load_ramdisk=N
如果N=1,就載入ramdisk;如果N=0,就不載入ramdisk;默認值為0。
2、prompt_ramdisk=N
N=1,提示插入軟盤;N=0,不提示插入軟盤;默認為1。
3、ramdisk_size=N或者ramdisk=N
設定ramdisk的最大值為N KB,默認為4096KB。
4、ramdisk_start=N
設置ramdisk的開始塊號為N,當ramdisk有內核的映象文件是需要這個參數。
5、noinitrd
(僅當內核配置了選項 CONFIG_BLK_DEV_RAM和CONFIG_BLK_DEV_INITRD)現在的內核都可以支持initrd了,引導進程首先裝載內核和一個初始化的ramdisk,然後內核將initrd轉換成普通的ramdisk,也就是讀寫模式的根文件系統設備。然後linuxrc執行,然後裝載真正的根文件系統,之後ramdisk被卸載,最後執行啟動序列,比如/sbin/init。
選項noinitrd告訴內核不執行上面的步驟,即使內核編譯了initrd,而是把initrd的數據寫到 /dev/initrd,只是這是一個一次性的設備

⑩ 怎樣在內核態把讀出的數據,直接寫到用戶空間的文件中

1、從資料庫讀取數據放到js裡面: 使用ajax來實現非同步載入後台文件,返回數據到js文件中即可,參考代碼: $.ajax( Method:'post', URL:'xxx', Data:{xxx}, success:function(data){ var database=data; } );

閱讀全文

與內核是如何知道哪個fd有數據相關的資料

熱點內容
我為什麼選擇程序猿 瀏覽:755
安怡為什麼恢復不了原始程序 瀏覽:225
信息流過載是什麼 瀏覽:222
環境實驗室信息管理系統有哪些 瀏覽:264
新車怎麼沒有產品 瀏覽:391
永恆紀元交易密碼錯誤限制多久 瀏覽:942
先練什麼技術最好 瀏覽:730
大盤都有什麼數據 瀏覽:477
東吳證券交易密碼如何設置 瀏覽:757
怎麼查到公司的大數據 瀏覽:709
短線有哪些交易模式 瀏覽:503
顧客反復買產品返費是什麼原因 瀏覽:548
家庭實用新產品有哪些 瀏覽:251
如何做外匯交易誤區 瀏覽:752
如何鍛煉王者的技術 瀏覽:115
哪裡能賣交易冷卻的飾品 瀏覽:666
寶雞第二商貿學校里邊有什麼技術 瀏覽:549
湖北怎麼查打疫苗信息 瀏覽:62
怎麼跟客戶說明產品變更了什麼 瀏覽:173
保稅區會計業務代理需要哪些條件 瀏覽:991