Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> Android熱插拔事件處理詳解

Android熱插拔事件處理詳解

編輯:關於Android編程

一、Android熱插拔事件處理流程圖

Android熱插拔事件處理流程如下圖所示:

 

\
錨點
錨點

二、組成

1. NetlinkManager:

全稱是NetlinkManager.cpp位於Android 4.x 源碼位置/system/vold/NetlinkManager.cpp。該類的主要通過引用NetlinkHandler類中的onEvent()方法來接收來自內核的事件消息,NetlinkHandler位於/system/vold/NetlinkHandler.cpp。

2. VolumeManager:

全稱是VolumeManager.cpp位於Android 4.x源碼位置/system/vold/VolumeManager.cpp。該類的主要作用是接收經過NetlinkManager處理過後的事件消息。因為我們這裡是SD的掛載,因此經過NetlinkManager處理過後的消息會分為五種,分別是:block,switch,usb_composite,battery,power_supply。這裡SD卡掛載的事件是block。

3. DirectVolume:

位於/system/vold/DirectVolume.cpp。該類的是一個工具類,主要負責對傳入的事件進行進一步的處理,block事件又可以分為:Add,Removed,Change,Noaction這四種。後文通過介紹Add事件展開。

4. Volume:

位於/system/vold/Volume.cpp,該類是負責SD卡掛載的主要類。Volume.cpp主要負責檢查SD卡格式,以及對復合要求的SD卡進行掛載,並通過Socket將消息SD卡掛載的消息傳遞給NativeDaemonConnector。

5. CommandListener:

該類位於位於/system/vold/CommandListener.cpp。通過vold socket與NativeDaemonConnector通信。

6. NativeDaemonConnector:

該類位於frameworks/base/services/java/com.android.server/NativeDaemonConnector.java。該類用於接收來自Volume.cpp 發來的SD卡掛載消息並向上傳遞。

7. MountService:

位於frameworks/base/services/java/com.android.server/MountService.java。MountService是一個服務類,該服務是系統服務,提供對外部存儲設備的管理、查詢等。在外部存儲設備狀態發生變化的時候,該類會發出相應的通知給上層應用。在Android系統中這是一個非常重要的類。

8.StorageManaer:

位於frameworks/base/core/java/andriod/os/storage/StorageManager.java。在該類的說明中有提到,該類是系統存儲服務的接口。在系統設置中,有Storage相關項,同時Setting也注冊了該類的監聽器。而StorageManager又將自己的監聽器注冊到了MountService中,因此該類主要用於上層應用獲取SD卡狀態。

錨點

三、典型流程描述 (SD卡掛載流程)

整個過程從Kernel檢測到SD卡插入事件開始,之前的一些硬件中斷的觸發以及driver的加載這裡並不敘述,一直到SD卡掛載消息更新到“Android——系統設置——存儲”一項中。

1. Kernel發出SD卡插入uevent。

2.NetlinkHandler::onEvent()接收內核發出的uevent並進行解析。

3. VolumeManager::handlBlockEvent()處理經過第二步處理後的事件。

4. 接下來調用DirectVolume:: handleBlockEvent()。

在該方法中主要有兩點需要注意:

第一,程序首先會遍歷mPath容器,尋找與event對應的sysfs_path是否存在與mPath容器中。

第二,針對event中的action有4種處理方式:Add,Removed,Change,Noaction 。

例如:在Add action中會有如下操作(因為我們這裡所講的是SD卡的掛載流程,因此以Add來說明),首先創建設備節點,其次對disk和partition兩種格式的設備分別進行處理。SD卡屬於disk類型。

5. 經過上一步之後會調用DirectVolume::handleDiskAdded()方法,在該方法中會廣播disk insert消息。

6. SocketListener::runListener會接收DirectVolume::handleDiskAdded()廣播的消息。該方法主要完成對event中數據的獲取,通過Socket。(PS:這裡的SocketListener.cpp位於Android源碼/system/core/libsysutils/src/中,後文的FramworkListener.cpp也是,之前自己找了很久 T_T)

7. 調用FrameworkListener::onDataAvailable()方法處理接收到的消息內容。

8. FrameworkListener::dispatchCommand()該方法用於分發指令。

9. 在FrameworkListener::dispatchCommand()方法中,通過runCommand()方法去調用相應的指令。

10. 在/system/vold/CommandListener.cpp中有runCommand()的具體實現。在該類中可以找到這個方法:CommandListener::VolumeCmd::runCommand(),從字面意思上來看這個方法就是對Volume分發指令的解析。該方法中會執行“mount”函數:vm->mountVolume(arg[2])。

11. mountVolume(arg[2])在VolumeManager::mountVolume()中實現,在該方法中調用v->mountVol()。

