1. 程式人生 > >嵌入式-項目1-基於視頻壓縮的實時監控系統-1.EPOLL框架

嵌入式-項目1-基於視頻壓縮的實時監控系統-1.EPOLL框架

驅動程序 hat 邊沿觸發 lec wro div 技術 情況下 因此

第一課-Epoll框架

  1. 為什麽要用Epoll

1)阻塞型IO

阻塞是指沒有獲得資源則掛起進程,直到獲得資源為止。被掛起的進程進入休眠狀態,被調度器的運行隊列移走,直到等待條件被滿足。

非阻塞是不能進行設備操作時不掛起,或放棄,或反復查詢,直到可以進行操作為止。

驅動程序常需要這種能力:當應用程序進行read(),write()等系統調用時,若設備的資源不能獲取,而用戶又希望以阻塞的方式訪問設備,驅動程序應該在設備驅動程序的xxx_read(), xxx_write()等操作中將進程阻直到資源可以獲取,以後,應用程序read(),write()等調用返回,整個過程仍然進行了正確的設備訪問,用戶並沒有感知到

;若用戶以非阻塞的方式訪問設備文件,則當設備資源不可獲取時,設備驅動的xxx_read()xxx_write()等操作應立即返回,read()write()等系統調用也隨即被訪問。

阻塞不是低效率,如果設備驅動不阻塞, 用戶想獲取設備資源只能不斷查詢,消耗CPU資源,阻塞訪問時,不能獲取資源的進程將進入休眠,將CPU資源讓給其他資源。阻塞的進程會進入休眠狀態,因此,必須確保有一個地方能喚醒休眠的進程。喚醒進程的地方最大可能發生在中斷裏面,因為硬件資源獲得的同時往往伴隨著一個中斷。

2)多路復用

多路復用是指以同一傳輸媒質(線路)承載多路信號進行通信的方式。各路信號在送往傳輸媒質以前,需按一定的規則進行調制,以利於各路已調信號在媒質中傳輸,並不致混淆,從而在傳到對方時使信號具有足夠能量,且可用反調制的方法加以區分、恢復成原信號。多路復用常用的方法有頻分多路復用和時分多路復用,碼分多路復用的應用也在不斷擴大。

頻分多路復用FDM (Frequency Division Multiplexing)時分多路復用TDM (Time Division Multiplexing)是兩種最常用的多路復用技術(Multiplexing)

目前我們使用的函數,有那個可以實現阻塞型IO和多路復用呢?當然即使select函數。

3select函數

select()的機制中提供一fd_set的數據結構,實際上是一long類型的數組 每一個數組元素都能與一打開的文件句柄(不管是Socket句柄,還是其他 文件或命名管道或設備句柄)建立聯系,建立聯系的工作由程序員完成, 當調用select()時,由內核根據

IO狀態修改fd_set的內容,由此來通知執 行了select()的進程哪一Socket或文件可讀或可寫。主要用於Socket通信當中!

我們看一下這個函數的一般使用過程:

需要頭文件#include <sys/select.h>

while(1)

{

FD_ZERO(&set); //1. 初始化文件描述符集合

foreach(需要監控的文件)

{

fd大於maxfd,則maxfd-fd

FD_SET(fd,&set) //2.將文件的描述符一個一個的加到文件中去

}

res=select(maxfd+1,&set,0,0,0); /*3. 調用select開始監控,要是沒有文件滿足要求,就會等待在這個地方,

等到要麽超時,要麽就等待在這個地方*/

if(FD_ISSET(listen_fd,&set))

{

newfd=accept(listen_fd);

array[nsock]=newfd;

if(--res<=0) continue;

}

foreach(需要監控的文件) /*4. 遍歷文件,看看是哪個的變化導致的這種退出,很占用資源*/

{

if(FD_ISSET(fd,&tyle="COLOR: #ff0000">set))

執行讀等相關的操作

如果錯誤或者關閉,則要刪除fd,將array中相應位置和最後而定一個元素相互調換就好,nsock減一

if(--res<=0) continue;

}

}

介於select函數在使用的時候,由於遍歷性使得運行的時間很長,很浪費資源,所以我們引出了下面的epoll函數。

(4)Epoll分析

epollLinux內核為處理大批量文件描述符而作了改進的poll,是Linux下多路復用IO接口select/poll的增強版本,它能顯著提高程序在大量並發連接中只有少量活躍的情況下的系統CPU利用率。另一點原因就是獲取事件的時候,它無須遍歷整個被偵聽的描述符集,只要遍歷那些被內核IO事件異步喚醒而加入Ready隊列的描述符集合就行了。epoll除了提供select/poll那種IO事件的水平觸發(Level Triggered)外,還提供了邊緣觸發(Edge Triggered),這就使得用戶空間程序有可能緩存IO狀態,減少epoll_wait/epoll_pwait的調用,提高應用程序效率。

無序遍歷,沒有上線,是linux系統中最優秀的多路復用機制。

  1. 怎麽使用Epoll

Epoll支持管道,FIFO,套接字,POSIX消息隊列,終端,設備等等,但就是不支持普通文件Epoll的使用分為3個環節。

epoll_creat/epoll_creat1(創建epoll監聽池)

