Java代码引起的NATIVE野指针问题(下)
时间:2025-11-05 00:31:12 出处:IT科技阅读(143)

朴英敏,代码的小米MIUI部门。引起E野从事嵌入式开发和调试工作8年多,指针擅长逆向分析方法,问题主要负责解决安卓系统稳定性问题。代码的
实施hook:
我们有了hook,引起E野但目前还不知道是指针哪个so中释放了functor。
如果无法确定是问题哪个so,可以多hook几个so就行了。代码的
当然对于特定的引起E野例子,也有技巧来确定so,指针比如我们这个例子:
被析构的问题对象是Functor类的对象,由于它的代码的vtbl地址我们能够从log中获取到,
而vtbl一般指向定义了该类的引起E野so中,所以用vtbl值(0×73648de0)去map表中找,指针就能确定是哪个so了。
... 73635000-73646000 rw-p 00000000 00:00 0 73646000-73648000 r-xp 00000000 b3:18 1287 /system/lib/libwebviewchromium_plat_support.so =>73648000-73649000 r--p 00001000 b3:18 1287 /system/lib/libwebviewchromium_plat_support.so 73649000-7364a000 rw-p 00002000 b3:18 1287 /system/lib/libwebviewchromium_plat_support.so 7364a000-73684000 rw-p 00000000 00:00 0 73684000-73696000 r-xp 00000000 b3:18 1034 /system/lib/libjavacrypto.so 73696000-73697000 r--p 00011000 b3:18 1034 /system/lib/libjavacrypto.so 73697000-73698000 rw-p 00012000 b3:18 1034 /system/lib/libjavacrypto.so ...而需要注意的是,C++对象的释放是高防服务器delete函数,
libwebviewchromium_plat_support.so不会直接调用libc的free函数,而是调用libc++.so中的delete函数,再由delete函数调用free函数,
所以我们得hook libc++.so的free函数,但打印调用栈的模块也依赖libc++.so,所以如果在hook函数中打印调用栈,也会遇到死循环问题。
所以我们得hook libwebviewchromium_plat_support.so中的delete函数,这样既减少log量,也能避免死循环。
先确认libwebviewchromium_plat_support.so是否依赖了delete函数:
$ readelf -s libwebviewchromium_plat_support.so |grep UND 0: 00000000 0 NOTYPE LOCAL DEFAULT UND 1: 00000000 0 FUNC GLOBAL DEFAULT UND __cxa_finalize 2: 00000000 0 FUNC GLOBAL DEFAULT UND __cxa_atexit 4: 00000000 0 FUNC GLOBAL DEFAULT UND __aeabi_unwind_cpp_pr0 5: 00000000 0 FUNC GLOBAL DEFAULT UND __aeabi_unwind_cpp_pr1 6: 00000000 0 FUNC GLOBAL DEFAULT UND getrlimit 7: 00000000 0 FUNC GLOBAL DEFAULT UND setrlimit 8: 00000000 0 FUNC GLOBAL DEFAULT UND __errno 9: 00000000 0 FUNC GLOBAL DEFAULT UND strerror 10: 00000000 0 FUNC GLOBAL DEFAULT UND __android_log_print => 11: 00000000 0 FUNC GLOBAL DEFAULT UND _Znwj => 12: 00000000 0 FUNC GLOBAL DEFAULT UND _ZdlPv 14: 00000000 0 FUNC GLOBAL DEFAULT UND __android_log_assert ... 51: 00000000 0 FUNC GLOBAL DEFAULT UND __aeabi_llsr 52: 00000000 0 OBJECT GLOBAL DEFAULT UND __popcount_tab其中11项_Znwj是new的符号,_ZdlPv是delete的符号。
接下来就用工具hook libwebviewchromium_plat_support.so的delete函数:
extern void _ZdlPv(void *); void inject__ZdlPv(void* ptr) { LOGD("delete %p",ptr); dumpNativeStack(); dumpJavaStack(); _ZdlPv(ptr); }hook后复现问题,抓到的log如下:
10-27 21:19:52.961 8027 8027 D ObserverLayout: onStop: clz=com.miui.player.display.view.DisplayFragmentLayout{45665838 V.E..... ........ 0,0-1080,1920 #7f080039 app:id/content} 10-27 21:19:52.965 8027 8027 I MusicBaseFragment: onDestroyView the view is still attached, delay destroy 10-27 21:19:52.966 8027 8027 D INJECT : delete 0x7a7b8530 10-27 21:19:52.986 8027 8027 D INJECT : #00 pc 000015f6 /system/lib/libinject.so (inject__ZdlPv+21) 10-27 21:19:52.986 8027 8027 D INJECT : #01 pc 00001134 /system/lib/libwebviewchromium_plat_supp 10-27 21:19:52.986 8027 8027 D INJECT : #02 pc 00001088 /system/lib/libwebviewchromium_plat_supp 10-27 21:19:52.987 8027 8027 D INJECT : #03 pc 0001d30c /system/lib/libdvm.so (dvmPlatformInvoke+112) 10-27 21:19:52.987 8027 8027 D INJECT : #04 pc 0004d8da /system/lib/libdvm.so (dvmCallJNIMethod(unsigned int const*, JV+397) 10-27 21:19:52.987 8027 8027 D INJECT : #05 pc 00026720 /system/lib/libdvm.so 10-27 21:19:52.987 8027 8027 D INJECT : #06 pc 0002d790 /system/lib/libdvm.so (dvmMterpStd(Thread*)+76) 10-27 21:19:52.987 8027 8027 D INJECT : #07 pc 0002adf4 /system/lib/libdvm.so (dvmInterpret(Thread*, Method const*, JVa+184) 10-27 21:19:52.988 8027 8027 D INJECT : #08 pc 00060058 /system/lib/libdvm.so (dvmInvokeMethod(Object*, Method const*, +391) 10-27 21:19:52.988 8027 8027 D INJECT : #09 pc 00067ff6 /system/lib/libdvm.so 10-27 21:19:52.988 8027 8027 D INJECT : #10 pc 00026720 /system/lib/libdvm.so 10-27 21:19:52.988 8027 8027 D INJECT : #11 pc 0002d790 /system/lib/libdvm.so (dvmMterpStd(Thread*)+76) 10-27 21:19:52.988 8027 8027 D INJECT : #12 pc 0002adf4 /system/lib/libdvm.so (dvmInterpret(Thread*, Method const*, JVa+184) 10-27 21:19:52.988 8027 8027 D INJECT : #13 pc 0005fd74 /system/lib/libdvm.so (dvmCallMethodV(Thread*, Method const*, O+335) 10-27 21:19:52.988 8027 8027 D INJECT : #14 pc 000494c2 /system/lib/libdvm.so 10-27 21:19:52.989 8027 8027 D INJECT : at com.android.webview.chromium.DrawGLFunctor.nativeDestroyGLFunctor(Native Method) 10-27 21:19:52.989 8027 8027 D INJECT : at com.android.webview.chromium.DrawGLFunctor.access$000(DrawGLFunctor.java:31) 10-27 21:19:52.989 8027 8027 D INJECT : at com.android.webview.chromium.DrawGLFunctor$DestroyRunnable.run(DrawGLFunctor.java:91) 10-27 21:19:52.989 8027 8027 D INJECT : at com.android.org.chromium.content.common.CleanupReference.runCleanupTaskInternal(CleanupReference.java:159) 10-27 21:19:52.989 8027 8027 D INJECT : at com.android.org.chromium.content.common.CleanupReference.access$300(CleanupReference.java:32) 10-27 21:19:52.989 8027 8027 D INJECT : at com.android.org.chromium.content.common.CleanupReference$LazyHolder$1.handleMessage(CleanupReference.java:93) 10-27 21:19:52.990 8027 8027 D INJECT : at com.android.org.chromium.content.common.CleanupReference.handleOnUiThread(CleanupReference.java:147) 10-27 21:19:52.990 8027 8027 D INJECT : at com.android.org.chromium.content.common.CleanupReference.cleanupNow(CleanupReference.java:141) 10-27 21:19:52.990 8027 8027 D INJECT : at com.android.webview.chromium.DrawGLFunctor.destroy(DrawGLFunctor.java:46) 10-27 21:19:52.990 8027 8027 D INJECT : at com.android.webview.chromium.WebViewChromium.destroy(WebViewChromium.java:430) 10-27 21:19:52.990 8027 8027 D INJECT : at android.webkit.WebView.destroy(WebView.java:667) 10-27 21:19:52.990 8027 8027 D INJECT : at com.xiaomi.music.hybrid.HybridFragment.destroyHybridView(HybridFragment.java:64) 10-27 21:19:52.990 8027 8027 D INJECT : at com.xiaomi.music.hybrid.HybridFragment.onDestroyView(HybridFragment.java:115) 10-27 21:19:52.990 8027 8027 D INJECT : at com.miui.player.component.MusicBaseFragment.onDestroyView(MusicBaseFragment.java:216) 10-27 21:19:52.991 8027 8027 D INJECT : at android.app.Fragment.performDestroyView(Fragment.java:1898) 10-27 21:19:52.991 8027 8027 D INJECT : at android.app.FragmentManagerImpl.moveToState(FragmentManager.java:954) 10-27 21:19:52.991 8027 8027 D INJECT : at android.app.FragmentManagerImpl.removeFragment(FragmentManager.java:1167) 10-27 21:19:52.991 8027 8027 D INJECT : at android.app.BackStackRecord.popFromBackStack(BackStackRecord.java:715) 10-27 21:19:52.991 8027 8027 D INJECT : at android.app.FragmentManagerImpl.popBackStackState(FragmentManager.java:1544) 10-27 21:19:52.992 8027 8027 D INJECT : at android.app.FragmentManagerImpl$3.run(FragmentManager.java:502) 10-27 21:19:52.992 8027 8027 D INJECT : at android.app.FragmentManagerImpl.execPendingActions(FragmentManager.java:1449) 10-27 21:19:52.992 8027 8027 D INJECT : at android.app.FragmentManagerImpl$1.run(FragmentManager.java:443) 10-27 21:19:52.992 8027 8027 D INJECT : at android.os.Handler.handleCallback(Handler.java:733) 10-27 21:19:52.992 8027 8027 D INJECT : at android.os.Handler.dispatchMessage(Handler.java:95) 10-27 21:19:52.992 8027 8027 D INJECT : at android.os.Looper.loop(Looper.java:136) 10-27 21:19:52.993 8027 8027 D INJECT : at android.app.ActivityThread.main(ActivityThread.java:5016) 10-27 21:19:52.993 8027 8027 D INJECT : at java.lang.reflect.Method.invokeNative(Native Method) 10-27 21:19:52.993 8027 8027 D INJECT : at java.lang.reflect.Method.invoke(Method.java:515) 10-27 21:19:52.993 8027 8027 D INJECT : at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:792) 10-27 21:19:52.993 8027 8027 D INJECT : at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:608) 10-27 21:19:52.993 8027 8027 D INJECT : at dalvik.system.NativeStart.main(Native Method) 10-27 21:19:53.020 8027 8027 I OpenGLRenderer: functor=0x7a7b8530,vtbl=0x400fc1b8从log中可以看到,确实是在distroy view的时候释放了Functor,而随后再Renderer中又使用了这个Functor。
打印崩溃时的云服务器java调用栈如下:
10-27 21:19:53.274 8027 8027 I dalvikvm: "main" prio=5 tid=1 TIMED_WAIT10-27 21:19:53.279 8027 8027 I dalvikvm: | group="main" sCount=0 dsCount=0 obj=0x41716ca8 self=0x415344f8 10-27 21:19:53.279 8027 8027 I dalvikvm: | sysTid=6895 nice=-6 sched=0/0 cgrp=apps handle=1074409812 10-27 21:19:53.280 8027 8027 I dalvikvm: | state=R schedstat=( 0 0 0 ) utm=184 stm=61 core=3 10-27 21:19:53.280 8027 8027 I dalvikvm: at android.view.GLES20Canvas.nDrawDisplayList(Native Method) 10-27 21:19:53.281 8027 8027 I dalvikvm: at android.view.GLES20Canvas.drawDisplayList(GLES20Canvas.java:420) 10-27 21:19:53.281 8027 8027 I dalvikvm: at android.view.HardwareRenderer$GlRenderer.drawDisplayList(HardwareRenderer.java:1709) 10-27 21:19:53.281 8027 8027 I dalvikvm: at android.view.HardwareRenderer$GlRenderer.draw(HardwareRenderer.java:1525) 10-27 21:19:53.282 8027 8027 I dalvikvm: at android.view.ViewRootImpl.draw(ViewRootImpl.java:2475) 10-27 21:19:53.282 8027 8027 I dalvikvm: at android.view.ViewRootImpl.performDraw(ViewRootImpl.java:2347) 10-27 21:19:53.283 8027 8027 I dalvikvm: at android.view.ViewRootImpl.performTraversals(ViewRootImpl.java:1977) 10-27 21:19:53.284 8027 8027 I dalvikvm: at android.view.ViewRootImpl.doTraversal(ViewRootImpl.java:1094) 10-27 21:19:53.285 8027 8027 I dalvikvm: at android.view.ViewRootImpl$TraversalRunnable.run(ViewRootImpl.java:5703) 10-27 21:19:53.285 8027 8027 I dalvikvm: at android.view.Choreographer$CallbackRecord.run(Choreographer.java:764) 10-27 21:19:53.286 8027 8027 I dalvikvm: at android.view.Choreographer.doCallbacks(Choreographer.java:577) 10-27 21:19:53.287 8027 8027 I dalvikvm: at android.view.Choreographer.doFrame(Choreographer.java:547) 10-27 21:19:53.288 8027 8027 I dalvikvm: at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:750) 10-27 21:19:53.289 8027 8027 I dalvikvm: at android.os.Handler.handleCallback(Handler.java:733) 10-27 21:19:53.289 8027 8027 I dalvikvm: at android.os.Handler.dispatchMessage(Handler.java:95) 10-27 21:19:53.290 8027 8027 I dalvikvm: at android.os.Looper.loop(Looper.java:136) 10-27 21:19:53.291 8027 8027 I dalvikvm: at android.app.ActivityThread.main(ActivityThread.java:5016) 10-27 21:19:53.291 8027 8027 I dalvikvm: at java.lang.reflect.Method.invokeNative(Native Method) 10-27 21:19:53.292 8027 8027 I dalvikvm: at java.lang.reflect.Method.invoke(Method.java:515) 10-27 21:19:53.293 8027 8027 I dalvikvm: at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:792) 10-27 21:19:53.293 8027 8027 I dalvikvm: at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:608) 10-27 21:19:53.293 8027 8027 I dalvikvm: at dalvik.system.NativeStart.main(Native Method)正常情况下,view在被destroy后不应该再被绘制,通过跟孙念沟通,得知这种情况可能是view在destroy前没有remove导致的。
分析代码:
上面delete时的调用栈中有特别的两行:
10-27 21:19:52.990 8027 8027 D INJECT : at com.xiaomi.music.hybrid.HybridFragment.destroyHybridView(HybridFragment.java:64) 10-27 21:19:52.990 8027 8027 D INJECT : at com.xiaomi.music.hybrid.HybridFragment.onDestroyView(HybridFragment.java:115)这个是应用的代码,而这个问题只有在这个应用上出现过,所以很可能是应用的代码引起的,
所以查了下opengrok中的代码,发现有两处destroyHybridView()的实现:
@v8-kk-pisces-alpha/packages/apps/MiuiMusic/common/music_sdk/hybrid/src/com/xiaomi/music/hybrid/HybridFragment.java private void destroyHybridView() { for (HybridView view : mHybridViews) { if (view != null) { view.destroy(); } } mHybridViews.clear(); } @v8-kk-pisces-alpha/packages/apps/MiuiSdk/library/src/java/miui/hybrid/HybridFragment.java private void destroyHybridView() { for (HybridView view : mHybridViews) { if (view != null) { => if (view.getParent() != null) { => ((ViewGroup) view.getParent()).removeView(view); => } view.destroy(); } } mHybridViews.clear(); }跟应用的同事沟通后得知,音乐应用是用上面的代码,也就是没有removeView的代码。
将上面代码中添加removeView的逻辑后不再复现问题。
虽然问题得到解决,但还不清楚为什么没有removeView会导致野指针。
为了找到根源仔细阅读了相关代码,发现代码中Render中有detachFunctor的b2b供应网代码:
class GLES20Canvas extends HardwareCanvas { ... public void detachFunctor(int functor) { nDetachFunctor(mRenderer, functor); }用studio在这个代码中设置断点,得到如下调用栈:
java.lang.Thread.State: RUNNABLE at android.view.GLES20Canvas.detachFunctor(GLES20Canvas.java:321) at android.view.HardwareRenderer$GlRenderer.detachFunctor(HardwareRenderer.java:1791) at android.view.ViewRootImpl.detachFunctor(ViewRootImpl.java:744) at com.android.webview.chromium.DrawGLFunctor$DestroyRunnable.detachNativeFunctor(DrawGLFunctor.java:97) at com.android.webview.chromium.DrawGLFunctor.detach(DrawGLFunctor.java:53) at com.android.webview.chromium.WebViewChromium.onDetachedFromWindow(WebViewChromium.java:1718) at android.webkit.WebView.onDetachedFromWindow(WebView.java:2108) at android.view.View.dispatchDetachedFromWindow(View.java:12631) at android.view.ViewGroup.dispatchDetachedFromWindow(ViewGroup.java:2587) at android.view.ViewGroup.removeViewInternal(ViewGroup.java:3845) at android.view.ViewGroup.removeViewInternal(ViewGroup.java:3818) at android.view.ViewGroup.removeView(ViewGroup.java:3750) at com.xiaomi.music.hybrid.HybridFragment.destroyHybridView(HybridFragment.java:66) at com.xiaomi.music.hybrid.HybridFragment.onDestroyView(HybridFragment.java:119) at com.miui.player.component.MusicBaseFragment.onDestroyView(MusicBaseFragment.java:216) at android.app.Fragment.performDestroyView(Fragment.java:1898) at android.app.FragmentManagerImpl.moveToState(FragmentManager.java:954) at android.app.FragmentManagerImpl.removeFragment(FragmentManager.java:1167) at android.app.BackStackRecord.popFromBackStack(BackStackRecord.java:715) at android.app.FragmentManagerImpl.popBackStackState(FragmentManager.java:1544) at android.app.FragmentManagerImpl$3.run(FragmentManager.java:502) at android.app.FragmentManagerImpl.execPendingActions(FragmentManager.java:1449) at android.app.FragmentManagerImpl$1.run(FragmentManager.java:443) at android.os.Handler.handleCallback(Handler.java:733) at android.os.Handler.dispatchMessage(Handler.java:95) at android.os.Looper.loop(Looper.java:136) at android.app.ActivityThread.main(ActivityThread.java:5016) at java.lang.reflect.Method.invokeNative(Method.java:-1) at java.lang.reflect.Method.invoke(Method.java:515) at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:792) at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:608) at dalvik.system.NativeStart.main(NativeStart.java:-1)加了removeView后,会从Render中删除Functor,这样Render在绘制时,不再调用这个Functor。
这个问题只会在KK上有,L以后对Render做的很大改动,即使不做removeView,也不会存在野指针问题。
【本文是专栏“小米开放平台”的原创文章,“小米开放平台”微信公众号:xiaomideveloper】