12. mountVol()方法在Volume::mountVol()中實現,該函數是真正的掛載函數。(在該方法中,後續的處理都在該方法中,在Mount過程中會廣播相應的消息給上層,通過setState()函數。)

13. setState(Volume::Checking);廣播給上層,正在檢查SD卡,為掛載做准備。

14. Fat::check();SD卡檢查方法,檢查SD卡是否是FAT格式。

15. Fat::doMount()掛載SD卡。

至此,SD的掛載已算初步完成,接下來應該將SD卡掛載後的消息發送給上層,在13中也提到過,在掛載以及檢查的過程中其實也有發送消息給上層的。

16. MountService的構造函數中會開啟監聽線程,用於監聽來自vold的socket信息。

Thread thread = new Thread(mConnector,VOLD_TAG); thread.start();

17. mConnector是NativeDaemonConnector的對象,NativeDaemonConnector繼承了Runnable並Override了run方法。在run方法中通過一個while(true)調用ListenToSocket()方法來實現實時監聽。

18. 在ListenToSocket()中,首先建立與Vold通信的Socket Server端,然後調用MountService中的onDaemonConnected()方法。(PS:Java與Native通信可以通過JNI,那麼Native與Java通信就需要通過Socket來實現了。Android中Native與Frameworks通信 這篇文章中有簡介,感興趣的朋友可以參考一下)

19. onDaemonConnected()方法是在接口INativeDaemonConnectorCallbacks中定義的,MountService實現了該接口並Override了onDaemonConnected()方法。該方法開啟一個線程用於更新外置存儲設備的狀態,主要更新狀態的方法也在其中實現。

20. 然後回到ListenToSocket中,通過inputStream來獲取Vold傳遞來的event,並存放在隊列中。

21. 然後這些event會在onDaemonConnected()通過隊列的”隊列.take()”方法取出。並根據不同的event調用updatePublicVolumeState()方法,在該方法中調用packageManagerService中的updateExteralState()方法來更新存儲設備的狀態。(注:這裡不太理解packageManagerService中的unloadAllContainers(args)方法)

22. 更新是通過packageHelper.getMountService().finishMediaUpdate()方法來實現的。

23. 在updatePublicVolumeState()方法中,更新後會執行如下代碼:

bl.mListener.onStorageStateChanged();

在Android源碼/packages/apps/Settings/src/com.android.settings.deviceinfo/Memory.java代碼中,實現了StorageEventListener 的匿名內部類,並Override了onStorageStateChanged();方法。因此在updatePublicVolumeState()中調用onStorageStateChanged();方法後,Memory.java中也會收到。在Memory.java中收到以後會在Setting界面進行更新,系統設置——存儲中會更新SD卡的狀態。從而SD卡的掛載從底層到達了上層。

錨點

四、Vold錨點

1. Vold簡介

Vold的全稱是volume daemon。主要負責系統對大容量存儲設備(USB/SD)的掛載/卸載任務,它是一個守護進程,該進程支持這些存儲外設的熱插拔。自Android 2.2開始,Vold升級為vold 2.0,配置文件路徑在Android 4.0之後變為/etc/vold.fstab。

錨點
2.Vold工作流程

Vold的工作流程大致可以分為三個部分:創建監聽、引導、事件處理。

(1)創建監聽

創建監聽指的是創建監聽鏈接,一方面用於監聽來自內核的uevent,另一方面用於監聽來自上層的控制命令,這些命令包括控制SD卡的掛載與卸載,這裡所說的鏈接也就是socket。在Android 系統啟動的時候,init進程會去解析init.rc文件,在該文件中,有如下代碼:

Service vold /system/bin/vold

Socket vold stream 0660 root mount

Iprio be 2

這樣系統會在啟動的時候創建與上層通信的socket,此socket name為"vold"。

在Android 4.0源碼/system/vold路徑下的main.cpp中創建了與內核通信的socket。在main.cpp中通過實例化VolumeManager和NetlinkManager時創建。

(2)引導

Vold進程啟動時候會對現有的外部存儲設備進行檢查。首先加載並解析vold.fstab,並檢查掛載點是否已被掛載。然後執行SD卡的掛載,最後處理USB大容量存儲。因為系統是按行解析的,通過查看vold.fstab可以很清楚的知道這一點。

vold.fatab中最重要的語句:

dev_mount sdcard /mnt/sdcard auto /devices/platform/rk29_sdmmc.0/mmc_host/mmc0

dev_mount

掛載命令 標簽 掛載點第幾個分區 設備的sysfs paths

注:

第幾個分區:如果為auto則表示第1個分區。

參數之間不能有空格,只能以tab為間隔(注意:這裡為了對齊因此采用空格隔開,如果自行修改vold.fstab之後加以空格的話系統會識別不到的)。