epoll_ctl(添加要監聽的事件)

epoll_wait(等待事件的發生)

(1)epoll_creat/epoll_creat1

創建epoll監聽池

synopsis:(梗要)

#include <sys/epoll.h>

int epoll_creat(int size); //老的版本中size表示監聽池的大小,現在沒用了

int epoll_creat1(int flags); /*flags表示創建標誌,一般的情況下,我麽讓它等於0,當它等於0的時候,這個函數和epoll_creat()函數的作用是一模一樣的 */

description

epoll_creat() craets an epoll “instance”, requesting the kernel to allocate an event backing store dimensioned for size descriptors. The size is not the maxmum size of the backing store but just a hint to the kernel about how to dimension internal structures.

epoll_creat() returns a file descriptor referring to the new epoll instance. This file descriptor is used for all the subsequent calls to the epoll interface.

返回值:

成功,無;失敗,-1

(2)epoll_ctl

添加要監聽的事件, select是以文件作為監聽對象的,但是epoll_ctl是以事件為監聽對象的。A文件(可讀)和A文件(可寫)算是兩個文件

synopsis:(梗要)

#include <sys/epoll.h>

int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);

description

This system call performs control operations on the epoll instance referred toby the file descriptor epfd. It requests that the operation op be performed for the target file dercriptor, fd.

epfd我們要監聽的監聽池的fd

op表示我們對監聽池進行的操作:

EPOLL_CTL_ADD:加入事件

EPOLL_CTL_MOD:更改事件

EPOLL_CTL_DEL:刪除事件

fd表示我們要操作的事件是哪一個文件

*event表示我們操作的類型:

EPOLLIN:可讀

EPOLLOUT:可寫

返回值:成功0,失敗-1.

(3)epoll_wait

等待事件的發生

synopsis:(梗要)

#include <sys/epoll.h>

int epoll_wait(int epfd struct epoll_event *event, int maxevent, int timeout);

description

epfd:等待的事件

*event:當事件被監聽到的時候,就會被放到這個數組裏面

maxevent:最多的時間數

timeout:等待的最長時間

返回值:

成功,表示有多少個事件發生;失敗,-1

  1. 程序-監聽兩個FIFO

創建文件ep.cew1.cew2.c。文件ep.c是我們創建的epoll監控文件,ew1.cew2.c文件分別是我們對創建的兩個FIFO文件的寫入文件。程序如下:

ep.c

#include<stdio.h>

#include<stdlib.h>

#include<unistd.h>

#include<fcntl.h>

#include<sys/epoll.h>

int main()

{

int fd1, fd2;

int efd;

int i;

char c;

int n;

struct epoll_event event;

struct epoll_event *events;

//創建FIO

mkfifo("/tmp/fifo1",0666);

mkfifo("/tmp/fifo2",0666);

fd1=open("/tmp/fifo1",O_RDONLY);

fd2=open("/tmp/fifo2",O_RDONLY);

//創建監聽池

efd=epoll_create1(0);

//構造監聽事件,加入監聽池

event.events = EPOLLIN|EPOLLET; /*EPOLLET表示邊沿觸發*/

event.data.fd = fd1;

epoll_ctl(efd,EPOLL_CTL_ADD,fd1,&event);

event.events = EPOLLIN;

event.data.fd = fd2;

epoll_ctl(efd,EPOLL_CTL_ADD,fd2,&event);

//開始監聽

events = calloc(100,sizeof(event)); //給要保存的數組分配空間,可以保存100個事件

n = epoll_wait(efd,events,100,-1);

for(i=0;i<n;i++)

{

if(events[i].events & EPOLLIN) //如果數據類型是EPOLLIN

{

read(events[i].data.fd,&c,1);

//假設我們讀的是個字符,長度當然就是1,要讀的文件在events[i].data.fd

printf("file %d can be read\n",events[i].data.fd);

}

if(events[i].events & EPOLLOUT) //如果數據類型是EPOLLOUT

{

//

}

if(events[i].events & EPOLLERR) //如果數據類型是EPOLLERR,出錯的

{

//

}

}

free(events); //釋放我們使用的空間,必須得有

close(fd1);

close(fd2); //關閉打開的文件

}

ew1.c

#include<stdio.h>

#include<stdlib.h>

#include<unistd.h>

#include<fcntl.h>

int main()

{

int fd;

char c1 = ‘c‘;

fd = open("/tmp/fifo1", O_WRONLY); //用只寫的方式打開

write(fd,&c1,1);

close(fd);

return 0;

}

ew2.c

#include<stdio.h>

#include<stdlib.h>

#include<unistd.h>

#include<fcntl.h>

int main()

{

int fd;

char c1 = ‘c‘;

fd = open("/tmp/fifo2", O_WRONLY); //用只寫的方式打開

write(fd,&c1,1);

close(fd);

return 0;

}

分別用gcc編譯成epew1ew2三個.o文件。在一個終端中運行./ep會是程序卡住,使用ctrl+shift+t生成另一個相同的中斷,運行./ew1./ew2後,第一個中斷可以繼續運行,結果為:

file 3 can be read

file 4 can be read

嵌入式-項目1-基於視頻壓縮的實時監控系統-1.EPOLL框架