clients[i] =NullClient;
serverClient =xalloc(sizeof(ClientRec));
if (!serverClient)
FatalError("couldn't createserver client");
InitClient(serverClient, 0,(pointer)NULL);
}
else
ResetWellKnownSockets();
当第一次循环时serverGeneration=1,执行的是第一个分支代码。
CreateWellKnownSockets() 初始化一系列sockets监听是否有clients申请连接。
InitProcVectors() 初始化ProcVector,SwappedProcVector结构
for循环是生成并初始化clients数组
之后便是serverClient变量的生成即初始化,serverClient是clients数组中索引为0的项,因为他是拥有rootwindow的client。
当之后的循环时serverGeneration =0,执行的是ResetWellKnownSockets即重置sockets工作。
(2)
InitOutput()是初始化分量较中的一环,处理过程可以分为如下部分:
1)xf86HandleConfigFile 解析xorg.con文件 ,获得xserver的配置信息。
2)xf86BusProbe 获得video的pci信息,例如framebuffer地址等。
3)DoConfigure() 根据配置文件 ,或者传进来的参数做相应的配置
4)xf86LoadModules load xorg.conf中配置的一系列模块
5)以此遍历注册的各个driver,调用其identify,probe函数,这样就根据显卡的型号加载了相应的驱动
6)匹配screen,主要是根据xorg.conf中配置的screen,查询是否有与其匹配的device
7)遍历screen,调用其匹配device驱动的PreInit函数。这样就完成了显卡驱动的预初始化
8)遍历screen,调用AddScreen函数,分配screenInfo.screen[]的一项,并做初始化ScreenInit.这样驱动的初始化基本完成。
(3)
InitInput()是初始化输入设备,例如键盘和鼠标等。如果xorg.conf中有SectionInputDevice配置,会按照
其配置扫描加载设备
b)xserver循环处理client消息
在初始化结束之后xserver便进入了循环处理阶段即
Dispatch()函数
该函数的流程主要是一个循环结构
while (!dispatchException)
即当不出现异常时循环会不断进行下去
每一次循环可以分为如下部分
(1)接受用户的输入,并发送给client
if (*icheck[0] !=*icheck[1])
{
ProcessInputEvents();
FlushIfCriticalOutputPending();
}
(2)等待clients发送事件过来
nready= WaitForSomething(clientReady);
(3)遍历每个发送信息的client,做如下处理
1)接受用户输入并发送
if (*icheck[0] !=*icheck[1])
ProcessInputEvents();
FlushIfCriticalOutputPending();
2)获得client的请求号
result =ReadRequestFromClient(client);
3)根据请求号调用队列中相应的处理函数
if (result >(maxBigRequestSize << 2))
result = BadLength;
else {
result = XaceHookDispatch(client, MAJOROP);
if (result == Success)
result = (*client->requestVector[MAJOROP])(client);
XaceHookAuditEnd(client, result);
}
4)若处理函数返回异常则做异常处理
if (result != Success)
{
if (client->noClientException !=Success)
CloseDownClient(client);
else
SendErrorToClient(client, MAJOROP,
MinorOpcodeOfRequest(client),
client->errorValue, result);
break;
}
}
5)提交处理结果
FlushAllOutput();
由此Dispatch函数解析结束
c)xserver退出
包含了一系列释放内存,关闭clients等操作,这里就不多做解析。
5.xserver,xclient协议简介
由上文对Dispatch函数的分析可以看出,xserver对client的处理主要是三步:
(1)获得事件信息
nready =WaitForSomething(clientReady);
(2)获得操作号
result =ReadRequestFromClient(client);
(3)根据操作号处理
result = (*client->requestVector[MAJOROP])(client);
因此其操作号和操作的对应是xserver与client的协议的一部分,类似操作
系统的系统调用号和系统调用之间的关系。
在上面介绍InitClients()中有对requestVector初始化
client->requestVector = InitialVector;
InitVector如下:
int (* InitialVector[3]) (
ClientPtr
) =
{
0,
ProcInitialConnection,
ProcEstablishConnection
};
其只有两个函数,一个是初始化Connection,一个是确立Connection
在ProcEstablishConnection中调用SendConnSetup寒酸,
SendConnSetup函数有:
client->requestVector =client->swapped ? SwappedProcVector :ProcVector;
即初始化requestVector为SwappedProcVector或ProcVector
ProcVector如下:
_X_EXPORT int (* ProcVector[256]) (
ClientPtr
)=
{
ProcBadRequest,
ProcCreateWindow,
ProcChangeWindowAttributes,
ProcGetWindowAttributes,
ProcDestroyWindow,
ProcDestroySubwindows,
ProcChangeSaveSet,
ProcReparentWindow,
ProcMapWindow,
ProcMapSubwindows,
ProcUnmapWindow,
。。。。。。。。。。。。。
ProcGetModifierMapping,
0,
0,
0,
0,
0,
0,
0,
ProcNoOperation
};
SwappedProcVector类似。
也就是说Client与server交互时,先按照固定的协议初始化Connector,并且告诉xserver其适合的协议。
然后server按照该协议解析client发送过来的操作号。
6.一个基于Xlib的简单例子了解Client流程
Xlib是对X协议的的一个简单的封装,可以让程序员不用了解细节而编写图形相关程序。实际上程序员直接调用Xlib的很少,更多使用的是
GTK+ ,QT等图形库。这些又是基于Xlib的图形库。
一个简单的Xlib例子如下
#include
#include
#include
#include
int main(void) {
Display *d;
Window w;
XEvent e;
char *msg = "Hello,World!";
int s;
d =XOpenDisplay(NULL);
if (d == NULL) {
fprintf(stderr, "Cannot open display\n");
exit(1);
}
s =DefaultScreen(d);
w =XCreateSimpleWindow(d, RootWindow(d, s), 10, 10, 100, 100, 1,
BlackPixel(d, s),WhitePixel(d, s));
XSelectInput(d, w,ExposureMask | KeyPressMask);
XMapWindow(d, w);
while (1) {
XNextEvent(d, &e);
if(e.type == Expose) {
XFillRectangle(d, w, DefaultGC(d, s), 20, 20,10, 10);
XDrawString(d, w, DefaultGC(d, s), 50, 50, msg,strlen(msg));
}
if(e.type == KeyPress)
break;
}
XCloseDisplay(d);
return 0;
}
这个程序就可以看作一个简单的client,包含client的大体流程。
编译: gcc input.c -o output -lX11
程序执行方式有两种:
1.在图形界面下直接执行程序
2.在用户目录下新建一个.xinitrc文件,写入
exec input
之后startx,执行的不是默认的图形界面程序而是input程序
7.radeon驱动初始化代码解析.
由上面对xserver初始化的介绍,可以看到,在初始化过程中主要是显卡驱动的三个函数的调用
Probe , PreInit , ScreenInit
以下以radeon驱动为例(xorg-xserver-video-ati-6.13.1),介绍驱动对显卡的初始化过程,以及图形加速中使用的函数。
(1)Probe函数
在radeon驱动中,probe函数主要是
static Bool
radeon_pci_probe(
DriverPtr pDriver,
int entity_num,
structpci_device *device,
intptr_t match_data
)
{
returnradeon_get_scrninfo(entity_num, (void *)device);
}
在radeon_get_scrninfo函数中有:主要是对pScrn和pENT的初始化。
在pScrn的初始化中给出了将要调用的PreInit 和ScreenInit函数
#ifdef XF86DRM_MODE
if (kms== 1) {
pScrn->PreInit = RADEONPreInit_KMS;
pScrn->ScreenInit = RADEONScreenInit_KMS;
pScrn->SwitchMode = RADEONSwitchMode_KMS;
pScrn->AdjustFrame = RADEONAdjustFrame_KMS;
pScrn->EnterVT = RADEONEnterVT_KMS;
pScrn->LeaveVT = RADEONLeaveVT_KMS;
pScrn->FreeScreen = RADEONFreeScreen_KMS;
pScrn->ValidMode = RADEONValidMode;
}else
#endif
{
pScrn->PreInit = RADEONPreInit;
pScrn->ScreenInit = RADEONScreenInit;
pScrn->SwitchMode = RADEONSwitchMode;
pScrn->AdjustFrame = RADEONAdjustFrame;
pScrn->EnterVT = RADEONEnterVT;
pScrn->LeaveVT = RADEONLeaveVT;
pScrn->FreeScreen = RADEONFreeScreen;
pScrn->ValidMode = RADEONValidMode;
}
不妨已RADEONPreInit_KMS ,RADEONScreenInit_KMS为例介绍驱动PreInit和ScreenInit过程
(2)PreInit
RADEONPreInit_KMS在结构上大体可以分为三个部分(虽然不严格),
a)pScrn->driverPrivate的初始化
例如:
info = RADEONPTR(pScrn);
info->pEnt =xf86GetEntityInfo(pScrn->entityList[pScrn->numEntities- 1]);
f (!radeon_alloc_dri(pScrn))
returnFALSE;
其实对pScrn->driverPrivate的初始化贯穿了整个PreInit,但是在前面比较集中。
b)drm的初始化
radeon_open_drm_master(pScrn)
调用drmOpen打开内核drm设备
drmmode_pre_init(pScrn,&info->drmmode,pScrn->bitsPerPixel / 8)
drmCommandWriteRead(info->dri->drmFD,DRM_RADEON_GEM_INFO, &mminfo, sizeof(mminfo))
等做其他方面的初始化
c)一些相关模块的load
例如:
xf86LoadSubModule(pScrn, "fb")
load framebuffer相关的so
!xf86LoadSubModule(pScrn, "ramdac")
load 与光标显示相关模块
RADEONPreInitAccel_KMS(pScrn)
根据加速方式选择决定load shadowfb 还是exa模块
细节很多大体上可以分这三个部分理解
(3)ScreenInit
RADEONScreenInit_KMS要比RADEONPreInit_KMS杂乱
但也可以看作如下几个部分
a)对pScrn->driverPrivate的比较集中的初始化
例如:
info->bufmgr =radeon_bo_manager_gem_ctor(info->dri->drmFD);
info->cs =radeon_cs_create(info->csm,RADEON_BUFFER_SIZE/4);
等比较明显的
以及
radeon_setup_kernel_mem(pScreen);
初始化地址映射相关的info信息
b)fbScreenInit
初始化framebuffer信息
c) 显示图像像素相关的初始化及fbPictureInit
例如:
if(pScrn->bitsPerPixel > 8) {
VisualPtr visual;
visual =pScreen->visuals +pScreen->numVisuals;
while (--visual>= pScreen->visuals) {
if ((visual->class | DynamicClass)== DirectColor) {
visual->offsetRed = pScrn->offset.red;
visual->offsetGreen =pScrn->offset.green;
visual->offsetBlue = pScrn->offset.blue;
visual->redMask = pScrn->mask.red;
visual->greenMask = pScrn->mask.green;
visual->blueMask =pScrn->mask.blue;
}
}
}
fbPictureInit (pScreen, 0, 0);
#ifdef RENDER
if ((s= xf86GetOptValString(info->Options,OPTION_SUBPIXEL_ORDER))) {
if (strcmp(s, "RGB") == 0)subPixelOrder = SubPixelHorizontalRGB;
else if (strcmp(s, "BGR") ==0) subPixelOrder = SubPixelHorizontalBGR;
else if (strcmp(s, "NONE") ==0) subPixelOrder = SubPixelNone;
PictureSetSubpixelOrder(pScreen, subPixelOrder);
}
#endif
这部分是fbPictureInit和对像素RGB顺序的初始化
d)BackStore相关的初始化
例如:
xf86DrvMsgVerb(pScrn->scrnIndex,X_INFO, RADEON_LOGLEVEL_DEBUG,
"Initializing backing store\n");
miInitializeBackingStore(pScreen);
xf86SetBackingStore(pScreen);
e)加速函数相关的初始化
例如:
if(info->r600_shadow_fb) {
xf86DrvMsg(scrnIndex, X_INFO,"Acceleration disabled\n");
info->accelOn =FALSE;
} else{
xf86DrvMsgVerb(pScrn->scrnIndex,X_INFO, RADEON_LOGLEVEL_DEBUG,
"Initializing Acceleration\n");
if (RADEONAccelInit(pScreen)){
xf86DrvMsg(scrnIndex, X_INFO, "Accelerationenabled\n");
info->accelOn = TRUE;
} else {
xf86DrvMsg(scrnIndex, X_ERROR,
"Acceleration initialization failed\n");
xf86DrvMsg(scrnIndex, X_INFO, "Accelerationdisabled\n");
info->accelOn = FALSE;
}
}
中的RADEONAccelInit(pScreen)函数
下面会对RADEONAccelInit(pScreen)函数做仔细的分析
f)光标显示相关的初始化
例如:
xf86DrvMsgVerb(pScrn->scrnIndex,X_INFO, RADEON_LOGLEVEL_DEBUG,
"Initializing DPMS\n");
xf86DPMSInit(pScreen, xf86DPMSSet, 0);
xf86DrvMsgVerb(pScrn->scrnIndex,X_INFO, RADEON_LOGLEVEL_DEBUG,
"Initializing Cursor\n");
xf86SetSilkenMouse(pScreen);
miDCInitialize(pScreen,xf86GetPointerScreenFuncs());
if(!xf86ReturnOptValBool(info->Options,OPTION_SW_CURSOR, FALSE)) {
if(RADEONCursorInit_KMS(pScreen)) {
}
}
其中xf86ReturnOptValBool(info->Options,OPTION_SW_CURSOR, FALSE)的判断决定对光标显示是否使用硬件加速
g)其他的初始化
例如CloseScreen,BlockHandler 等变量赋值
Crtc初始化xf86CrtcScreenInit (pScreen)
和colormap相关的drmmode_setup_colormap(pScreen, pScrn)。
(4)RADEONAccelInit
需要重点介绍的是RADEONAccelInit函数,因为在这个函数中引入了初始化图像加速相关的函数
以笔者调试过的RS780为例:
其调用的图形加速相关的初始化是R600DrawInit(pScreen)函数,因为驱动不支持RS780的xaa加速,而软件加速shodowfb效果不好,必须使用exa加速。
R600DrawInit()函数中包含了众多加速函数的初始化其中最重要的是如下5系列函数
a)Solid相关的函数
info->accel_state->exa->PrepareSolid= R600PrepareSolid;
info->accel_state->exa->Solid= R600Solid;
info->accel_state->exa->DoneSolid= R600DoneSolid;
Solid即是向某一区域填充色的操作
b)Copy相关的函数
info->accel_state->exa->PrepareCopy= R600PrepareCopy;
info->accel_state->exa->Copy= R600Copy;
info->accel_state->exa->DoneCopy= R600DoneCopy;
Copy是不同区域直接拷贝的函数
c)Composite函数
info->accel_state->exa->CheckComposite= R600CheckComposite;
info->accel_state->exa->PrepareComposite= R600PrepareComposite;
info->accel_state->exa->Composite= R600Composite;
info->accel_state->exa->DoneComposite= R600DoneComposite;
Composite是不同窗口组合在一起的操作
d)UploadToScreen函数
info->accel_state->exa->UploadToScreen= R600UploadToScreenCS;
UploadToScreen是向framebuffer拷贝矩形域数据的函数
e)DownloadFromScreen函数
info->accel_state->exa->DownloadFromScreen= R600DownloadFromScreenCS;
DownloadFromScreen是从framebuffer拷贝出矩形域数据的函数
至此radeon驱动初始化相关的内容做了一次简单的浏览。