如果vold.fstab解析無誤,VolueManager將創建DirectVolume,若vold.fstab解析不存在或者打開失敗,Vold將會讀取Linux內核中的參數,此時如果參數中存在SDCARD(也就是SD的默認路徑),VolumeManager則會創建AutoVolume,如果不存在這個默認路徑那麼就不會創建。

(3)事件處理

通過對兩個socket的監聽,完成對事件的處理以及對上層應用的響應。

a) Kernel發出uevent

NetlinkManager檢測到kernel發出的uevent,解析後調用NetlinkHandler::onEvent()方法。該方法會分別處理不同的事件,這裡重要的事件有:

“block”事件主要指Volume的mount、unmount、createAsec等。由VolumeManager的handleBlockEvent(evt)來處理,根據多態性最終將會調用AutoVolume或者DirectVolume的handleBlockEvent方法來處理。

“switch”事件主要指Volume的connet、disconnet等。根據相關操作,改變設備參數(設備類型、掛載點等)通過CommandListener告知FrameWork層。

b)FrameWork發出控制命令

與a)相反,CommandListener檢測到FrameWork層的命令(MountService發出的命令)調用VolumeManager的函數,VolumeManager找出對應的Volume,調用Volume函數去掛載/卸載操作。而Volume類中的相關操作最終通過調用Linux函數完成。

五、Vold用戶態錨點

1. NetlinkManager

NetlinkManager負責與Kernel交互,通過PF_NETLINK來現。

Vlod啟動代碼如下(/system/vold/main.cpp):

intmain(){

VolumeManager*vm;

CommandListener*cl;

NetlinkManager*nm;

SLOGI("Vold2.1(therevenge)firingup");

mkdir("/dev/block/vold",0755);

/*Createoursingletonmanagers*/

if(!(vm=VolumeManager::Instance())){

SLOGE("UnabletocreateVolumeManager");

exit(1);

};

if(!(nm=NetlinkManager::Instance())){

SLOGE("UnabletocreateNetlinkManager");

exit(1);

};

cl=newCommandListener();

vm->setBroadcaster((SocketListener*)cl);

nm->setBroadcaster((SocketListener*)cl);

if(vm->start()){

SLOGE("UnabletostartVolumeManager(%s)",strerror(errno));

exit(1);

}

/*解析/etc/vold.fstab文件,

讀取type,label,mount_point,part

1)構建DirectVolume對象:如果part為auto,則調用dv=newDirectVolume(vm,label,mount_point,-1);

2)添加vold.fstab中定義的某一掛載項對應的sysfs_path到DirectVolume對象的mPaths容器dv->addPath(sysfs_path);

3)將這個DirectVolume對象添加到VolumeManager對象的容器mVolumes中vm->addVolume(dv);

*/

if(process_config(vm)){

SLOGE("Errorreadingconfiguration(%s)...continuinganyways",strerror(errno));

}

/*會調用NetlinkManager類的start()方法,它創建PF_NETLINKsocket,

並開啟線程從此socket中讀取數據*/

if(nm->start()){

SLOGE("UnabletostartNetlinkManager(%s)",strerror(errno));

exit(1);

}

#ifdefUSE_USB_MODE_SWITCH

SLOGE("StartMiscdevicesManager...");

MiscManager*mm;

if(!(mm=MiscManager::Instance())){

SLOGE("UnabletocreateMiscManager");

exit(1);

};

mm->setBroadcaster((SocketListener*)cl);

if(mm->start()){

SLOGE("UnabletostartMiscManager(%s)",strerror(errno));

exit(1);

}

G3Dev*g3=newG3Dev(mm);

g3->handleUsb();

mm->addMisc(g3);

#endif

coldboot("/sys/block");//冷啟動,vold錯過了一些uevent,重新觸發。向sysfs的uevent文件寫入”add\n”字符也可以觸發sysfs事件,相當執行了一次熱插拔。

//coldboot("/sys/class/switch");

/*

*Nowthatwe'reup,wecanrespondtocommands

*/

if(cl->startListener()){

SLOGE("UnabletostartCommandListener(%s)",strerror(errno));

exit(1);

}

//Eventuallywe'llbecomethemonitoringthread

while(1){

sleep(1000);

}

SLOGI("Voldexiting");

exit(0);

}

NetlinkManager的家族關系如下所示:

 

\

 

上圖中的虛線為啟動是的調用流程。

(1) class NetlinkManager(在其start函數中創建了NetlinkHandler對象,並把創建的socket作為參數)

(2)class NetlinkHandler: public NetlinkListener(實現了onEvent)

(3) class NetlinkListener : public SocketListener (實現了onDataAvailable)

(4) class SocketListener(實現了runListener,在一個線程中通過select查看哪些socket有數據,通過調用onDataAvailable來讀取數據)

 

錨點
錨點
2. NetlinkManager::start()

view plaincopy

