編輯:關於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卡狀態。
整個過程從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的全稱是volume daemon。主要負責系統對大容量存儲設備(USB/SD)的掛載/卸載任務,它是一個守護進程,該進程支持這些存儲外設的熱插拔。自Android 2.2開始,Vold升級為vold 2.0,配置文件路徑在Android 4.0之後變為/etc/vold.fstab。
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函數完成。
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來讀取數據)
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讀取數據。
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;
}
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
}
}
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的協議, 二者只有協議一致才可以發送。
在用戶態的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中的表示。
相關數據結構如下圖所示:
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;
}
Android應用程序窗口的繪圖表面在創建完成之後,我們就可以從上到下地繪制它裡面的各個視圖了,即各個UI元素了。不過在繪制這些UI元素之前,我們還需要從上到下地測量它們
Android——滑動屏幕監聽+ Palette獲取圖片中的顏色 滑動屏幕監聽——音量+亮度的調整package
微信團隊推送了一條消息,大家期待已久的微信公眾平台手機版正式上線,當然目前還僅限HTML5的操作,暫無客戶端下載。那麼對於要運營微信公眾號的人來說,這個微信
學習知識:界面組成、事件監聽器====界面組成====1.用戶界面的基本組件叫做View,都是繼承android.view.View類,Android裡面預定義很多基本的