在(android源碼目錄)/hardware/ril/libril/ril.cpp文件中定義了一個管道:
/*此段代碼在ril.cpp中*/
static void *eventLoop(void *param)
{
ret = pipe(filedes);
…其他代碼…
s_fdWakeupRead = filedes[0];
s_fdWakeupWrite = filedes[1];
/*將讀管道設爲非阻塞方式*/
fcntl(s_fdWakeupRead, F_SETFL, O_NONBLOCK);
/*設置s_wakeupfd_event事件的處理函數爲processWakeupCallback*/
ril_event_set (&s_wakeupfd_event, s_fdWakeupRead, true,
processWakeupCallback, NULL);/*processWakeupCallback函數的作用是清空管道*/
/*調用rilEventAddWakeup函數,該函數內容下文已附*/
rilEventAddWakeup (&s_wakeupfd_event);
// Only returns on error
ril_event_loop();
…其他代碼…
}
static void rilEventAddWakeup(struct ril_event *ev)/*此段代碼在ril.cpp中*/
{
ril_event_add(ev);/*把ev添加到監視列表watch_table數組,並將ev->fd加入readFds描述符集*/
triggerEvLoop();/*如果此時不在evenloop線程中了,就把evenloop喚醒,該函數內容下文已附*/
}
static void triggerEvLoop()/*此段代碼在ril.cpp中*/
{
int ret;
if (!pthread_equal(pthread_self(), s_tid_dispatch)) {
/* trigger event loop to wakeup. No reason to do this,
* if we're in the event loop thread */
do {
ret = write (s_fdWakeupWrite, " ", 1);
} while (ret < 0 && errno == EINTR);
}
}
可以從ril_event_add和triggerEvLoop函數看出,每次調用rilEventAddWakeup時,就把安裝了processWakeupCallback(作用是清空管道)處理函數的s_wakeupfd_event事件寫入監視列表watch_table[]數組,然後再向管道內寫入一個字節的空格。
管道的作用是這樣的,eventloop函數所在的進程向下調用ril_event_loop()函數(ril_event.cpp),後者將readFds做了一個本地備份(自然s_wakeupfd_event的描述符也在裏面,因爲ril_event_add已將將ev->fd加入readFds描述符集)。然後再向下遇到了一個select
n = select(nfds, &rfds, NULL, NULL, ptv);
此時如果管道里面有數據,s_fdWakeupRead即爲可用,則select不會阻塞。因此,如果eventloop進程在這裏因爲沒有可用的讀描述符而被阻塞的話,執行rilEventAddWakeup後,管道內被寫入了至少一個空格,則rfds中至少將有一個描述符變得可用,select函數返回。因此eventloop線程(此時該進程正在執行ril_event_loop函數)被select的阻塞的情況(如果被阻塞的話)將被解除。
eventloop線程繼續向下執行,被寫入監視列表watch_table數組的s_wakeupfd_event事件在ril_event_loop函數(ril_event.cpp)調用函數processReadReadies(&rfds, n)時放入了pending_list中,並隨着firePending()函數的執行,processWakeupCallback函數(作用是清空管道)也得到執行。這樣,管道的讀描述符重新變得不可用,使其不影響正常的select功能。相當於將喚醒eventloop進程的“扳機”重新歸位,等待下一次某個進程需要時再次調用的triggerEvLoop。
總的來說,就是通過管道內的數據有無,控制select是否解除阻塞的過程。
轉載:http://hi.baidu.com/mcu99/blog/item/1b408b2c530282e48b13991e.html