intNetlinkManager::start(){

structsockaddr_nlnladdr;

intsz=64*1024;

inton=1;

memset(&nladdr,0,sizeof(nladdr));

nladdr.nl_family=AF_NETLINK;

nladdr.nl_pid=getpid();

nladdr.nl_groups=0xffffffff;

//創建一個socket用於內核空間和用戶空間的異步通信,監控系統的hotplug事件

if((mSock=socket(PF_NETLINK,

SOCK_DGRAM,NETLINK_KOBJECT_UEVENT))<0){

SLOGE("Unabletocreateueventsocket:%s",strerror(errno));

return-1;

}

if(setsockopt(mSock,SOL_SOCKET,SO_RCVBUFFORCE,&sz,sizeof(sz))<0){

SLOGE("UnabletosetueventsocketSO_RECBUFFORCEoption:%s",strerror(errno));

return-1;

}

if(setsockopt(mSock,SOL_SOCKET,SO_PASSCRED,&on,sizeof(on))<0){

SLOGE("UnabletosetueventsocketSO_PASSCREDoption:%s",strerror(errno));

return-1;

}

if(bind(mSock,(structsockaddr*)&nladdr,sizeof(nladdr))<0){

SLOGE("Unabletobindueventsocket:%s",strerror(errno));

return-1;

}

//利用新創建的socket實例化一個NetlinkHandler類對象,NetlinkHandler繼承了類NetlinkListener,

//NetlinkListener又繼承了類SocketListener

mHandler=newNetlinkHandler(mSock);

if(mHandler->start()){//啟動NetlinkHandler

SLOGE("UnabletostartNetlinkHandler:%s",strerror(errno));

return-1;

}

return0;

}

把socket作為參數創建了NetlinkHandler對象,然後啟動NetlinkHandler。

view plaincopy

intNetlinkHandler::start(){

returnthis->startListener();

}

intSocketListener::startListener(){

if(!mSocketName&&mSock==-1){

SLOGE("Failedtostartunboundlistener");

errno=EINVAL;

return-1;

}elseif(mSocketName){

if((mSock=android_get_control_socket(mSocketName))<0){

SLOGE("Obtainingfiledescriptorsocket'%s'failed:%s",

mSocketName,strerror(errno));

return-1;

}

}

if(mListen&&listen(mSock,4)<0){

SLOGE("Unabletolistenonsocket(%s)",strerror(errno));

return-1;

}elseif(!mListen)

mClients->push_back(newSocketClient(mSock,false));

if(pipe(mCtrlPipe)){

SLOGE("pipefailed(%s)",strerror(errno));

return-1;

}

if(pthread_create(&mThread,NULL,SocketListener::threadStart,this)){

SLOGE("pthread_create(%s)",strerror(errno));

return-1;

}

return0;

}

void*SocketListener::threadStart(void*obj){

SocketListener*me=reinterpret_cast(obj);

me->runListener();

pthread_exit(NULL);

returnNULL;

}

voidSocketListener::runListener(){

SocketClientCollection*pendingList=newSocketClientCollection();

while(1){//死循環,一直監聽

SocketClientCollection::iteratorit;

fd_setread_fds;

intrc=0;

intmax=-1;

FD_ZERO(&read_fds);//清空文件描述符集read_fds

if(mListen){

max=mSock;

FD_SET(mSock,&read_fds);//添加文件描述符到文件描述符集read_fds

}

FD_SET(mCtrlPipe[0],&read_fds);//添加管道的讀取端文件描述符到read_fds

if(mCtrlPipe[0]>max)

max=mCtrlPipe[0];

pthread_mutex_lock(&mClientsLock);//對容器mClients的操作需要加鎖

for(it=mClients->begin();it!=mClients->end();++it){

intfd=(*it)->getSocket();

FD_SET(fd,&read_fds);////遍歷容器mClients的所有成員,調用內聯函數getSocket()獲取文件描述符,並添加到文件描述符集read_fds

if(fd>max)

max=fd;

}

pthread_mutex_unlock(&mClientsLock);

//等待文件描述符中某一文件描述符或者說socket有數據到來

if((rc=select(max+1,&read_fds,NULL,NULL,NULL))<0){

if(errno==EINTR)

continue;

SLOGE("selectfailed(%s)",strerror(errno));

sleep(1);

continue;

}elseif(!rc)

continue;

if(FD_ISSET(mCtrlPipe[0],&read_fds))

break;

if(mListen&&FD_ISSET(mSock,&read_fds)){//監聽套接字處理

structsockaddraddr;

socklen_talen;

intc;

do{

alen=sizeof(addr);

c=accept(mSock,&addr,&alen);//接收鏈接請求,建立連接,如果成功c即為建立鏈接後的數據交換套接字,將其添加到mClient容器

}while(c<0&&errno==EINTR);

if(c<0){

SLOGE("acceptfailed(%s)",strerror(errno));

sleep(1);

continue;

}

pthread_mutex_lock(&mClientsLock);

mClients->push_back(newSocketClient(c,true));

pthread_mutex_unlock(&mClientsLock);

}

/*Addallactiveclientstothependinglistfirst*/

pendingList->clear();

pthread_mutex_lock(&mClientsLock);

for(it=mClients->begin();it!=mClients->end();++it){

intfd=(*it)->getSocket();

if(FD_ISSET(fd,&read_fds)){

pendingList->push_back(*it);

}

}

pthread_mutex_unlock(&mClientsLock);

/*Processthependinglist,sinceitisownedbythethread,

*thereisnoneedtolockit*/

while(!pendingList->empty()){//非監聽套接字處理

/*Popthefirstitemfromthelist*/

it=pendingList->begin();

SocketClient*c=*it;

pendingList->erase(it);

/*Processit,iffalseisreturnedandoursocketsare

*connection-based,removeanddestroyit*/

//******onDataAvailable在NetlinkListener中實現*********

if(!onDataAvailable(c)&&mListen){

/*Removetheclientfromourarray*/

pthread_mutex_lock(&mClientsLock);

for(it=mClients->begin();it!=mClients->end();++it){

if(*it==c){

mClients->erase(it);

break;

}

}

pthread_mutex_unlock(&mClientsLock);

/*Removeourreferencetotheclient*/

c->decRef();

}

}

}

deletependingList;

}

