说到QQ,你会想起什么?
是那标志性的“咳咳”上线声,还是“唧唧唧唧”的新消息通知声,还是天天挂机升等级?
在我们90后的青春里,一定少不了国民软件QQ,而最近我有幸体验了一把新版公测的Windows QQ,发现了很多惊喜,这绝对是一次重磅更新!
首先来看一下安装包,在安装大小方面,新版Windows QQ比起原来的版本,安装包足足减少了60MB!
安装完成之后,就是登录,这是以前的登录界面:
这是新版QQ的登录界面:
记忆中的QQ主界面它是这样的:
以往我们通过QQ聊天,需要双击联系人,开启一个独立的聊天窗口,当与多个人同时聊天时,需要在多个窗口之间来回切换,非常不便。
而现在,它变成了这样:
这一次,QQ采用了全新的三栏式设计,和微信、钉钉等软件一样,在一个窗口搞定,不用再切来切去,更符合当下我们在电脑上聊天沟通的习惯。
不仅如此,新版的QQ整体设计更加简洁,去除了传统界面上琳琅满目的功能按钮,整体给人年轻、轻便的感觉。
单是界面的改动还不足以称之为重磅更新,最重要的是,这一次,QQ采用了全新的技术架构!
NTQQ!
说到这个NT,熟悉Windows的同学应该不会陌生,微软当年搞Windows时,为了和早期的Windows版本相区分,就提出了一个Windows NT的内核,NT意思就是New Technology,这一次腾讯使用了这个名字,足以看出这不是一次普通的版本更新,更是一次技术架构的自我革命。
大家知道,一直以来,QQ是一款native应用,最大的问题是没法跨平台。在Windows上,一早是用MFC,后来有了DirectUI、WPF等开发技术,但这些东西只能用于Windows平台。在Linux和Mac上,又得使用其他的技术,比如Qt。光是开发团队,就得为三个平台各组建一套,人力成本高不说,软件的更新迭代还很难在所有平台上保持相同的节奏。
而现在,QQ使用了全新的技术架构:Electron!这些问题都将成为历史。
Electron是一种基于Node.js和Chromium构建的开源框架,可以帮助开发者使用Web技术(如HTML、CSS和JavaScript)构建跨平台桌面应用程序。相比于传统的本地应用程序开发,Electron有以下优势:
跨平台:Electron可以在Windows、macOS和Linux等多个平台上运行,因此可以更快速地开发出跨平台的应用程序,而无需为每个平台编写单独的代码。
开发效率高:Electron使用Web技术,开发者可以使用常用的Web开发工具和框架,如React、Angular、Vue等,因此可以快速开发出具有现代化界面和良好交互体验的应用程序。
更新方便:使用Electron开发的应用程序可以轻松进行更新,开发者只需将更新后的代码上传至服务器即可,用户启动应用程序时会自动下载最新版本。
生态系统完善:Electron拥有一个强大的生态系统,提供了大量的扩展和插件,可以方便地实现各种功能,如自动更新、错误报告、调试工具等。
界面优美:Electron可以使用Web技术创建漂亮、响应式的界面,可以使用HTML和CSS自定义应用程序的外观和交互。
因为以上优点,采用Electron技术作为QQ的全新技术架构,可以使得QQ在跨平台支持、开发效率、界面设计、应用程序更新和社区支持等方面都得到了很大的提升。
除此之外,最近几年由于众所周知的原因,国家很多重点单位、企业、高校都对国产化平台的呼声越来越强,有了Electron的技术加持,未来,QQ对国产化平台,对信创的支持都将不再是问题。
不过话说回来,技术方案没有银弹。比起原生的Native QQ可以直接面向操作系统编程,可以做很多极致的优化,大家可能担心Electron相对而言在性能这一块是不是不如之前的技术方案呢?
这个问题对于一般的小公司可能是一个问题,但这可是腾讯啊,腾讯就是靠QQ起家的,对于这个问题不可能不知道,不可能不重视,不可能不解决。
这是QQ团队一位大佬的原话,我摘录在这里:
充分细致地针对每种场景进行分析,再结合定制深度优化Electron和与Native通信层面的优化,内存可以被很好控制,剩下的不过是再把一些内存泄漏的场景找出来而已
不愧是鹅厂,使用新技术架构把功能实现只是基本要求,在此之上,人家还做了很多深度定制化的优化,把Electron的体验做到和Native应用一样流畅丝滑。
就我实际体验来看,我使用了大文件传输、语音视频、远程桌面等非常考验性能的功能,NTQQ都完成的非常出色,让你完全察觉不了这是用Electron技术开发的应用。
在这个过程中,内存占用也不算高,表现比较稳定:
在国产Linux、Mac平台不断崛起的今天,软件跨平台技术方案一定会成为各大软件厂商不得不面对的问题,前有Java虚拟机统一后端服务跨平台问题,现有Electron统一客户端跨平台问题,国民软件QQ迈出了历史性的一步,势必引领国内客户端产品开发的新潮流。
我叫GDB,是一个调试器,程序员通过我可以调试他们编写的软件,分析其中的bug。
作为一个调试器,调试分析是我的看家本领,像是给目标进程设置断点,或者让它单步执行,又或是查看进程中的变量、内存数据、CPU的寄存等等操作,我都手到擒来。
你只要输入对应的命令,我就能帮助你调试你的程序。
我之所以有这些本事,都得归功于一个强大的系统函数,它的名字叫ptrace。
long ptrace(
enum __ptrace_request request,
pid_t pid,
void *addr,
void *data
);
不管是开始调试进程,还是下断点、读写进程数据、读写寄存器,我都是通过这个函数来进行,要是没了它,我可就废了。
它的第一个参数是一个枚举型的变量,表示要执行的操作,我支持的调试命令很多都是靠它来实现的:
你可以通过我来启动一个新的进程调试,我会使用fork创建出一个新的子进程,然后在子进程中通过execv来执行你指定的程序。
不过在执行你的程序之前,我会在子进程中调用ptrace函数,然后指定第一个参数为PTRACE_TRACEME,这样一来,我就能监控子进程中发生的事情了,也才能对你指定的程序进行调试。
你也可以让我attach到一个已经运行的进程分析,这样的话,我直接调用ptrace函数,并且指定第一个参数为PTRACE_ATTACH就可以了,然后我就会变成那个进程的父进程。
具体要选择哪种方式来调试,这就看你的需要了。不过不管哪种方式,最终我都会“接管”被调试的进程,它里面发生的各种信号事件我都能得到通知,方便我对它进行调试操作。
软件断点
作为一个调试器,最常用的功能就是给程序下断点了。
你可以通过break命令告诉我,你要在程序的哪个位置添加断点。
当我收到你的命令之后,我会偷偷把被调试进程中那个位置的指令修改为一个0xCC,这是一条特殊指令的CPU机器码——int 3,是x86架构CPU专门用来支持调试的指令。
我的这个修改是偷偷进行的,你如果通过我来查看被调试进程的内存数据,或者在反汇编窗口查看那里的指令,会发现跟之前一样,这其实是我使的障眼法,让你看起来还是原来的数据,实际上已经被我修改过了,你要是不信,你可以另外写个程序来查看那里的数据内容,看看我说的是不是真的。
一旦被调试的进程运行到那个位置,CPU执行这条特殊的指令时,会陷入内核态,然后取出中断描述符表IDT中的3号表项中的处理函数来执行。
IDT中的内容,操作系统一启动早就安排好了,所以系统内核会拿到CPU的执行权,随后内核会发送一个SIGTRAP信号给到被调试的进程。
而因为我的存在,这个信号会被我截获,我收到以后会检查一下是不是程序员之前下的断点,如果是的话,就会显示断点触发了,然后等待程序员的下一步指示。
在没有下一步指示之前,被调试的进程都不会进入就绪队列被调度执行。
直到你使用continue命令告诉我继续,我再偷偷把替换成int 3的指令恢复,然后我再次调用ptrace函数告诉操作系统让它继续运行。
这就是我给程序下断点的秘密。
不知道你有没有发现一个问题,当我把替换的指令恢复后让它继续运行,以后就再也不会中断在这里了,可程序员并没有撤销这个断点,而是希望每次执行到这里都能中断,这可怎么办呢?
我有一个非常巧妙的办法,就是让它单步执行,只执行一条指令,然后又会中断到我这里,但这时候我并不会通知程序员,而仅仅是把刚才恢复的断点又给打上(替换指令),然后就继续运行。这一切都发生的神不知鬼不觉,程序员根本察觉不到。
单步调试
说到单步执行,应该算是程序员调试程序的时候除了下断点之外最常见的操作了,每一次只让被调试的进程运行一条指令,这样方便跟踪排查问题。
你可能很好奇我是如何让它单步执行的呢?
单步执行的实现可比下断点简单多了,我不用去修改被调试进程内存中的指令,只需要调用ptrace函数,传递一个PTRACE_SINGLESTEP参数就行了,操作系统会自动把它设置为单步执行的模式。
我也很好奇操作系统是怎么办到的,就去打听了一下。
原来x86架构CPU有一个标志寄存器,名叫eflags,它里面不止包含了程序运行的一些状态,还有一些工作模式的设定。
其中就有一个TF标记,用来告诉CPU进入单步执行模式,只要把这个标记为设置为1,CPU每执行一条指令,就会触发一次调试异常,调试异常的向量号是1,所以触发的时候,都会取出IDT中的1号表项中的处理函数来执行。
接下来的事情就跟命中断点差不多了,我会截获到内核发给被调试进程的SIGTRAP信号,然后等待程序员的下一步指令。
如果你继续进行单步调试,那我便继续重复这个过程。
如果你有程序的源代码,你还可以进行源码级别的单步调试,不过这里的单步就指的是源代码中的一行了。
这种情况下要稍微麻烦一点,我还要分析出每一行代码对应的指令有哪些,然后用上面说的单步执行指令的方法,一条条指令快速掠过,直到这一行代码对应的指令都执行完成。
内存断点
有的时候,直接给程序中代码的位置下断点并不能包治百病。比如程序员发现某个内存地址的内容老是莫名其妙被修改,想知道到底是哪个函数干的,这时候连地址都没有,根本没法下断点。
单步执行也不行,那么多条指令,得执行到猴年马月去才能找到?
不用担心,我可以帮你解决这个烦恼。
你可以通过watch命令告诉我,让我监视被调试进程中某个内存地址的数据变化,一旦发现被修改,我都会把它给停下来报告给你。
猜猜我是如何做到的呢?
我可以用单步执行的方式,每执行一步,就检查一下内容有没有没修改,一旦发现就停下来通知你们程序员。
不过这种方式实在是太麻烦了,会严重拖垮被调试进程的性能。
好在x86架构的CPU提供了硬件断点的能力,帮我解决了大问题。
在x86架构CPU的内部内置了一组调试寄存器,从DR0到DR7,总共8个。通过在DR0-DR3中设置要监控的内存地址,然后在DR7中设置要监控的模式,是读还是写,剩下的交给CPU就好了。
CPU执行的时候,一旦发现有符合调试寄存器中设置的情况发生时,就会产生调试异常,然后取出IDT中的1号表项中的处理函数来执行,接下来的事情就跟单步调试产生的异常差不多了。
CPU内部依靠硬件电路来完成监控,可比我们软件一条一条的检查快多了!
现在,你不止可以使用watch命令来监控内存被修改,还可以使用rwatch、awatch命令来告诉我去监控内存被读或者被写。
好了,这一期的故事就讲到这里了,如果你想查看更多未发布过的新鲜又有趣的技术小故事,可以扫描下方二维码购买我最新出版的《趣话计算机底层技术》图书。书中用一个个的小故事系统性的讲解了计算机底层技术的基本原理,以及如何运用他们解决日常工作中的各种实际问题。
|