猜你喜欢
- TCLXESSX2(体验创新科技,释放无限创意)
- Ubuntu的网络参数保存在文件 /etc/network/interfaces中,默认设置使用dhcp,内容如下:# The primary network interfaceauto eth0iface eth0 inet dhcp设置静态ip的方法如下:(1)编辑 /etc/network/interfaces1.1)将dhcp 一行屏蔽# The primary network interfaceauto eth0#iface eth0 inet dhcp1.2)添加和静态ip有关的参数# The primary network interfaceiface eth0 inet staticaddress 192.168.0.10netmask 255.255.255.0gateway 192.168.0.1(2)编辑 /etc/resolv.conf,设置dnsnameserver 202.96.134.133nameserver 202.106.0.20(3)执行下面两个命令,启用新设置$sudo ifdown eth0$sudo ifup eth0
- 10月13日消息,Ubuntu 15.10(Wily Werewolf)即将在10月22日正式发布,目前Ubuntu 15.10已经确认达成最终内核的冻结,也就是说,今后除了一些bug修复,将不会再有相关升级。具体说来,Ubuntu 15.10进入冻结阶段后,其软件栈和内核都不会再有升级,这能够让开发者更好地进行测试,为最终发布做好准备。据悉,Ubuntu 15.10所用Linux内核为4.2版。来自Canonical的Joseph Salisburty几天前就表示:“我们即在10月8日达成Wily Werewoft内核冻结,若还有针对15.10的补丁,请尽快提交。按照内核冻结的最终期限,所有补丁需要遵守我们的SRU策略,存在错过发布的可能。”
- ubuntu怎么开启root帐号?ubuntu 的root账户具有最高的系统权限,它类似于windows系统中的管理员账号,但是比windows系统中管理员账号的权限更高,一般都情况下不要使用root账户,但是有的时候还是要使用root账户,下面就为大家介绍ubuntu 开启root帐号方法!说明:小编的这个建议只适合于10.10之前版本的ubuntu系统,后面的11.04,11.10。。。14.04系统因为采用的默认桌面不同,所以本经验不适用,若要使用,那么请安装GNOME桌面ubuntu 开启root帐号方法:1、点击系统菜单栏中的“应用程序”,然后点击附件,之后在打开的附件子菜单中选择“终端”2、点击终端后就打开如下图所示的终端工具,大家要做的所有操作都在这个窗口中进行3、其实root账户是存在的,只是需要我们给它设置一个密码,然后使用的时候用root用户名登陆,然后输入对应的密码就就以root用户登录了,所以开启root账户,实际上就是给root用户设置一个密码的过程,下面我们就来给root设置密码,另外还需要注意的是,只能使安全ubuntu系统的时候创建的用户账号才能启用root账号,使用下面的命令来给root账号设置密码:sudo passwd root执行上面的命令后,就会提示要求输入当前用户的密码4、确认我们的密码正确后,就会提示“Enter new UNIX password”,这个就是设置密码的提示,输入大家要设置的密码,注意在设置密码的时候是看不到任何字符的,只有我们自己注意了5、输入密码后敲回车键,之后会提示我们再次确认密码,输入确认密码,然后按Enter回车键6、密码设置成功,这样root用户也就开启了,以后大家要使用root账号的时候只要使用相关切换命令就可以了,具体怎么切换到root账号的命令请看小编另外一个经验的分享END以上就是ubuntu怎么开启root帐号的方法,希望对大家有所帮助!
- 如何在平板电脑上实现双开手游?(教程详解及实用技巧)
- 电脑开机显示错误1802的原因及解决方法(深入探究电脑开机错误1802的根源,提供有效的解决方案)
- Ubuntu或者Debian系统中update-rc.d命令,是用来更新系统启动项的脚本。这些脚本的链接位于/etc/rcN.d/目录,对应脚本位于/etc/init.d/目录。在了解update-rc.d命令之前,你需要知道的是有关Linux系统主要启动步骤,以及Ubuntu中运行级别的知识。复制代码代码如下:复制代码代码如下:按指定顺序、在指定运行级别中启动或关闭复制代码代码如下:实例:update-rc.d apachectl start 20 2 3 4 5 . stop 20 0 1 6 .解析:表示在2、3、4、5这五个运行级别中,由小到大,第20个开始运行apachectl;在 0 1 6这3个运行级别中,第20个关闭apachectl。这是合并起来的写法,注意它有2个点号,效果等于下面方法:复制代码代码如下:A启动后B才能启动,B关闭后A才关闭复制代码代码如下:启动和关闭顺序为90,级别默认复制代码代码如下:修改LINUX默认启动级别# 0 – 停机(千万不要把initdefault设置为0 )# 1 – 单用户模式# 2 – 多用户,但是没有NFS# 3 – 完全多用户模式# 4 – 没有用到# 5 – X11# 6 – 重新启动(千万不要把initdefault设置为6 )# 对各个运行级的详细解释:0 为停机,机器关闭。1 为单用户模式,就像Win9x下的安全模式类似。2 为多用户模式,但是没有NFS支持。3 为完整的多用户模式,是标准的运行级。4 一般不用,在一些特殊情况下可以用它来做一些事情。例如在笔记本电脑的电池用尽时,可以切换到这个模式来做一些设置。5 就是X11,进到X Window系统了。6 为重启,运行init 6机器就会重启。修改级别vi /etc/inittab把id:3:initdefault:中的3改为5就是默认进入图形界面了
- 多桌面是一个非常有用的功能,它能让不同的工作任务分别被放置在不同的工作区中, 保持桌面的整洁,让人心情舒畅, 保持工作的高效率。多桌面一直是Linux桌面的一大特色,不过ubuntu这几个版本默认都没有开启多桌面,估计是为了照顾windows用户迁移适应。作为Linux爱好者, 如何开启ubuntu的多桌面功能呢?1、点击左侧菜单中的“扳手齿轮”图标,打开系统设置。2、进入“外观”选项。3、选择“行为”标签页,勾选“开启工作区”。然后关闭系统设置。4、使用快捷键 CTRL + 上/下/左/右 切换工作区。
- 常见的电脑设置错误及解决方法(避免电脑设置错误的关键注意事项)