SocketListener::runListener是線程真正執行的函數:mListen成員用來判定是否監聽套接字,Netlink套接字屬於udp套接字,非監聽套接字,該函數的主要功能體現在,如果該套接字有數據到來,就調用函數onDataAvailable讀取數據。

 

錨點
錨點
3. NetlinkListener::onDataAvailable

 

view plaincopy

boolNetlinkListener::onDataAvailable(SocketClient*cli)

{

intsocket=cli->getSocket();

ssize_tcount;

//從socket中讀取kernel發送來的uevent消息

count=TEMP_FAILURE_RETRY(uevent_kernel_multicast_recv(socket,mBuffer,sizeof(mBuffer)));

if(count<0){

SLOGE("recvmsgfailed(%s)",strerror(errno));

returnfalse;

}

NetlinkEvent*evt=newNetlinkEvent();

if(!evt->decode(mBuffer,count,mFormat)){

SLOGE("ErrordecodingNetlinkEvent");

}else{

onEvent(evt);//在NetlinkHandler中實現

}

deleteevt;

returntrue;

}

 

錨點
錨點
4. NetlinkHandler::onEvent

 

view plaincopy

voidNetlinkHandler::onEvent(NetlinkEvent*evt){

VolumeManager*vm=VolumeManager::Instance();

constchar*subsys=evt->getSubsystem();

if(!subsys){

SLOGW("Nosubsystemfoundinnetlinkevent");

return;

}

if(!strcmp(subsys,"block")){

if(uEventOnOffFlag)

{

SLOGW("####netlinkeventblock####");

evt->dump();

}

vm->handleBlockEvent(evt);

#ifdefUSE_USB_MODE_SWITCH

}elseif(!strcmp(subsys,"usb")

||!strcmp(subsys,"scsi_device")){

SLOGW("subsystemfoundinnetlinkevent");

MiscManager*mm=MiscManager::Instance();

mm->handleEvent(evt);

#endif

}

}

 

錨點
錨點
5. uevent_kernel_multicast_recv

 

view plaincopy

/**

*Likerecv(),butchecksthatmessagesactuallyoriginatefromthekernel.

*/

ssize_tuevent_kernel_multicast_recv(intsocket,void*buffer,size_tlength){

structioveciov={buffer,length};

structsockaddr_nladdr;

charcontrol[CMSG_SPACE(sizeof(structucred))];

structmsghdrhdr={

&addr,

sizeof(addr),

&iov,

1,

control,

sizeof(control),

0,

};

ssize_tn=recvmsg(socket,&hdr,0);

if(n<=0){

returnn;

}

if(addr.nl_groups==0||addr.nl_pid!=0){

/*ignoringnon-kernelorunicastnetlinkmessage*/

gotoout;

}

structcmsghdr*cmsg=CMSG_FIRSTHDR(&hdr);

if(cmsg==NULL||cmsg->cmsg_type!=SCM_CREDENTIALS){

/*ignoringnetlinkmessagewithnosendercredentials*/

gotoout;

}

structucred*cred=(structucred*)CMSG_DATA(cmsg);

if(cred->uid!=0){

/*ignoringnetlinkmessagefromnon-rootuser*/

gotoout;

}

returnn;

out:

/*clearresidualpotentiallymaliciousdata*/

bzero(buffer,length);

errno=EIO;

return-1;

}

 

