我们知道,一般C语言的入口都是main,那么MiniGUIMain函数怎么成为入口呢?
在MiniGUI源码include/minigui.h中,定义了宏
- #define MiniGUIMain \
- MiniGUIAppMain (int args, const char* argv[]); \
- int main_entry (int args, const char* argv[]) \
- { \
- int iRet = 0; \
- if (InitGUI (args, argv) != 0) { \
- return 1; \
- } \
- iRet = MiniGUIAppMain (args, argv); \
- TerminateGUI (iRet); \
- return iRet; \
- } \
- int MiniGUIAppMain
其中main_entry,在linux 直接被定义为main, 但是,当_USE_MINIGUIENTRY被定义后,它被定义为minigui_entry:
- #ifdef _USE_MINIGUIENTRY
- #define main_entry minigui_entry
- int minigui_entry (int args, const char* arg[]);
- #else
- #define main_entry main
- #endif
定义minigui_entry可以用在一些RTOS系统上,如vxworks上。因为它们的入口不是从main开始的。
上面我们的程序,就被展开。可以看到,其中最最要的是InitGUI和TerminateGUI两个函数。
InitGUI函数时主要的初始化函数,我们详细分析下该函数。
InitGUI就定义在minigui.h中,它的原型是:
- MG_EXPORT int GUIAPI InitGUI (int, const char **);
InitGUI的实现,有多个版本,在线程版中,是在src/kernal/init.c中实现的。我们重点考察这一部分:(删除了一些不重要的部分)
- int GUIAPI InitGUI (int args, const char *agr[])
- {
- int step = 0;
- .......
- if (!mg_InitFixStr ()) { //初始化字符串管理内存。MiniGUI在内部实现一个字符串的内存管理器,可以加快分配速度,减少内存碎片
- fprintf (stderr, "KERNEL>InitGUI: Init Fixed String module failure!\n");
- return step;
- }
-
- step++;
-
- if (!mg_InitMisc ()) { //初始化杂项,主要是加载MiniGUI.cfg文件,后面的很多操作,都依赖于MiniGUI.cfg中的配置
- fprintf (stderr, "KERNEL>InitGUI: Initialization of misc things failure!\n");
- return step;
- }
-
- step++;
- switch (mg_InitGAL ()) { //初始化 GAL,它负责打开图形设备
- case ERR_CONFIG_FILE:
- fprintf (stderr,
- "KERNEL>InitGUI: Reading configuration failure!\n");
- return step;
-
- case ERR_NO_ENGINE:
- fprintf (stderr,
- "KERNEL>InitGUI: No graphics engine defined!\n");
- return step;
-
- case ERR_NO_MATCH:
- fprintf (stderr,
- "KERNEL>InitGUI: Can not get graphics engine information!\n");
- return step;
-
- case ERR_GFX_ENGINE:
- fprintf (stderr,
- "KERNEL>InitGUI: Can not initialize graphics engine!\n");
- return step;
- }
-
- 。。。。。
-
-
- step++;
- if (!mg_InitSystemRes ()) { //初始化内建资源管理器
- fprintf (stderr, "KERNEL>InitGUI: Can not initialize system resource!\n");
- goto failure1;
- }
-
-
- step++;
- if(!mg_InitGDI()) { //初始化GDI, 主要是字体的初始化
- fprintf (stderr, "KERNEL>InitGUI: Initialization of GDI failure!\n");
- goto failure1;
- }
-
-
- step++;
- if (!mg_InitScreenDC (__gal_screen)) {//创建和初始化主屏幕
- fprintf (stderr, "KERNEL>InitGUI: Can not initialize screen DC!\n");
- goto failure1;
- }
-
- g_rcScr.left = 0;
- g_rcScr.top = 0;
- g_rcScr.right = GetGDCapability (HDC_SCREEN_SYS, GDCAP_MAXX) + 1;
- g_rcScr.bottom = GetGDCapability (HDC_SCREEN_SYS, GDCAP_MAXY) + 1;
-
-
- step++;
- if( !mg_InitCursor() ) {//初始化光标
- fprintf (stderr, "KERNEL>InitGUI: Count not init mouse cursor!\n");
- goto failure1;
- }
-
-
- step++;
- if(!mg_InitLWEvent()) {//初始化IAL
- fprintf(stderr, "KERNEL>InitGUI: Low level event initialization failure!\n");
- goto failure1;
- }
-
-
- step++;
- if (!mg_InitLFManager ()) {//初始化Look And Feel Render管理器。这一部分是3.0独有的。
- fprintf (stderr, "KERNEL>InitGUI: Initialization of LF Manager failure!\n");
- goto failure;
- }
-
- #ifdef _MGHAVE_MENU
-
- step++;
- if (!mg_InitMenu ()) {//初始化菜单系统
- fprintf (stderr, "KERNEL>InitGUI: Init Menu module failure!\n");
- goto failure;
- }
- #endif
-
-
- step++;
- if(!mg_InitControlClass()) {//初始化控件类,这样可以使得预定义的控件都可以使用
- fprintf(stderr, "KERNEL>InitGUI: Init Control Class failure!\n");
- goto failure;
- }
-
-
- 。。。。
-
- step++;
- if (!mg_InitDesktop ()) { //创建和初始化一个桌面窗口。桌面窗口是非常重要的托管窗口,它是所有主窗口的父窗口,完成众多的消息分发和主窗口管理工作。它在一个单独线程中运行
- fprintf (stderr, "KERNEL>InitGUI: Init Desktop failure!\n");
- goto failure;
- }
-
-
- step++;
- if(!mg_InitAccel()) {
- fprintf(stderr, "KERNEL>InitGUI: Init Accelerator failure!\n");
- goto failure;
- }
-
-
- step++;
- if (!mg_InitDesktop ()) {
- fprintf (stderr, "KERNEL>InitGUI: Init Desktop failure!\n");
- goto failure;
- }
-
- step++;
- if (!mg_InitFreeQMSGList ()) {
- fprintf (stderr, "KERNEL>InitGUI: Init free QMSG list failure!\n");
- goto failure;
- }
-
-
- step++;
- if (!createThreadInfoKey ()) {
- fprintf (stderr, "KERNEL>InitGUI: Init thread hash table failure!\n");
- goto failure;
- }
-
-
- step++;
- if (!SystemThreads()) { //创建desktop线程
- fprintf (stderr, "KERNEL>InitGUI: Init system threads failure!\n");
- goto failure;
- }
-
-
- SetKeyboardLayout ("default");
-
-
- SetCursor (GetSystemCursor (IDC_ARROW));
-
-
- SetCursorPos (g_rcScr.right >> 1, g_rcScr.bottom >> 1);
-
- #ifdef _MG_EVALUATION
- mg_InitEvaluation ();
- #endif
- return 0;
-
- failure:
- mg_TerminateLWEvent ();
- failure1:
- mg_TerminateGAL ();
- fprintf (stderr, "KERNEL>InitGUI: Init failure, please check your MiniGUI configuration or resource.\n");
- return step;
- }
TerminateGUI是一个相反的过程。有兴趣的朋友可以自己研究下,这里不再详细说明。
在线程版,当InitGUI被启动后,实际上又额外创建了3个线程,分别是Desktop线程,timer主线程和IAL线程。这是可以通过gdb 来观察。使用gdb ./helloworld,在主线程调用完InitGUI函数后暂停,通过 info threads命令,我们可以看到:
- (gdb) info threads
- 4 Thread 0xb6eafb70 (LWP 23218) 0x0012d422 in __kernel_vsyscall ()
- 3 Thread 0xb76b0b70 (LWP 23217) 0x0012d422 in __kernel_vsyscall ()
- 2 Thread 0xb7eb1b70 (LWP 23216) 0x0012d422 in __kernel_vsyscall ()
- * 1 Thread 0xb7fec6c0 (LWP 23212) MiniGUIAppMain (argc=1, argv=0xbffff434)
- at helloworld.c:171
其中,线程1就是主线程,另外几个线程都是由InitGUI创建的。可以通过thread N 和bt命令,得到堆栈情况:
- (gdb) thread 2
- [Switching to thread 2 (Thread 0xb7eb1b70 (LWP 23216))]#0 0x0012d422 in __kernel_vsyscall ()
- (gdb) bt
- #0 0x0012d422 in __kernel_vsyscall ()
- #1 0x0013a2e0 in sem_wait@GLIBC_2.0 () from /lib/tls/i686/cmov/libpthread.so.0
- #2 0x001e67f1 in PeekMessageEx (pMsg=0xb7eb1368, hWnd=3133696, iMsgFilterMin=0,
- iMsgFilterMax=0, bWait=1, uRemoveMsg=1) at message.c:672
- #3 0x001e3f9e in GetMessage (data=0xbffff32c) at ../../include/window.h:2250
- #4 DesktopMain (data=0xbffff32c) at desktop-ths.c:123
- #5 0x0013396e in start_thread () from /lib/tls/i686/cmov/libpthread.so.0
- #6 0x003f5a4e in clone () from /lib/tls/i686/cmov/libc.so.6
- (gdb) thread 3
- [Switching to thread 3 (Thread 0xb76b0b70 (LWP 23217))]#0 0x0012d422 in __kernel_vsyscall ()
- (gdb) bt
- #0 0x0012d422 in __kernel_vsyscall ()
- #1 0x003ee971 in select () from /lib/tls/i686/cmov/libc.so.6
- #2 0x00170f90 in __mg_os_time_delay (ms=10) at nposix.c:384
- #3 0x001db484 in _os_timer_loop (data=0xbffff25c) at timer.c:101
- #4 TimerEntry (data=0xbffff25c) at timer.c:117
- #5 0x0013396e in start_thread () from /lib/tls/i686/cmov/libpthread.so.0
- #6 0x003f5a4e in clone () from /lib/tls/i686/cmov/libc.so.6
- (gdb) thread 4
- [Switching to thread 4 (Thread 0xb6eafb70 (LWP 23218))]#0 0x0012d422 in __kernel_vsyscall ()
- (gdb) bt
- #0 0x0012d422 in __kernel_vsyscall ()
- #1 0x0013a4e7 in sem_post@GLIBC_2.0 () from /lib/tls/i686/cmov/libpthread.so.0
- #2 0x001db8b0 in EventLoop (data=0xbffff32c) at init.c:141
- #3 0x0013396e in start_thread () from /lib/tls/i686/cmov/libpthread.so.0
- #4 0x003f5a4e in clone () from /lib/tls/i686/cmov/libc.so.6
- (gdb)
线程2,其入口是DesktopMain。该函数是在src/kernel/desktop-ths.c(线程版)文件中定义的。这个函数是在InitGUI中调用SystemThreads中创建的线程并启动其运行的。
线程3,其入口是TimerEntry,该函数时MiniGUI中定时器的实现。在线程版中,它通过线程和sleep等方法获得等距时间。它的实现 在src/kernel/timer.c中。它的创建是在SystemThreads中调用__mg_timer_init函数中创建的。
线程4,其入口是EventLoop,该函数时IAL的主要监视线程。也是在SystemThreads中创建的。
连同主线程在内的这4个线程,实际上大部时间都处于休眠状态,仅仅会在有事件发生时才唤醒。所以,对CPU的影响很小。
有一些开发板在移植后,会出席CPU占用过高的情况,其中一种原因是由于IAL的实现不合理造成的。EventLoop线程依靠IAL的具体实现, 由它来阻塞线程,直到收到按键或者鼠标消息。有些开发板需要通过定时器去轮询,而不是通过select方法来等待。当轮询时间过短时,会造成cpu占用率 升高。