高性能サーバの下部ベールの取り外し
78239 ワード
高性能サーバの下部ベールの取り外し
一、前言
私たちはよく高性能サーバーを聞いていますが、それは高性能サーバーとは何ですか.大白話で説明すると、事件の処理が速く、効率が高く、サーバー資源の占有が少なく、多重化などが万千の寵愛を集めている.しかし、高性能を実現するには、優れたアーキテクチャと下位インタフェースが必要です.
この文章は
現在主流の高性能サーバの下層は
epollソースコードを取得するには、微信公衆番号-バックグラウンドサーバで開発し、「epollソースコード」に返信して取得してください.
二、ソースコード解読
2つの重要な構造体
eventpoll構造体:
epitem構造体
int epoll_create(int size);
作用:epoll_を呼び出すcreateメソッドepollのハンドルを作成する
ソース:
ソースコードから見ると
私が他の場所から資料を取得したのは、以前は下層実装がハッシュ表だったが、今は赤と黒の木で、互換性のためにこのパラメータを残していて、真偽も知らないので、理解しておくべきだということだ.
次に
ここで、
以上、epollがファイルを作成する過程で、初期化とファイル関連などを行った.
int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);
役割:epollのイベント登録関数
ソース:
関数
ここでは、イベントのバインドとマウント操作をさらに行います.このsocketにイベントの準備ができている場合は、ep_が呼び出されます.poll_callback関数.イベントを準備キューに追加し、epollを起動します.wait;
int epoll_wait(int epfd, struct epoll_event *events,int maxevents, int timeout);
役割:epollモニタリングのイベントですでに発生したイベントを待つ.
ソース
コアは
三、まとめ
私のレベルと時間が限られていることをお許しください.今回
epoll_create epoll_createがパラメータを入力する場合、パラメータが0より大きいことを保証すればよいが、このパラメータの場合は役に立たない 初期化待ち行列と初期化準備チェーンテーブル、および初期化赤黒ツリーのヘッダノード は、
epoll_ctl epoll_event構造はカーネル空間にコピーする、加えられたfdがpoll接合(epoll,poll,selectI/O多重化はpoll動作をサポートする必要がある)をサポートするか否かを判断する. は、 を判断する.まずeventpoll構造の赤黒ツリーで対応するfdがすでに存在するかどうかを検索し、見つからない場合は挿入操作をサポートします.そうしないと、重複するエラーを報告し、修正、削除操作があります. 挿入操作時にfdに対応するepitem構造が作成され、関連メンバーが初期化され、poll_が呼び出されるように指定されます.wait時のコールバック関数は、データ準備完了時にプロセスを起動するために使用され、(その内部では、デバイスの待機キューを初期化し、そのプロセスを待機キューに登録する)このステップを完了し、 . epitemはこのsocketに関連しており、状態が変化するとep_を通過します.poll_callback()が知らせに来た. は、登録操作を完了するために、追加されたfdの
epoll_wait eventpollオブジェクトのチェーンテーブルが空であるか、操作が必要であるかを判断する.待ち行列を初期化し、自分を掛け、自分のプロセス状態を設定する が睡眠可能な状態である.信号が来たかどうかを判断し(あると中断して目が覚める)、なければschedule_を呼び出すtimeout睡眠、 タイムアウトまたは起動された場合、まず自分が初期化する待機キューから削除し、その後、リソースのユーザー空間へのコピーを開始する .コピーリソースは、まず準備イベントチェーンテーブルを中間チェーンテーブルに移行し、次にユーザ空間に1つずつ遍歴コピーし、水平トリガであるか否かを1つずつ判断し、そうであれば再び準備チェーンテーブル に挿入する.
ユーザ状態とカーネル状態のデータコピー方式ユーザ状態はカーネル状態にデータをコピーし、関数を呼び出した: カーネル状態データがユーザ状態にコピーされ、関数が呼び出される: ここで注意して、多くのブログの上でデータをコピーして使うのは共有メモリで、間違いで、くれぐれも信じないでください~~~~
ETとLTモードの異なる原理
ここではイベントタイプにEPOLLETビットが含まれているかどうかを判断し、含まれていない場合はそのイベントに対応するepitemオブジェクトをepollのrdllistチェーンテーブルに再追加し、ユーザ状態プログラムは次回epoll_を呼び出すwait()が戻るとまたこのepitemを取得できます.次のepollまで待ってwaitすると、すぐに戻り、ユーザースペースに通知されます.
epollはなぜ効率的なのか(selectより)
ソース:https://www.cnblogs.com/apprentice89/p/3234677.htmlは、上記の呼び出し方式のみから また、カーネルはslabメカニズムを使用し、epollに高速なデータ構造を提供しています.カーネルでは、すべてファイルがあります.従って、epollは、上述した監視されたfdを格納するためのファイルシステムをカーネルに登録する.epollを呼び出すとcreateの場合、この仮想epollファイルシステムにfileノードが作成されます.もちろんこのfileは普通のファイルではなく、epollにしかサービスしません.epollは、カーネルによって初期化されると(オペレーティングシステムが起動する)、同時にepoll独自のカーネル高速cache領域を開き、監視したいfdを配置するために使用されます.これらのfdは、カーネルcacheに赤と黒の木の形で保存され、迅速な検索、挿入、削除をサポートします.このカーネル高速cache領域は、連続した物理メモリページを作成し、上にslabレイヤを作成することです.簡単に言えば、物理的に希望するsizeのメモリオブジェクトを割り当て、使用するたびに空き割り当てられたオブジェクトを使用します. epollの3つ目の利点は、epoll_を呼び出すとctlが100万個のfdを詰め込むとepoll_waitは依然として速く戻ることができ、イベントが発生したfdを有効にユーザーに与えることができます.epollを呼び出しているからですcreateの時、カーネルは私たちにepollファイルシステムの中でfileノードを建てて、カーネルcacheの中で赤い黒い木を建てて後でepoll_を保存しますctlから送信されたfdに加えて、epoll_wait呼び出しの場合は,このリストチェーンテーブルにデータがあるかどうかを観察するだけでよい.データがあれば返し、データがなければsleep、timeout時間になったらチェーンテーブルにデータがなくても返します.だから、epoll_waitは非常に効率的です.また、通常、百万計のfdを監視しても、一度に少量の準備完了fdしか返さないことが多いので、epoll_waitはカーネル状態copyの少量のfdからユーザ状態に至るだけである.では、この準備リストチェーンテーブルはどのようにメンテナンスされていますか?epoll_を実行するとctlの場合、fdをepollファイルシステムのfileオブジェクトに対応する赤と黒のツリーに置くほか、カーネル割り込みハンドラにコールバック関数を登録し、このfdが割り込んだら、準備リストチェーンテーブルに入れるようカーネルに伝える.したがって、1つのfd(例えばsocket)にデータが到着すると、カーネルはデバイス(例えばネットワークカード)上のデータcopyをカーネルに挿入した後、fd(socket)を準備リストチェーンテーブルに挿入する.
四、過去のすばらしいまとめ
GDBマルチスレッドの旅
肝臓!ダイナミックプランニング
C++ロック使用上の注意
心血を注いだ再帰
***
公衆番号-バックグラウンドサーバーの開発に注目してください.もっと素晴らしいので、見に来てください.
一、前言
私たちはよく高性能サーバーを聞いていますが、それは高性能サーバーとは何ですか.大白話で説明すると、事件の処理が速く、効率が高く、サーバー資源の占有が少なく、多重化などが万千の寵愛を集めている.しかし、高性能を実現するには、優れたアーキテクチャと下位インタフェースが必要です.
この文章は
linux
プラットフォームに限られています.windowsプラットフォームの下で、IOCPの使い方を参考にすることができます.ここではあまり言いません.現在主流の高性能サーバの下層は
EPOLL
インタフェースをカプセル化し、epollを使用してイベント処理を行うが、なぜepollは高性能サーバの下層イベント処理として使用できるのか.では、ソースコードから始めて、ベールを外しましょう.epollソースコードを取得するには、微信公衆番号-バックグラウンドサーバで開発し、「epollソースコード」に返信して取得してください.
二、ソースコード解読
2つの重要な構造体
eventpoll構造体:
/*
* file->private_data
*/
/*
eventpoll epoll ,
1. struct rb_root rbr; , ,
socket , epoll_ctl
epoll socket ,
struct epitem ,
2.struct list_head rdllist; ,
epoll_wait
3.struct file *file; , epoll
*/
struct eventpoll {
// , kernel , ( )
// ready_list
spinlock_t lock;
// eventloop ,
struct mutex mtx;
// epoll_wait ,
wait_queue_head_t wq;
// file->poll ,
wait_queue_head_t poll_wait;
// ,
struct list_head rdllist;
// epoll
struct rb_root rbr;
// ,
struct epitem *ovflist;
// user
struct user_struct *user;
//
struct file *file;
//
int visited;
struct list_head visited_list_link;
};
epitem構造体
// epoll
struct epitem {
// eventpoll
struct rb_node rbn;
// eventpoll.rdllist
struct list_head rdllink;
// ovflist
struct epitem *next;
/* fd + file, key */
struct epoll_filefd ffd;
/* Number of active wait queue attached to poll operations */
int nwait;
// (eppoll_entry)
// ,
// wait_queue
// ( ),
//
struct list_head pwqlist;
// epitem
struct eventpoll *ep;
/* List header used to link this item to the "struct file" items list */
struct list_head fllink;
/* epoll_ctl */
struct epoll_event event;
};
int epoll_create(int size);
作用:epoll_を呼び出すcreateメソッドepollのハンドルを作成する
ソース:
SYSCALL_DEFINE1(epoll_create, int, size)
{
if (size <= 0)
return -EINVAL;
return do_epoll_create(0);
}
ソースコードから見ると
size
というパラメータはあまり役に立たず、0より大きくさえあればいいのです~私が他の場所から資料を取得したのは、以前は下層実装がハッシュ表だったが、今は赤と黒の木で、互換性のためにこのパラメータを残していて、真偽も知らないので、理解しておくべきだということだ.
次に
do_epoll_create
を見てみましょうstatic int do_epoll_create(int flags)
{
int error, fd;
struct eventpoll *ep = NULL;
struct file *file;
/* Check the EPOLL_* constant for consistency. */
BUILD_BUG_ON(EPOLL_CLOEXEC != O_CLOEXEC);
if (flags & ~EPOLL_CLOEXEC)
return -EINVAL;
/*
* Create the internal data structure ("struct eventpoll").
*/
error = ep_alloc(&ep);
if (error < 0)
return error;
/*
* Creates all the items needed to setup an eventpoll file. That is,
* a file structure and a free file descriptor.
*/
// ,
fd = get_unused_fd_flags(O_RDWR | (flags & O_CLOEXEC));
if (fd < 0) {
error = fd;
goto out_free_ep;
}
// [eventpoll] , , epoll
file = anon_inode_getfile("[eventpoll]", &eventpoll_fops, ep,
O_RDWR | (flags & O_CLOEXEC));
if (IS_ERR(file)) {
error = PTR_ERR(file);
goto out_free_fd;
}
ep->file = file;
// file
fd_install(fd, file);
return fd;
out_free_fd:
put_unused_fd(fd);
out_free_ep:
ep_free(ep);
return error;
}
ここで、
error = ep_alloc(&ep);
は、eventpoll
の構造を割り当てて行う初期化動作である.以上、epollがファイルを作成する過程で、初期化とファイル関連などを行った.
int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);
役割:epollのイベント登録関数
ソース:
SYSCALL_DEFINE4(epoll_ctl, int, epfd, int, op, int, fd,
struct epoll_event __user *, event)
{
struct epoll_event epds;
// : , epoll_event NULL
// epoll_event copy
if (ep_op_has_event(op) &&
//
copy_from_user(&epds, event, sizeof(struct epoll_event)))
return -EFAULT;
return do_epoll_ctl(epfd, op, fd, &epds, false);
}
関数
do_epoll_ctl
を見てみましょう.int do_epoll_ctl(int epfd, int op, int fd, struct epoll_event *epds,
bool nonblock)
{
int error;
int full_check = 0;
struct fd f, tf;
struct eventpoll *ep;
struct epitem *epi;
struct eventpoll *tep = NULL;
//
.....
epi = ep_find(ep, tf.file, fd);
error = -EINVAL;
switch (op) {
//
case EPOLL_CTL_ADD:
if (!epi) {
epds->events |= EPOLLERR | EPOLLHUP;
error = ep_insert(ep, epds, tf.file, fd, full_check);
} else
error = -EEXIST;
break;
//
case EPOLL_CTL_DEL:
if (epi)
error = ep_remove(ep, epi);
else
error = -ENOENT;
break;
//
case EPOLL_CTL_MOD:
if (epi) {
if (!(epi->event.events & EPOLLEXCLUSIVE)) {
epds->events |= EPOLLERR | EPOLLHUP;
error = ep_modify(ep, epi, epds);
}
} else
error = -ENOENT;
break;
}
if (tep != NULL)
mutex_unlock(&tep->mtx);
mutex_unlock(&ep->mtx);
error_tgt_fput:
if (full_check) {
clear_tfile_check_list();
mutex_unlock(&epmutex);
}
fdput(tf);
error_fput:
fdput(f);
error_return:
return error;
}
do_epoll_ctl
関数では、ファイル記述子の検証を行い、入力されたfdに従って追加して監視することが多いので、ここでは増加した操作を見てみましょう.// epollfd fd
static int ep_insert(struct eventpoll *ep, const struct epoll_event *event,
struct file *tfile, int fd, int full_check)
{
int error, pwake = 0;
__poll_t revents;
long user_watches;
struct epitem *epi;
struct ep_pqueue epq;
lockdep_assert_irqs_enabled();
user_watches = atomic_long_read(&ep->user->epoll_watches);
if (unlikely(user_watches >= max_user_watches))
return -ENOSPC;
// epi
if (!(epi = kmem_cache_alloc(epi_cache, GFP_KERNEL)))
return -ENOMEM;
/* Item initialization follow here ... */
INIT_LIST_HEAD(&epi->rdllink);
INIT_LIST_HEAD(&epi->fllink);
INIT_LIST_HEAD(&epi->pwqlist);
// epoll fd epitem ep
epi->ep = ep;
// epitem ffd
ep_set_ffd(&epi->ffd, tfile, fd);
// fd
epi->event = *event;
epi->nwait = 0;
epi->next = EP_UNACTIVE_PTR;
if (epi->event.events & EPOLLWAKEUP) {
error = ep_create_wakeup_source(epi);
if (error)
goto error_create_wakeup_source;
} else {
RCU_INIT_POINTER(epi->ws, NULL);
}
/* Initialize the poll table using the queue callback */
epq.epi = epi;
// ep_ptable_queue_proc epq.pt 。
init_poll_funcptr(&epq.pt, ep_ptable_queue_proc);
/*
* Attach the item to the poll hooks and get current event bits.
* We can safely use the file* here because its usage count has
* been increased by the caller of this function. Note that after
* this operation completes, the poll callback can start hitting
* the new item.
*/
// ep_ptable_queue_proc, wait queue head
// ,
revents = ep_item_poll(epi, &epq.pt, 1);
/*
* We have to check if something went wrong during the poll wait queue
* install process. Namely an allocation for a wait queue failed due
* high memory pressure.
*/
error = -ENOMEM;
if (epi->nwait < 0)
goto error_unregister;
/* Add the current item to the list of active epoll hook for this file */
// epitem f_ep_links
spin_lock(&tfile->f_lock);
list_add_tail_rcu(&epi->fllink, &tfile->f_ep_links);
spin_unlock(&tfile->f_lock);
/*
* Add the current item to the RB tree. All RB tree operations are
* protected by "mtx", and ep_insert() is called with "mtx" held.
*/
// epitem ep
ep_rbtree_insert(ep, epi);
/* now check if we've created too many backpaths */
error = -EINVAL;
if (full_check && reverse_path_check())
goto error_remove_epi;
/* We have to drop the new item inside our item list to keep track of it */
write_lock_irq(&ep->lock);
/* record NAPI ID of new item if present */
ep_set_busy_poll_napi_id(epi);
/* If the file is already "ready" we drop it inside the ready list */
// , epitem
if (revents && !ep_is_linked(epi)) {
list_add_tail(&epi->rdllink, &ep->rdllist);
ep_pm_stay_awake(epi);
/* Notify waiting tasks that events are available */
if (waitqueue_active(&ep->wq))
// sys_epoll_wait , sys_epoll_wait
wake_up(&ep->wq);
if (waitqueue_active(&ep->poll_wait))
pwake++;
}
write_unlock_irq(&ep->lock);
atomic_long_inc(&ep->user->epoll_watches);
/* We have to call this outside the lock */
if (pwake)
ep_poll_safewake(ep, NULL);
return 0;
error_remove_epi:
spin_lock(&tfile->f_lock);
list_del_rcu(&epi->fllink);
spin_unlock(&tfile->f_lock);
rb_erase_cached(&epi->rbn, &ep->rbr);
error_unregister:
ep_unregister_pollwait(ep, epi);
/*
* We need to do this because an event could have been arrived on some
* allocated wait queue. Note that we don't care about the ep->ovflist
* list, since that is used/cleaned only inside a section bound by "mtx".
* And ep_insert() is called with "mtx" held.
*/
write_lock_irq(&ep->lock);
if (ep_is_linked(epi))
list_del_init(&epi->rdllink);
write_unlock_irq(&ep->lock);
wakeup_source_unregister(ep_wakeup_source(epi));
error_create_wakeup_source:
kmem_cache_free(epi_cache, epi);
return error;
}
ここでは、イベントのバインドとマウント操作をさらに行います.このsocketにイベントの準備ができている場合は、ep_が呼び出されます.poll_callback関数.イベントを準備キューに追加し、epollを起動します.wait;
int epoll_wait(int epfd, struct epoll_event *events,int maxevents, int timeout);
役割:epollモニタリングのイベントですでに発生したイベントを待つ.
ソース
SYSCALL_DEFINE4(epoll_wait, int, epfd, struct epoll_event __user *, events,
int, maxevents, int, timeout)
{
return do_epoll_wait(epfd, events, maxevents, timeout);
}
do_epoll_wait
関数を直接見に行きましょう~static int do_epoll_wait(int epfd, struct epoll_event __user *events,
int maxevents, int timeout)
{
int error;
struct fd f;
struct eventpoll *ep;
/* The maximum number of event must be greater than zero */
if (maxevents <= 0 || maxevents > EP_MAX_EVENTS)
return -EINVAL;
/* Verify that the area passed by the user is writeable */
if (!access_ok(events, maxevents * sizeof(struct epoll_event)))
return -EFAULT;
/* Get the "struct file *" for the eventpoll file */
// epoll struct file
// struct file eventpoll
f = fdget(epfd);
if (!f.file)
return -EBADF;
/*
* We have to check that the file structure underneath the fd
* the user passed to us _is_ an eventpoll file.
*/
error = -EINVAL;
if (!is_file_epoll(f.file))
goto error_fput;
/*
* At this point it is safe to assume that the "private_data" contains
* our own data structure.
*/
// private_data eventpoll
ep = f.file->private_data;
/* Time to fish for events ... */
//
error = ep_poll(ep, events, maxevents, timeout);
error_fput:
fdput(f);
return error;
}
コアは
ep_poll
関数にあるようですね~行ってみましょうstatic int ep_poll(struct eventpoll *ep, struct epoll_event __user *events,
int maxevents, long timeout)
{
int res = 0, eavail, timed_out = 0;
u64 slack = 0;
wait_queue_entry_t wait;
ktime_t expires, *to = NULL;
lockdep_assert_irqs_enabled();
// timeout
if (timeout > 0) {
struct timespec64 end_time = ep_set_mstimeout(timeout);
slack = select_estimate_accuracy(&end_time);
to = &expires;
*to = timespec64_to_ktime(end_time);
//
} else if (timeout == 0) {
/*
* Avoid the unnecessary trip to the wait queue loop, if the
* caller specified a non blocking operation. We still need
* lock because we could race and not see an epi being added
* to the ready list while in irq callback. Thus incorrectly
* returning 0 back to userspace.
*/
timed_out = 1;
write_lock_irq(&ep->lock);
eavail = ep_events_available(ep);
write_unlock_irq(&ep->lock);
goto send_events;
}
fetch_events:
// , eventpoll rdllist
if (!ep_events_available(ep))
ep_busy_loop(ep, timed_out);
eavail = ep_events_available(ep);
if (eavail)
goto send_events;
/*
* Busy poll timed out. Drop NAPI ID for now, we can add
* it back in when we have moved a socket with a valid NAPI
* ID onto the ready list.
*/
ep_reset_busy_poll_napi_id(ep);
do {
/*
* Internally init_wait() uses autoremove_wake_function(),
* thus wait entry is removed from the wait queue on each
* wakeup. Why it is important? In case of several waiters
* each new wakeup will hit the next waiter, giving it the
* chance to harvest new event. Otherwise wakeup can be
* lost. This is also good performance-wise, because on
* normal wakeup path no need to call __remove_wait_queue()
* explicitly, thus ep->lock is not taken, which halts the
* event delivery.
*/
init_wait(&wait);
write_lock_irq(&ep->lock);
/*
* Barrierless variant, waitqueue_active() is called under
* the same lock on wakeup ep_poll_callback() side, so it
* is safe to avoid an explicit barrier.
*/
// ep_poll_callback() ,
// TASK_INTERRUPTIBLE 。
__set_current_state(TASK_INTERRUPTIBLE);
/*
* Do the final check under the lock. ep_scan_ready_list()
* plays with two lists (->rdllist and ->ovflist) and there
* is always a race when both lists are empty for short
* period of time although events are pending, so lock is
* important.
*/
eavail = ep_events_available(ep);
if (!eavail) {
if (signal_pending(current))
res = -EINTR;
else
__add_wait_queue_exclusive(&ep->wq, &wait);
}
write_unlock_irq(&ep->lock);
if (eavail || res)
break;
if (!schedule_hrtimeout_range(to, slack, HRTIMER_MODE_ABS)) {
timed_out = 1;
break;
}
/* We were woken up, thus go and try to harvest some events */
eavail = 1;
} while (0);
//
__set_current_state(TASK_RUNNING);
if (!list_empty_careful(&wait.entry)) {
write_lock_irq(&ep->lock);
__remove_wait_queue(&ep->wq, &wait);
write_unlock_irq(&ep->lock);
}
send_events:
if (fatal_signal_pending(current)) {
/*
* Always short-circuit for fatal signals to allow
* threads to make a timely exit without the chance of
* finding more events available and fetching
* repeatedly.
*/
res = -EINTR;
}
/*
* Try to transfer events to user space. In case we get 0 events and
* there's still timeout left over, we go trying again in search of
* more luck.
*/
/* , event , copy ... */
if (!res && eavail &&
!(res = ep_send_events(ep, events, maxevents)) && !timed_out)
goto fetch_events;
return res;
}
ep_send_events()
関数は、ユーザが入力したメモリをep_send_events_data
構造に単純にカプセル化し、ep_scan_ready_list()
を呼び出して、準備キュー内のイベントをユーザ空間のメモリに入力する.ユーザ空間はこの結果にアクセスし,処理を行う.static int ep_send_events(struct eventpoll *ep,
struct epoll_event __user *events, int maxevents)
{
struct ep_send_events_data esed;
esed.maxevents = maxevents;
esed.events = events;
ep_scan_ready_list(ep, ep_send_events_proc, &esed, 0, false);
return esed.res;
}
static __poll_t ep_send_events_proc(struct eventpoll *ep, struct list_head *head,
void *priv)
{
struct ep_send_events_data *esed = priv;
__poll_t revents;
struct epitem *epi, *tmp;
struct epoll_event __user *uevent = esed->events;
struct wakeup_source *ws;
poll_table pt;
init_poll_funcptr(&pt, NULL);
esed->res = 0;
/*
* We can loop without lock because we are passed a task private list.
* Items cannot vanish during the loop because ep_scan_ready_list() is
* holding "mtx" during this call.
*/
lockdep_assert_held(&ep->mtx);
list_for_each_entry_safe(epi, tmp, head, rdllink) {
if (esed->res >= esed->maxevents)
break;
/*
* Activate ep->ws before deactivating epi->ws to prevent
* triggering auto-suspend here (in case we reactive epi->ws
* below).
*
* This could be rearranged to delay the deactivation of epi->ws
* instead, but then epi->ws would temporarily be out of sync
* with ep_is_linked().
*/
ws = ep_wakeup_source(epi);
if (ws) {
if (ws->active)
__pm_stay_awake(ep->ws);
__pm_relax(ws);
}
list_del_init(&epi->rdllink);
/*
* If the event mask intersect the caller-requested one,
* deliver the event to userspace. Again, ep_scan_ready_list()
* is holding ep->mtx, so no operations coming from userspace
* can change the item.
*/
revents = ep_item_poll(epi, &pt, 1);
if (!revents)
continue;
// copy
if (__put_user(revents, &uevent->events) ||
__put_user(epi->event.data, &uevent->data)) {
// epi ready
list_add(&epi->rdllink, head);
ep_pm_stay_awake(epi);
if (!esed->res)
esed->res = -EFAULT;
return 0;
}
esed->res++;
uevent++;
if (epi->event.events & EPOLLONESHOT)
epi->event.events &= EP_PRIVATE_BITS;
else if (!(epi->event.events & EPOLLET)) {
/*
* If this file has been added with Level
* Trigger mode, we need to insert back inside
* the ready list, so that the next call to
* epoll_wait() will check again the events
* availability. At this point, no one can insert
* into ep->rdllist besides us. The epoll_ctl()
* callers are locked out by
* ep_scan_ready_list() holding "mtx" and the
* poll callback will queue them in ep->ovflist.
*/
list_add_tail(&epi->rdllink, &ep->rdllist);
ep_pm_stay_awake(epi);
}
}
return 0;
}
__put_user
を見れば分かるように、カーネルコピーデータからユーザ空間に使用されています.put_user関数は、いわゆる共有メモリとは少しも関係ありませんが、現在ブログには多くのエラーがありますので、修正してください.三、まとめ
私のレベルと時間が限られていることをお許しください.今回
epoll
のソースコードを読むのは、カーネルとユーザー状態のデータコピーの使用方法がネット上で議論されていることから始まったので、epoll
のソースコードを探して大雑把に読んで、後で時間を割いて精読しますが、今回はepoll
のソースコードを大雑把に読んだのですが、収穫も多く、次に簡単にまとめてみます~(このまとめは自分でソースコードを見て得たものもあれば、ネットから集めた資料もありますが、間違っていたら教えてください)epoll_create
eventpoll
構造を割り当て、初期化動作を行う.epoll_ctl
ep = f.file->private_data;
event_を取得pollオブジェクト;op
によりイベントの修正、追加、削除操作fileoperation->poll
関数(最後にpoll_wait操作を呼び出す)を最後に呼び出し、epitem構造を赤黒ツリーに追加する.epoll_wait
ユーザ状態とカーネル状態のデータコピー方式
copy_from_user
__put_user
ETとLTモードの異なる原理
else if (!(epi->event.events & EPOLLET)) {
/*
* If this file has been added with Level
* Trigger mode, we need to insert back inside
* the ready list, so that the next call to
* epoll_wait() will check again the events
* availability. At this point, no one can insert
* into ep->rdllist besides us. The epoll_ctl()
* callers are locked out by
* ep_scan_ready_list() holding "mtx" and the
* poll callback will queue them in ep->ovflist.
*/
list_add_tail(&epi->rdllink, &ep->rdllist);
ep_pm_stay_awake(epi);
}
ここではイベントタイプにEPOLLETビットが含まれているかどうかを判断し、含まれていない場合はそのイベントに対応するepitemオブジェクトをepollのrdllistチェーンテーブルに再追加し、ユーザ状態プログラムは次回epoll_を呼び出すwait()が戻るとまたこのepitemを取得できます.次のepollまで待ってwaitすると、すぐに戻り、ユーザースペースに通知されます.
epollはなぜ効率的なのか(selectより)
ソース:https://www.cnblogs.com/apprentice89/p/3234677.html
epoll
対select/poll
の利点を見ることができる:select/pollは、呼び出すたびに監視するすべてのfdをselect/pollシステム呼び出しに伝達する(これは、呼び出すたびにfdリストをユーザ状態からカーネル状態にコピーし、fdの数が多い場合、これは低効率になることを意味する).epoll_を呼び出すたびにwaitの場合(select/pollを呼び出すのと同じ役割を果たす)、fdリストをカーネルに渡す必要はありません.epoll_ctlでは、監視が必要なfdがカーネルに通知される(epoll_ctl
は、毎回すべてのfdをコピーする必要はなく、インクリメンタル操作のみを行う必要がある).だから、epoll_を呼び出してcreateの後、カーネルはカーネル状態で監視するfdを格納するデータ構造の準備を開始した.毎回epoll_ctlはこのデータ構造を簡単に維持するだけです.四、過去のすばらしいまとめ
GDBマルチスレッドの旅
肝臓!ダイナミックプランニング
C++ロック使用上の注意
心血を注いだ再帰
***
公衆番号-バックグラウンドサーバーの開発に注目してください.もっと素晴らしいので、見に来てください.