錨點
錨點

 

六、與Vold相關的Kernel態

用戶態創建的netlink sock被kernel保存在:nl_table[sk->sk_protocol].mc_listKernel態創建的netlink sock被kernel保存在:uevent_sock_list,上面的sk->sk_protocol為uevent_sock_list的協議, 二者只有協議一致才可以發送。

 

錨點
錨點
1. 創建kernel態sock

 

在用戶態的socket創建方式(/system/vold/NetlinkManager.cpp):

view plaincopy

if((mSock=socket(PF_NETLINK,

SOCK_DGRAM,NETLINK_KOBJECT_UEVENT))<0){

SLOGE("Unabletocreateueventsocket:%s",strerror(errno));

return-1;

}

在Kernel的socket創建方式(/kernel/lib/kobject_uevent.c):view plaincopy

staticintuevent_net_init(structnet*net)

{

structuevent_sock*ue_sk;

ue_sk=kzalloc(sizeof(*ue_sk),GFP_KERNEL);

if(!ue_sk)

return-ENOMEM;

ue_sk->sk=netlink_kernel_create(net,NETLINK_KOBJECT_UEVENT,

1,NULL,NULL,THIS_MODULE);

if(!ue_sk->sk){

printk(KERN_ERR

"kobject_uevent:unabletocreatenetlinksocket!\n");

kfree(ue_sk);

return-ENODEV;

}

mutex_lock(&uevent_sock_mutex);

list_add_tail(&ue_sk->list,&uevent_sock_list);

mutex_unlock(&uevent_sock_mutex);

return0;

}

從上面的代碼可知,此sock被創建之後,被增加到全局變量uevent_sock_list列表中,下面的分析圍繞此列表進行。

netlink_kernel_create函數原型:view plaincopy

structsock*netlink_kernel_create(structnet*net,intunit,unsignedintgroups,

void(*input)(structsk_buff*skb),

structmutex*cb_mutex,structmodule*module)

1)struct net *net:是一個網絡名字空間namespace,在不同的名字空間裡面可以有自己的轉發信息庫,有自己的一套net_device等等。默認情況下都是使用init_net這個全局變量

2)int unit:表示netlink協議類型,如 NETLINK_KOBJECT_UEVENT

3)unsigned int groups: 組類型

4)void (*input)(struct sk_buff *skb):參數input則為內核模塊定義的netlink消息處理函數,當有消息到達這個netlink socket時,該input函數指針就會被調用。函數指針input的參數skb實際上就是函數netlink_kernel_create返回的 struct sock指針,sock實際是socket的一個內核表示數據結構,用戶態應用創建的socket在內核中也會有一個struct sock結構來表示。

5) struct mutex *cb_mutex: 互斥銷

6) struct module *module: 一般為THIS_MODULE

struct sock

用戶態socket在kernel中的表示。

 

錨點
錨點
2.相關數據結構

 

相關數據結構如下圖所示:

 

\

 

 

錨點
錨點
3.發送消息給用戶空間

 

 

錨點
錨點
3.1 發送消息流程圖

 

 

\

 

 

錨點
錨點
3.2 kobject_uevent_env

 

view plaincopy

/**

*kobject_uevent_env-sendanueventwithenvironmentaldata

*

*@action:actionthatishappening

*@kobj:structkobjectthattheactionishappeningto

*@envp_ext:pointertoenvironmentaldata

*

*Returns0ifkobject_uevent_env()iscompletedwithsuccessorthe

*correspondingerrorwhenitfails.

*/

intkobject_uevent_env(structkobject*kobj,enumkobject_actionaction,

char*envp_ext[])

{

structkobj_uevent_env*env;

constchar*action_string=kobject_actions[action];

constchar*devpath=NULL;

constchar*subsystem;

structkobject*top_kobj;

structkset*kset;

conststructkset_uevent_ops*uevent_ops;

u64seq;

inti=0;

intretval=0;

#ifdefCONFIG_NET

structuevent_sock*ue_sk;

#endif

pr_debug("kobject:'%s'(%p):%s\n",

kobject_name(kobj),kobj,__func__);

/*searchtheksetwebelongto*/

top_kobj=kobj;

while(!top_kobj->kset&&top_kobj->parent)

top_kobj=top_kobj->parent;

if(!top_kobj->kset){

pr_debug("kobject:'%s'(%p):%s:attemptedtosenduevent"

"withoutkset!\n",kobject_name(kobj),kobj,

__func__);

return-EINVAL;

}

kset=top_kobj->kset;

uevent_ops=kset->uevent_ops;

/*skiptheevent,ifuevent_suppressisset*/

if(kobj->uevent_suppress){

pr_debug("kobject:'%s'(%p):%s:uevent_suppress"

"causedtheeventtodrop!\n",

kobject_name(kobj),kobj,__func__);

return0;

}

/*skiptheevent,ifthefilterreturnszero.*/

if(uevent_ops&&uevent_ops->filter)

if(!uevent_ops->filter(kset,kobj)){

pr_debug("kobject:'%s'(%p):%s:filterfunction"

"causedtheeventtodrop!\n",

kobject_name(kobj),kobj,__func__);

return0;

}

/*originatingsubsystem*/

if(uevent_ops&&uevent_ops->name)

subsystem=uevent_ops->name(kset,kobj);

else

subsystem=kobject_name(&kset->kobj);

if(!subsystem){

pr_debug("kobject:'%s'(%p):%s:unsetsubsystemcausedthe"

"eventtodrop!\n",kobject_name(kobj),kobj,

__func__);

return0;

}

/*environmentbuffer*/

env=kzalloc(sizeof(structkobj_uevent_env),GFP_KERNEL);

if(!env)

return-ENOMEM;

/*completeobjectpath*/

devpath=kobject_get_path(kobj,GFP_KERNEL);

if(!devpath){

retval=-ENOENT;

gotoexit;

}

/*defaultkeys*/

retval=add_uevent_var(env,"ACTION=%s",action_string);

if(retval)

gotoexit;

retval=add_uevent_var(env,"DEVPATH=%s",devpath);

if(retval)

gotoexit;

retval=add_uevent_var(env,"SUBSYSTEM=%s",subsystem);

if(retval)

gotoexit;

/*keyspassedinfromthecaller*/

if(envp_ext){

for(i=0;envp_ext[i];i++){

retval=add_uevent_var(env,"%s",envp_ext[i]);

if(retval)

gotoexit;

}

}

/*lettheksetspecificfunctionadditsstuff*/

if(uevent_ops&&uevent_ops->uevent){

retval=uevent_ops->uevent(kset,kobj,env);

if(retval){

pr_debug("kobject:'%s'(%p):%s:uevent()returned"

"%d\n",kobject_name(kobj),kobj,

__func__,retval);

gotoexit;

}

}

/*

*Mark"add"and"remove"eventsintheobjecttoensureproper

*eventstouserspaceduringautomaticcleanup.Iftheobjectdid

*sendan"add"event,"remove"willautomaticallygeneratedby

*thecore,ifnotalreadydonebythecaller.

*/

if(action==KOBJ_ADD)

kobj->state_add_uevent_sent=1;

elseif(action==KOBJ_REMOVE)

kobj->state_remove_uevent_sent=1;

/*wewillsendanevent,sorequestanewsequencenumber*/

spin_lock(&sequence_lock);

seq=++uevent_seqnum;

spin_unlock(&sequence_lock);

retval=add_uevent_var(env,"SEQNUM=%llu",(unsignedlonglong)seq);

if(retval)

gotoexit;

#ifdefined(CONFIG_NET)

/*sendnetlinkmessage*/

mutex_lock(&uevent_sock_mutex);

list_for_each_entry(ue_sk,&uevent_sock_list,list){

structsock*uevent_sock=ue_sk->sk;

structsk_buff*skb;

size_tlen;

/*allocatemessagewiththemaximumpossiblesize*/

len=strlen(action_string)+strlen(devpath)+2;

skb=alloc_skb(len+env->buflen,GFP_KERNEL);

if(skb){

char*scratch;

/*addheader*/

scratch=skb_put(skb,len);

sprintf(scratch,"%s@%s",action_string,devpath);//action_string+devpath

/*copykeystoourcontinuouseventpayloadbuffer*/

for(i=0;ienvp_idx;i++){

len=strlen(env->envp[i])+1;

scratch=skb_put(skb,len);

strcpy(scratch,env->envp[i]);

}

NETLINK_CB(skb).dst_group=1;

retval=netlink_broadcast_filtered(uevent_sock,skb,

0,1,GFP_KERNEL,

kobj_bcast_filter,

kobj);

/*ENOBUFSshouldbehandledinuserspace*/

if(retval==-ENOBUFS)

retval=0;

}else

retval=-ENOMEM;

}

mutex_unlock(&uevent_sock_mutex);

#endif

/*calluevent_helper,usuallyonlyenabledduringearlyboot*/

if(uevent_helper[0]&&!kobj_usermode_filter(kobj)){

char*argv[3];

argv[0]=uevent_helper;

argv[1]=(char*)subsystem;

argv[2]=NULL;

retval=add_uevent_var(env,"HOME=/");

if(retval)

gotoexit;

retval=add_uevent_var(env,

"PATH=/sbin:/bin:/usr/sbin:/usr/bin");

if(retval)

gotoexit;

retval=call_usermodehelper(argv[0],argv,

env->envp,UMH_WAIT_EXEC);

}

exit:

kfree(devpath);

kfree(env);

returnretval;

}

view plaincopy

/**

*kobject_uevent-notifyuserspacebysendinganuevent

*

*@action:actionthatishappening

*@kobj:structkobjectthattheactionishappeningto

*

*Returns0ifkobject_uevent()iscompletedwithsuccessorthe

*correspondingerrorwhenitfails.

*/

intkobject_uevent(structkobject*kobj,enumkobject_actionaction)

{

returnkobject_uevent_env(kobj,action,NULL);

}

 

錨點
錨點

 

3.3 netlink_broadcast_filtered

view plaincopy

intnetlink_broadcast_filtered(structsock*ssk,structsk_buff*skb,u32pid,

u32group,gfp_tallocation,

int(*filter)(structsock*dsk,structsk_buff*skb,void*data),

void*filter_data)

{

structnet*net=sock_net(ssk);

structnetlink_broadcast_datainfo;

structhlist_node*node;

structsock*sk;

skb=netlink_trim(skb,allocation);

info.exclude_sk=ssk;

info.net=net;

info.pid=pid;

info.group=group;

info.failure=0;

info.delivery_failure=0;

info.congested=0;

info.delivered=0;

info.allocation=allocation;

info.skb=skb;

info.skb2=NULL;

info.tx_filter=filter;

info.tx_data=filter_data;

/*Whilewesleepinclone,donotallowtochangesocketlist*/

netlink_lock_table();

//向nl_table[ssk->sk_protocol].mc_list中的每個sock發送此netlink消息

sk_for_each_bound(sk,node,&nl_table[ssk->sk_protocol].mc_list)

do_one_broadcast(sk,&info);

consume_skb(skb);

netlink_unlock_table();

if(info.delivery_failure){

kfree_skb(info.skb2);

return-ENOBUFS;

}else

consume_skb(info.skb2);

if(info.delivered){

if(info.congested&&(allocation&__GFP_WAIT))

yield();

return0;

}

return-ESRCH;

}

static struct netlink_table *nl_table;是全局變量,它維護了用戶態創建的所有netlink sock,按協議分類,每種協議一個鏈表mc_list。它在函數netlink_proto_init中被初始化,向nl_table[sk->sk_protocol].mc_list中增加sock的調用流程如下(kernel/net/netlink/af_netlink.c):

 

\

 

 

錨點
錨點

 

3.4 do_one_broadcast

view plaincopy

staticinlineintdo_one_broadcast(structsock*sk,

structnetlink_broadcast_data*p)

{

structnetlink_sock*nlk=nlk_sk(sk);

intval;

if(p->exclude_sk==sk)

gotoout;

if(nlk->pid==p->pid||p->group-1>=nlk->ngroups||

!test_bit(p->group-1,nlk->groups))

gotoout;

if(!net_eq(sock_net(sk),p->net))

gotoout;

if(p->failure){

netlink_overrun(sk);

gotoout;

}

sock_hold(sk);

if(p->skb2==NULL){

if(skb_shared(p->skb)){

p->skb2=skb_clone(p->skb,p->allocation);

}else{

p->skb2=skb_get(p->skb);

/*

*skbownershipmayhavebeensetwhen

*deliveredtoaprevioussocket.

*/

skb_orphan(p->skb2);

}

}

if(p->skb2==NULL){

netlink_overrun(sk);

/*Clonefailed.NotifyALLlisteners.*/

p->failure=1;

if(nlk->flags&NETLINK_BROADCAST_SEND_ERROR)

p->delivery_failure=1;

}elseif(p->tx_filter&&p->tx_filter(sk,p->skb2,p->tx_data)){

kfree_skb(p->skb2);

p->skb2=NULL;

}elseif(sk_filter(sk,p->skb2)){

kfree_skb(p->skb2);

p->skb2=NULL;

}elseif((val=netlink_broadcast_deliver(sk,p->skb2))<0){

netlink_overrun(sk);

if(nlk->flags&NETLINK_BROADCAST_SEND_ERROR)

p->delivery_failure=1;

}else{

p->congested|=val;

p->delivered=1;

p->skb2=NULL;

}

sock_put(sk);

out:

return0;

}

 

錨點
錨點

 

3.5 netlink_broadcast_deliver

view plaincopy

staticinlineintnetlink_broadcast_deliver(structsock*sk,

structsk_buff*skb)

{

structnetlink_sock*nlk=nlk_sk(sk);

if(atomic_read(&sk->sk_rmem_alloc)<=sk->sk_rcvbuf&&

!test_bit(0,&nlk->state)){

skb_set_owner_r(skb,sk);

skb_queue_tail(&sk->sk_receive_queue,skb);

sk->sk_data_ready(sk,skb->len);

returnatomic_read(&sk->sk_rmem_alloc)>sk->sk_rcvbuf;

}

return-1;

}

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved