(出号口诀)—输入完宽带帐号密码一键提取选完大区然后闪退怎么弄好

五一假期即将到来,假期期间的天气相信是大家关注的重点,据悉,2018五一假...
深圳社保如何查询?请大家在下面的框中输入身份证号,电脑号,进行查询。什...
伴随全国范围内的一股冷空气来袭,全国多地开启降雨天气,深圳也不列外,...
十二生肖各方面运势会如何?下面一起来看看郑博士一周生肖...
男士在买裤子的时候经常被问到裤子尺码而无法答出准确的裤...
青岛日照最好玩的旅游景点有哪些?下文为大家整理了 青岛日...
南昌周边自驾旅游景点哪里好玩?下文为大家整理了 南昌周边自驾旅游景点大全...
女人佩戴什么配饰可以转运?下文为大家整理了 女人佩戴什么...
新娘跟妆师婚礼当天流程是怎样的?下面为大家整理了 跟妆师一天的流程 ,一起...
对于很多第一次当妈妈的人来说对婴儿用品了解的不多,那么...
对于小户型的家庭来说,洗衣机的安放是个难题,有些只能是...
卧室是人们休息的地方,其装修环境直接影响睡眠质量,那卧室颜色怎么搭配呢...
现在的人家里有很多贵重东西都要用保护套,电视要盖个布,...
科目一属于理论考试,也是驾照考试的第一科,那2018科目一考试难点有哪些呢...
过去人们称高考是过独木桥,过去了就是人中龙凤,否则就真...
有很多人都有失眠的现象,许多人都不知道该怎么办才好,其...
小孩子便秘是种常见的现象,很多父母还是会为此而烦躁。下面要给大家分享的...
当兵体检项目有哪些呢?有不少年轻人想当兵但是又不知道具...
薄荷叶是植物薄荷的叶子,平常时,发烧、牙疼、恶心干呕、咽喉肿痛、食欲不...
近日,三星正式发布Exynos 9810移动处理器,那么这款 三星Ex...1:QQ音乐等音乐软件的锁屏界面显示歌词其实就是&b&不断更换新的锁屏图片的水印&/b&,PP助手的锁屏界面显示下载进度也是同理&br&&br&2:有的软件为了实现后台下载,会&b&在&/b&按下home键之后在&b&后台默认播放空白音乐&/b&,这样系统本身就会识别为音乐,不会在按下home键之后将它关闭了&br&&br&3:大部分杀内存软件是软件本身&b&先产生大量数据占用大部分内存来使iOS自身释放空闲内存&/b&,然后软件本身再恢复正常的内存占用&br&&br&4:iPhoneQQ即使关闭后台之后仍然不掉线因为iOS版本的QQ即使手机后台关闭,腾讯后台也会&b&自动默认在线48小时&/b&,软件本身和软件的通知是独立的,所以能正常接收推送(2011年6月份之前iPhone版QQ是按下home键自动离线的,但是可以接收消息)&br&&br&5:因为流量监测软件不能像安卓一样一直在后台驻留,为了实现流量监测功能,它会在第一次开启软件时读取系统当前的蜂窝数据用量,然后在你第二次打开软件时再次读取当前系统的蜂窝数据用量,相减之后就得出了你这一段时间的蜂窝数据用量&br&&br&6:关于软件评分,有的游戏会弹出评分请求,在app store里评分完之后会给一些游戏奖励,实际上那些软件是无法检测到你有没有评分有没有好评的,只是给你提供一个弹窗,你点出去然后再打开这个软件&b&即使不评分照样可以获得奖励&/b&&br&&br&7:各种手机助手不越狱也可以安装所谓的正版软件,这是通过漏洞&b&绕过了iTunes来使用一个帐号授权去无数个机器&/b&(原本一个iTunes帐号只能授权5个机器),这其实也是属于盗版,而且当你再次通过AppStore下载安装软件之后,之前通过各种助手下载的那些软件会出现闪退,需要重新授权。&br&&br&12月2日更新&br&—————————————————&br&8:各种电池助手电池管家只能在检测到你的充电状态后给你播放一个屏保,记录一下充电时间,它们&b&并不能读取电池参数&/b&,而且也无法对你的电池起到什么保护作用,计算的剩余充电时间也是不准确的(越狱后可以安装《BatteryDetective》来检测实际电池容量)&br&&br&目前想到这么多,不断补充中....
1:QQ音乐等音乐软件的锁屏界面显示歌词其实就是不断更换新的锁屏图片的水印,PP助手的锁屏界面显示下载进度也是同理 2:有的软件为了实现后台下载,会在按下home键之后在后台默认播放空白音乐,这样系统本身就会识别为音乐,不会在按下home键之后将它关闭…
同样作为美术+业余策划,目前也在用业余时间开发独立游戏。并且找到了不错的程序合作,还是难度最大的异地合作,我在北京,程序在东京。所以我觉得在回答这个问题上自己还是有些经验的。我不懂程序,仅从个人角度出发,来说说我认为能找到&b&强力程序&/b&的几个切入点吧。&br&&br&&b&第一是目标一致&/b&,都做你们希望做的游戏。如果他想做山寨换皮,你想做独立创新,那么就算了,道不同,不可能玩到一起去。除非你发工资给人家。以换皮为目的的独立游戏应该不多吧。&br&&br&&b&第二是熟悉游戏制作流程&/b&。思维清晰,对自己的专业范围工作流程了如指掌。我相信一个强力的程序是不愿意和一个半调子美术合作开发游戏的。如果美术自己不了解相应的开发流程,而是只会画画的话,哪怕画得再好,在双方的合作中也会带来很多的磨合成本,因为美术和程序的思维方式是完全不一样的。如果是用松散网络合作形式,那么沟通起来会更加困难。&br&&br&举个简单的例子,如果你想做一个攻击技能,在你想把这个招数做到有多华丽之前,你要先制定好技能释放时间,动作帧数,动作是否包含位移,如果需要位移,距离是多少。位移效果是由程序判断还是播放预先做好的预制动画。以及能否被打断等等一系列制作规范。&br&如果只是简单说对人家说,我想了一个牛逼招数,效果已经画好了,你去把功能做好吧。这样程序是没法和你合作的。&br&&br&就目前国内环境而言,能够制定出一套合理的制作规范的开发人员非常少,这也是很多独立游戏做不下去的原因。&br&&br&&b&第三制定合理的工作内容和开发流程。&/b&不要设定超出你们能力范围和精力范围的工作目标。经常看到网络上很多满怀热情的开发者,信心满满的想制作出媲美3A大作的独立游戏。树立崇高的目标固然能激励团队成员和吸引高手加盟,但是制作内容超过能力和时间能承受的范围,热情耗尽后,结果也往往不了了之。目标定太高,发工资给人家也没用的。同样,开发流程混乱的话,制作的先后次序搞不清楚,或者一天一个想法反复修改,也会招致开发人员反感。在工作中因为是拿钱办事,这么做无所谓,只要公司愿意支付修改成本就行,大部分国内公司做项目都是这个德性。但是如果是合作开发,大家地位平等,这么做就很容易出问题。&br&&br&&br&&b&第四是主动承担大量工作。&/b&如果你是项目的发起者和主要制作人,那么项目中需要的美术资源规划和制作,策划文档撰写,相关参考提供,开发工具选择,都必须你来亲力亲为。不要等别人找你要东西。以我目前制作中的项目为例,因为是IOS平台的游戏,在程序加入项目初期就提供了ipad作为开发测试用,并且购买了开发者账号。在开发中遇到困难,要主动寻求解决方案。这些都是团队发起人应该做的基础工作。&br&&br&&b&第五还是要证实自身强大。&/b&如果你能做出战地4的画面效果,并且能在项目中证明这一点。相信是个程序员都愿意和你合作的。&br&&br&&b&第六是合理外包工作量。&/b&很多低技术含量的重复劳动,能外包就外包吧,尽量减少主力成员工作量,不要用这些无聊工作磨灭团队热情,把精力放在有技术含量的地方。&br&&br&做到了这些,就靠运气和耐心吧。多在程序员出没的论坛和Q群转悠,适时表现自己,假以时日,总能找到志同道合的好基友。&br&&br&最后以我们做的项目为例来介绍我们的开发模式吧,以供参考。&br&这是我们游戏实机效果截图,运行硬件平台为ipad4.目前开发进度40%&br&&figure&&img src=&https://pic2.zhimg.com/e445b187ba2d_b.jpg& data-rawwidth=&2048& data-rawheight=&1536& class=&origin_image zh-lightbox-thumb& width=&2048& data-original=&https://pic2.zhimg.com/e445b187ba2d_r.jpg&&&/figure&&br&&figure&&img src=&https://pic1.zhimg.com/ea34d3d832c295366cedd1ffc8c7cfdc_b.jpg& data-rawwidth=&2048& data-rawheight=&1536& class=&origin_image zh-lightbox-thumb& width=&2048& data-original=&https://pic1.zhimg.com/ea34d3d832c295366cedd1ffc8c7cfdc_r.jpg&&&/figure&&br&&figure&&img src=&https://pic1.zhimg.com/dd3d818ee8450_b.jpg& data-rawwidth=&2048& data-rawheight=&1536& class=&origin_image zh-lightbox-thumb& width=&2048& data-original=&https://pic1.zhimg.com/dd3d818ee8450_r.jpg&&&/figure&&br&&figure&&img src=&https://pic4.zhimg.com/0aa947b6f218f60e4143_b.jpg& data-rawwidth=&2048& data-rawheight=&1536& class=&origin_image zh-lightbox-thumb& width=&2048& data-original=&https://pic4.zhimg.com/0aa947b6f218f60e4143_r.jpg&&&/figure&&br&我们的角色细节&br&&figure&&img src=&https://pic1.zhimg.com/fef4c72ffefa28d4c2d46c_b.jpg& data-rawwidth=&982& data-rawheight=&1200& class=&origin_image zh-lightbox-thumb& width=&982& data-original=&https://pic1.zhimg.com/fef4c72ffefa28d4c2d46c_r.jpg&&&/figure&&br&我相信这样的效果,让人很难相信这是移动平台上的游戏。&br&很难相信这是国产游戏。&br&很难相信这是几个人在素未蒙面的情况下用业余时间开发的游戏。&br&在这款游戏中,作为发起人,本人负责了游戏的玩法设计和美术资源制作。为了在移动平台实现尽量丰富的细节,我制定了一切和和美术相关的技术和艺术规范,比如同屏模型面数规范,贴图数量与重用率的规范,drawcall限制,贴图和模型内存使用限制,动态关卡的大小和加载距离,同屏物体的显示裁剪距离,lightmap的大小和分辨率,预乘可见性的范围大小....以及达成这样效果需要的贴图制作流程,模型制作流程,材质制作流程等等。这样让程序的精力能够集中在游戏的功能内容开发上。&br&&br&在开发流程上,我们用快速迭代的方式迅速过滤掉不成熟的想法。我们先假定设想的一切功能都是不合理的,在没有得到验证前,不做产品级的效果,这样大大缩短了开发时间。用很短的时间做出功能原型后,立刻测试,早发现问题早排除。确认没有大问题后,再开始细节的开发工作。避免重复劳动。&br&&br&在游戏内容制作上,我们将玩法切分成一个一个功能细节,我做出符合规范的美术资源(&b&注意只要符合规范就可以,不一定是成品&/b&!),交给程序负责功能实现,然后程序做出功能再交给我添加到关卡中组合成游戏内容,然后我再打包运行游戏进行测试,确定可行后进行产品级效果的制作。形成一个流水般的循环。&br&&br&一个合理的流程能够数倍提高开发效率。这里要吐槽一下很多国内游戏公司,包括那些所谓的大公司,在游戏制作的项目管理上都是没入门的水平。&br&&br&你会发现,通过这种方式,能够尽快,平稳度过磨合期,之后你会越做越好。而当你越做越好,团队凝聚力也会越来越强。从而吸引更多高手加盟,形成良性循环。
同样作为美术+业余策划,目前也在用业余时间开发独立游戏。并且找到了不错的程序合作,还是难度最大的异地合作,我在北京,程序在东京。所以我觉得在回答这个问题上自己还是有些经验的。我不懂程序,仅从个人角度出发,来说说我认为能找到强力程序的几个切…
首先,这个问题可归类为&a href=&//link.zhihu.com/?target=http%3A//en.wikipedia.org/wiki/Arbitrary-precision_arithmetic& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&Arbitrary-precision arithmetic&/a&(任意精度计算),一般会包含任意精度的整数(或定点数)、有理数及浮点数。&br&&br&许多答案提及到使用10进制(或是10进制的字符串)进行运算,但这并不是唯一的方法,而且比较慢。&br&&br&如果程序有大量的计算,而把结果转换成10进制显示并非程序的主要功能,那么可以考虑使用2^n进制。例如在32位架构下,使用vector&uint32_t&及去代表一个任意精度的无号整数。&br&&br&我们首先要实现n位全加器,用2^n进制的话可以使用机器本身的算术指令,例如以下的 C++实现:&br&&br&&div class=&highlight&&&pre&&code class=&language-cpp&&&span class=&kt&&uint32_t&/span& &span class=&nf&&FullAdder32&/span&&span class=&p&&(&/span&&span class=&kt&&uint32_t&/span& &span class=&n&&a&/span&&span class=&p&&,&/span& &span class=&kt&&uint32_t&/span& &span class=&n&&b&/span&&span class=&p&&,&/span& &span class=&kt&&uint32_t&/span& &span class=&n&&inCarry&/span&&span class=&p&&,&/span& &span class=&kt&&uint32_t&/span&&span class=&o&&*&/span& &span class=&n&&outCarry&/span&&span class=&p&&)&/span&
&span class=&p&&{&/span&
&span class=&kt&&uint64_t&/span& &span class=&n&&c&/span& &span class=&o&&=&/span& &span class=&k&&static_cast&/span&&span class=&o&&&&/span&&span class=&kt&&uint64_t&/span&&span class=&o&&&&/span&&span class=&p&&(&/span&&span class=&n&&a&/span&&span class=&p&&)&/span&
&span class=&o&&+&/span& &span class=&k&&static_cast&/span&&span class=&o&&&&/span&&span class=&kt&&uint64_t&/span&&span class=&o&&&&/span&&span class=&p&&(&/span&&span class=&n&&b&/span&&span class=&p&&)&/span&
&span class=&o&&+&/span& &span class=&k&&static_cast&/span&&span class=&o&&&&/span&&span class=&kt&&uint64_t&/span&&span class=&o&&&&/span&&span class=&p&&(&/span&&span class=&n&&inCarry&/span&&span class=&p&&);&/span&
&span class=&o&&*&/span&&span class=&n&&outCarry&/span& &span class=&o&&=&/span& &span class=&k&&static_cast&/span&&span class=&o&&&&/span&&span class=&kt&&uint32_t&/span&&span class=&o&&&&/span&&span class=&p&&(&/span&&span class=&n&&c&/span& &span class=&o&&&&&/span& &span class=&mi&&32&/span&&span class=&p&&);&/span&
&span class=&k&&return&/span& &span class=&k&&static_cast&/span&&span class=&o&&&&/span&&span class=&kt&&uint32_t&/span&&span class=&o&&&&/span&&span class=&p&&(&/span&&span class=&n&&c&/span& &span class=&o&&&&/span& &span class=&mh&&0xFFFFFFFF&/span&&span class=&p&&);&/span&
&span class=&p&&}&/span&
&/code&&/pre&&/div&&br&一些编译器提供带进位的加法,例如VC x86中有_addcarry_u32()和addcarry_u64()指令。&br&&br&任意精度的无号整数的加法和减法可以使用简单的漣波进位加法器(ripple carry adder)实现,也可考虑使用并行的超前进位加法器(carry-lookahead adder)。&br&&br&&div class=&highlight&&&pre&&code class=&language-cpp&&&span class=&c1&&// 最低位在A.front(),最高位在A.back()。&/span&
&span class=&kt&&void&/span& &span class=&nf&&RippleCarryAdder&/span&&span class=&p&&(&/span&
&span class=&k&&const&/span& &span class=&n&&vector&/span&&span class=&o&&&&/span&&span class=&kt&&uint32_t&/span&&span class=&o&&&&&/span& &span class=&n&&A&/span&
&span class=&k&&const&/span& &span class=&n&&vector&/span&&span class=&o&&&&/span&&span class=&kt&&uint32_t&/span&&span class=&o&&&&&/span& &span class=&n&&B&/span&
&span class=&n&&vector&/span&&span class=&o&&&&/span&&span class=&kt&&uint32_t&/span&&span class=&o&&&*&/span& &span class=&n&&R&/span&&span class=&p&&)&/span&
&span class=&p&&{&/span&
&span class=&n&&assert&/span&&span class=&p&&(&/span&&span class=&o&&!&/span&&span class=&n&&A&/span&&span class=&p&&.&/span&&span class=&n&&empty&/span&&span class=&p&&());&/span&
&span class=&n&&assert&/span&&span class=&p&&(&/span&&span class=&o&&!&/span&&span class=&n&&B&/span&&span class=&p&&.&/span&&span class=&n&&empty&/span&&span class=&p&&());&/span&
&span class=&n&&assert&/span&&span class=&p&&(&/span&&span class=&n&&R&/span& &span class=&o&&!=&/span& &span class=&nb&&NULL&/span&&span class=&p&&);&/span&
&span class=&k&&const&/span& &span class=&kt&&size_t&/span& &span class=&n&&n&/span& &span class=&o&&=&/span& &span class=&n&&max&/span&&span class=&p&&(&/span&&span class=&n&&A&/span&&span class=&p&&.&/span&&span class=&n&&size&/span&&span class=&p&&(),&/span& &span class=&n&&B&/span&&span class=&p&&.&/span&&span class=&n&&size&/span&&span class=&p&&())&/span&
&span class=&n&&R&/span&&span class=&o&&-&&/span&&span class=&n&&clear&/span&&span class=&p&&();&/span&
&span class=&n&&R&/span&&span class=&o&&-&&/span&&span class=&n&&reserve&/span&&span class=&p&&(&/span&&span class=&n&&n&/span& &span class=&o&&+&/span& &span class=&mi&&1&/span&&span class=&p&&);&/span&
&span class=&c1&&// 为简单起见,超越范围的输入补零。可优化为不同cases的循环,减少inner-loop分支。&/span&
&span class=&kt&&uint32_t&/span& &span class=&n&&inCarry&/span& &span class=&o&&=&/span& &span class=&mi&&0u&/span&&span class=&p&&;&/span&
&span class=&k&&for&/span& &span class=&p&&(&/span&&span class=&kt&&size_t&/span& &span class=&n&&i&/span& &span class=&o&&=&/span& &span class=&mi&&0&/span&&span class=&p&&;&/span& &span class=&n&&i&/span& &span class=&o&&&&/span& &span class=&n&&n&/span&&span class=&p&&;&/span& &span class=&n&&i&/span&&span class=&o&&++&/span&&span class=&p&&)&/span& &span class=&p&&{&/span&
&span class=&kt&&uint32_t&/span& &span class=&n&&outCarry&/span&&span class=&p&&;&/span&
&span class=&n&&R&/span&&span class=&o&&-&&/span&&span class=&n&&push_back&/span&&span class=&p&&(&/span&&span class=&n&&FullAdder32&/span&&span class=&p&&(&/span&
&span class=&n&&i&/span& &span class=&o&&&&/span& &span class=&n&&A&/span&&span class=&p&&.&/span&&span class=&n&&size&/span&&span class=&p&&()&/span& &span class=&o&&?&/span& &span class=&n&&A&/span&&span class=&p&&[&/span&&span class=&n&&i&/span&&span class=&p&&]&/span& &span class=&o&&:&/span& &span class=&mi&&0u&/span&&span class=&p&&,&/span&
&span class=&n&&i&/span& &span class=&o&&&&/span& &span class=&n&&B&/span&&span class=&p&&.&/span&&span class=&n&&size&/span&&span class=&p&&()&/span& &span class=&o&&?&/span& &span class=&n&&B&/span&&span class=&p&&[&/span&&span class=&n&&i&/span&&span class=&p&&]&/span& &span class=&o&&:&/span& &span class=&mi&&0u&/span&&span class=&p&&,&/span&
&span class=&n&&inCarry&/span&&span class=&p&&,&/span&
&span class=&o&&&&/span&&span class=&n&&outCarry&/span&&span class=&p&&));&/span&
&span class=&n&&inCarry&/span& &span class=&o&&=&/span& &span class=&n&&outCarry&/span&&span class=&p&&;&/span&
&span class=&p&&}&/span&
&span class=&c1&&// 最后的进位&/span&
&span class=&k&&if&/span& &span class=&p&&(&/span&&span class=&n&&inCarry&/span&&span class=&p&&)&/span&
&span class=&n&&R&/span&&span class=&o&&-&&/span&&span class=&n&&push_back&/span&&span class=&p&&(&/span&&span class=&n&&inCarry&/span&&span class=&p&&);&/span&
&span class=&p&&}&/span&
&span class=&c1&&// 注意:随手写,未经测试&/span&
&/code&&/pre&&/div&&br&减法可使用二进位补数记法(two's complement representation),然后用加法处理。二进位补数记法的取反很简单,而加一也可以放在开始时设置inCarry=1。但要注意处理负数的结果,要支持负数的话,每个任意精度的数还要多加一个符号标记。&br&&br&然而,如果需要把结果转换为十进制,便需要除法或c。每次除10取模可得一个10进位。除以一个常数可以优化为乘数及移位,详情可看《&a href=&//link.zhihu.com/?target=http%3A//book.douban.com/subject//& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&Hacker's Delight (豆瓣)&/a&》。&br&&br&另一种方法,是使用接近机器位数的10^n进位作为基数(base),例如32位架构可以使用10^9。优点是可以简单地转换为10进位的输出,缺点是浪费了一些运算能力。&br&&br&题目没提及乘法和除法,简单提一提。乘法要分开低位的乘法和高位的乘法,如VC x86中的__mulh()便是64位乘法结果的高位部分,如果没有相关指令就要把输入切为一半一半来计算。然后像竖式计算般把每对输入相乘然后加总,这称为长整数乘法(long multiplication)。另外还有一些更快的方法,如&a href=&//link.zhihu.com/?target=http%3A//en.wikipedia.org/wiki/Karatsuba_algorithm& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&Karatsuba algorithm&/a&、Toom–Cook multiplication、Sch?nhage–Strassen algorithm。除法的话也有长除法(long division)和其他更快的算法。
首先,这个问题可归类为(任意精度计算),一般会包含任意精度的整数(或定点数)、有理数及浮点数。 许多答案提及到使用10进制(或是10进制的字符串)进行运算,但这并不是唯一的方法,而且比较慢。 如果程序有大量的计算,…
我们公司引擎部门新员工,一般会有两个入门练习:&br&一是只用类似DrawPixel的函数,实现一个软件光栅化。&br&二是使用自家引擎做一个完整游戏。可以比较简单,但必须完整。&br&一个去鹅厂的小伙伴也做过类似跑酷类手游作为练手。&br&&br&所以我觉得,从学习的角度,一边做游戏,一边做个玩具引擎,并不冲突。&br&做一个自己的引擎出来,满足技术好奇心,也能试验想法;&br&用一个开源图形引擎做一个类型的游戏,能了解组成部分和主要需求。了解&br&&br&楼上有好多关于做游戏还是做引擎的讨论,都是有道理的。&br&如&a data-hash=&0effe9e423faad125fa9c6a& href=&//www.zhihu.com/people/0effe9e423faad125fa9c6a& class=&member_mention& data-editable=&true& data-title=&@张静vinjn& data-tip=&p$b$0effe9e423faad125fa9c6a& data-hovercard=&p$b$0effe9e423faad125fa9c6a&&@张静vinjn&/a& 等所说,如果没做过一个完整的游戏,直接只做引擎,学习的效率和引擎的质量都不会太高。&br&但在很多人心里,会把引擎开发的这个工作神话。这种时候自己做一个引擎,对提高自己的信心会有帮助。大部分贬低引擎开发工作的人,至少都是有能撸一个的底气的(且不说质量)。&br&&br&-------------------------------------------------------------------------------------------------------------&br&所以对两个方面,我都推荐一些自己感觉不错的资料:&br&&br&游戏逻辑方面:推荐一个网站, &a href=&//link.zhihu.com/?target=http%3A//gameprogrammingpatterns.com/& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&Game Programming Patterns&/a&,作者把自己的书放github上,供读者提意见。&br&&br&引擎架构方面:Game Engine Architecture,这本得看中文翻译的。&br&图形引擎方面,主要是算法和API,引擎架构抄一套别人的。&br&图形基础算法书籍:在lz的基础上,推荐一个3D Game Engine Design,里面的3D算法和原理讲解很详细,可惜有点老。&br&高级图形技术:除了Real Time Rendering 3和GPU Pro系列以外,可以跟KlayGE和OpenGPU。这个方向量力而行,国内现在的行情是转手游的多,一些复杂的效果研究太深入也可能发挥不了。&br&&br&update:&br&下面评论有问,我贴一下。&br&软件光栅化可以简单理解为,只给你一个画点的函数,你需要用C++实现一个三维物体显示的过程。一般这个工作是由Direct3D/OpenGL的驱动实现来做的。&br&这个工作可以做的很难,也可以很简单。我们公司貌似所有客户端程序都会做这个,但要求跟老大有关。&br&最基本就是实现一个固定管线,包括顶点坐标的矩阵变化,画线,三角形填充光栅化算法,裁剪,Gouround光照,纹理坐标插值,ZBuffer等等。&br&要做好点,就可以把一个引擎Renderer部分的借口都实现了,用C++写个VS/PS,跟D3D/OpenGL平级。
我们公司引擎部门新员工,一般会有两个入门练习: 一是只用类似DrawPixel的函数,实现一个软件光栅化。 二是使用自家引擎做一个完整游戏。可以比较简单,但必须完整。 一个去鹅厂的小伙伴也做过类似跑酷类手游作为练手。 所以我觉得,从学习的角度,一边做…
谢邀。。。。 压根没人邀请我,好像这个词成为知乎的通用开篇词语。&br&&br&&br&十月一的假期间,看到这个问题,当时没怎么在意。几天后,在微博上,又看到这个问题,冥冥中,有种想回答的冲动。上周六时,研发部门内部周会时,听到其他项目组的一个整型溢出问题,导致刷钱的bug。这更加坚定我要回答这个问题的决心,拯救地球的任务,就交给我了。以下是在下经历过的webgame安全问题的经验,定有不妥指出,还请各位斧正。如果觉得帮助了你,亦可分享给其他做webgame研发的朋友,做交流探讨。&br&&br&原问题是『网页游戏都有哪些安全问题?』,我觉得不妥,我给改成了『网页游戏都有哪些安全问题?如何做得更安全?』,同时,问题也从『大家来研究探讨一下,网页游戏攻防技术。必定,这个话题很敏感。目前,网页游戏已经很多了,会不会被黑产盯上?网页游戏会不会被黑,数据库会不会被拖库』改成了『大家来研究探讨一下,网页游戏攻防技术。必定,这个话题很敏感。目前,网页游戏已经很多了,会不会被黑产盯上?网页游戏会不会被入侵?入侵方式有哪些?如何做好网页游戏的入侵防御?挽救措施有哪些?如何才能最小化减少厂商损失?』,更改的理由是『本文原提问者开篇提到「大家来研究探讨一下,网页游戏攻防技术。」,那么应该不光提到如何入侵,更应该提到如何防御,应该细心描述漏洞形成原理,规避方式,以提高研发者技能水平;应该详细讲解安全事件发生后,如何最小化减少厂商损失,减少用户损失,保护游戏平衡。』,幸运的是,这个修改,被知乎通过了。对此,表示感谢。&br&&br&前面安全专家&a class=&member_mention& data-editable=&true& data-title=&@余弦& data-hash=&afea1df0fc2b1fd51e9ca04d8b166f80& href=&//www.zhihu.com/people/afea1df0fc2b1fd51e9ca04d8b166f80& data-hovercard=&p$b$afea1df0fc2b1fd51e9ca04d8b166f80&&@余弦&/a& 站在安全工作者的角度,回答了这个问题。下面我将以webgame研发者角度,切合游戏业务模块逻辑,从业务需求,数据库设计,程序编写,操作方式上来讲解漏洞形成原理,规避方案,也欢迎大家讨论。 &br&&br&&br&&b&登录认证&/b&&br&
近几年,网页游戏几乎都是以联运方式运营,意味着游戏服务器本身不保存用户密码,用户登录在平台,通过平台跟游戏服务器的接口对接登录。接口做加密认证。故webgame的帐号密码安全问题,这里不提了。但登录认证的hash字符串安全,也还是要注意的。比如登录hash字符串的生效时间,hash字符串的加密参数来源,比如包括用户名、登录IP,浏览器user-agent等数据,以防止改hash被泄漏了,也是很难通过服务器的验证。&br&&br&&b&游戏充值&/b&&br&
webgame的游戏充值流程,跟普通网页充值流程一致,没有特殊的地方,其不同点就是跟其他众多平台做联合运营时,势必要每个公司做接口对接,且接口规范各式各样,且游戏厂商没有话语权,必须按照他们的接口规范来,这实在棘手。腾讯的充值接口的验证方式,安全性做的较为突出,大约代码:&div class=&highlight&&&pre&&code class=&language-php&&&span class=&x&&$signKey = array('openid','appid','ts','payitem','token','billno','version','zoneid','providetype','amt','payamt_coins','pubacct_payamt_coins');&/span&
&span class=&x&&$sign = array();&/span&
&span class=&x&&//从GET参数中,对比找出上面参数的值&/span&
&span class=&x&&foreach($signKey as $key ) {&/span&
&span class=&x&&
if (isset($data[$key]))&/span&
&span class=&x&&
&span class=&x&& $sign[$key] = $data[$key];
//只有 GET里有的参数,才参与sig的计算&/span&
&span class=&x&&
&span class=&x&&}&/span&
&span class=&x&&######开始生成签名############&/span&
&span class=&x&&//1: URL编码 URI&/span&
&span class=&x&&$url = rawurlencode($url);&/span&
&span class=&x&&//2:按照key进行字典升序排列&/span&
&span class=&x&&ksort($sign);&/span&
&span class=&x&&//3: &拼接,并URL编码&/span&
&span class=&x&&$arrQuery = array();&/span&
&span class=&x&&foreach ($sign as $key =& $val ) &/span&
&span class=&x&&{ &/span&
&span class=&x&&
$arrQuery[] = $key . '=' . str_replace('-','%2D',$val);&/span&
&span class=&x&&}
&span class=&x&&$query_string = join('&', $arrQuery);&/span&
&span class=&x&&//4 以POST方式拼接 1、3 以及URL&/span&
&span class=&x&&$src = 'GET&'.$url.'&'.rawurlencode($query_string);&/span&
&span class=&x&&// ## 构造密钥&/span&
&span class=&x&&$key = $this-&config-&get('qq_appkey').'&';&/span&
&span class=&x&&//### 生成签名&/span&
&span class=&x&&$sig = base64_encode(hash_hmac(&sha1&, $src, strtr($key, '-_', '+/'), true));&/span&
&span class=&x&&if ( $sig != $data['sig'] ) {&/span&
&span class=&x&&
$return['ret'] = 4;&/span&
&span class=&x&&
$return['msg'] = '请求参数错误:(sig)';&/span&
&span class=&x&&
$this-&output-&set(json_encode($return));&/span&
&span class=&x&&&/span&
&span class=&x&&}&/span&
&/code&&/pre&&/div&在此基础上,还可以做的严谨点:&br&&ol&&li&增加随机参数名、参数值。随机参数名、参数值由联运方随机生成,按照参数名的字符串所属ASCII码顺序排序,参数名、参数值均参与sign的计算,增加暴力破解密钥(app key)难度。&br&&/li&&li&增加回调验证订单号,金额信息。游戏充值服务器接收到充值请求时,反向到该平台回调接口,确认此笔订单有效性,以防止加密密钥泄漏的问题。&br&&/li&&/ol&&b&远程文件引入&/b&&br& 在网页游戏的研发中,多数都是使用框架来做,即使用REQUEST来的参数,作为请求文件名的一部分,来使用,那么很容易形成远程文件引入的漏洞。在我们之前的游戏中,曾出现过一例这样的漏洞问题。&br&&div class=&highlight&&&pre&&code class=&language-php&&&span class=&x&&// Load the local application controller&/span&
&span class=&x&&// Note: The Router class automatically validates the controller path.
If this include fails it&/span&
&span class=&x&&// means that the default controller in the Routes.php file is not resolving to something valid.&/span&
&span class=&x&&if ( ! file_exists(APPROOT.'controllers/'.load('Router')-&getDirectory().load('Router')-&getClass().EXT))&/span&
&span class=&x&&{&/span&
&span class=&x&& load('Errors')-&show404('Unable to load your default controller.
Please make sure the controller specified in your Routes.php file is valid.');&/span&
&span class=&x&&}&/span&
&span class=&x&&include(APPROOT.'controllers/'.load('Router')-&getDirectory().load('Router')-&getClass().EXT);&/span&
&span class=&x&&load('Benchmark')-&mark('load_basic_class_time_end');&/span&
&/code&&/pre&&/div&&figure&&img src=&https://pic4.zhimg.com/d68b576c17941dccf7a37f7aeaad0733_b.jpg& data-rawwidth=&988& data-rawheight=&191& class=&origin_image zh-lightbox-thumb& width=&988& data-original=&https://pic4.zhimg.com/d68b576c17941dccf7a37f7aeaad0733_r.jpg&&&/figure&从代码以及案例图中,可以看到对于REQUEST的参数没有过滤处理,直接作为文件名来include引入的,故导致这种问题,类似上页图中QQ群网站的漏洞。若PHP version & 5.3.4 ,还会发生Null(%00) 截断的问题,带来更大的安全问题。在我们新的项目中,我们更改了实现方式,我们游戏所有接口都会走gateway,gateway里,对控制器名做类名规范的检测处理,再在指定几个目录下做autoload加载文件,且还会对REQUEST的类名、方法用ReflectionClass反射类的处理,检测到类、方法、参数是否合法。一来避免『远程文件引入』漏洞问题,二来便于前后端联调时,抛出更详细的异常,方便调试。下面为参考代码:&br&&div class=&highlight&&&pre&&code class=&language-php&&&span class=&x&&require_once CONFIG_PATH . &/auto.php&;&/span&
&span class=&x&&spl_autoload_register(&__autoload&);&/span&
&span class=&x&&......&/span&
&span class=&x&&//默认消息格式&/span&
&span class=&x&&$view-&clear();&/span&
&span class=&x&&$view-&error(MLanguages::COM__INVALID_REQUST);&/span&
&span class=&x&&$msg = new Afx_Amf_plugins_AcknowledgeMessage($val-&data[0]-&$messageIdField);&/span&
&span class=&x&&$msg-&setBody($view-&get());&/span&
&span class=&x&&$message-&data = $&/span&
&span class=&x&&...&/span&
&span class=&x&&$a = new Yaf_Request_Simple();&/span&
&span class=&x&&$a-&setControllerName($method[0]);&/span&
&span class=&x&&$a-&setActionName($method[1]);&/span&
&span class=&x&&$objC = new ReflectionClass($method[0].&Controller&);&/span&
&span class=&x&&$arrParamenter = $objC-&getMethod($method[1].&Action&)-&getParameters();&/span&
&span class=&x&&$arrRequest = isset($val-&data[0]-&body[0]) ? (array)$val-&data[0]-&body[0] : array();&/span&
&span class=&x&&$bCanCall =&/span&
&span class=&x&&foreach ($arrParamenter as $objParam)&/span&
&span class=&x&&{&/span&
&span class=&x&&
$parm = $objParam-&getName();&/span&
&span class=&x&&
$bIsOption = $objParam-&isOptional();
//是否为可选参数&/span&
&span class=&x&&
if (isset($arrRequest[$parm]))&/span&
&span class=&x&&
&span class=&x&&
$a-&setParam($parm , $arrRequest[$parm]);&/span&
&span class=&x&&
&span class=&x&&
elseif ($objParam-&isOptional())&/span&
&span class=&x&&
&span class=&x&&
//可选参数&/span&
&span class=&x&&
&span class=&x&&
else&/span&
&span class=&x&&
&span class=&x&&
$bCanCall =&/span&
&span class=&x&&
&span class=&x&&}&/span&
&span class=&x&&if ($bCanCall)&/span&
&span class=&x&&{&/span&
&span class=&x&&
$rp = $app-&getDispatcher()-&dispatch($a);&/span&
&span class=&x&&
$msg = new Afx_Amf_plugins_AcknowledgeMessage($val-&data[0]-&$messageIdField);&/span&
&span class=&x&&
$msg-&setBody($view-&get());&/span&
&span class=&x&&
$message-&data = $&/span&
&span class=&x&&}&/span&
&/code&&/pre&&/div&&br&&b&SQL 注入&/b&&br&SQL注入原理、方式,跟普通web应用一样,没什么特别的,在使用REQUEST来的参数时,过滤处理即可。可能在消息格式,以及注入操作简便上,会蒙蔽研发人员的眼睛,被忽略掉了。比如我们项目的AMF消息格式,在前端界面没出来之前,我们后端程序员一般使用Pinta来模拟操作,调试程序。前端界面出来之后,会使用Charles proxy来捕捉http请求。在这些过程中,请求接口、参数的构造,没有普通web那么简单。研发人员也容易忽略对请求参数的过滤,故很容易形成这种问题。防御方式做过滤处理,或SQL预编译。&br&&figure&&img src=&https://pic2.zhimg.com/baf2f10b201_b.jpg& data-rawwidth=&478& data-rawheight=&480& class=&origin_image zh-lightbox-thumb& width=&478& data-original=&https://pic2.zhimg.com/baf2f10b201_r.jpg&&&/figure&SQL 注入产生&br&&figure&&img src=&https://pic3.zhimg.com/dfccd1eba8161699ebbbcb6_b.jpg& data-rawwidth=&477& data-rawheight=&490& class=&origin_image zh-lightbox-thumb& width=&477& data-original=&https://pic3.zhimg.com/dfccd1eba8161699ebbbcb6_r.jpg&&&/figure&&br&为了提高游戏服务器的吞吐能力,网页游戏的架构也是一直在演变的。在之前以Mysql作为数据存储的webgame架构中,其他节点都是可以水平扩展,或者说依赖简单粗暴的增加服务器来解决,单单作为唯一数据存储中心,不能这么做。为此,很多webgame的数据存储改用Nosql来代替,甚至java、C/C++的游戏数据,直接在内存中操作,游戏关服时,才写入到DB中。故SQL注入的问题,也会越来越少。&br&&br&&b&通讯协议与消息格式&/b&&br&网页游戏虽然名字叫网页游戏,但通讯协议并非全是http,也有很多使用socket,以及http+socket并用的做法。我们是http协议+amf消息格式,以及socket并用来实现。在http与https的取舍上,我们考虑到ssl的启用后,大量的ssl解密加密运算,势必会增加服务器大量的CPU计算压力。而传输的内容,多数是游戏业务的操作,响应,是能接受被监听嗅探的行为的(认证信息除外)。站在安全角度,这不能理解。但站在产品角度,考虑一下 投入产出,然后选择http通讯,也是可以理解的。socket在我们游戏中,除了在聊天应用上使用外,在一些组队、帮派战之类需要多个玩家之间同步数据信息时,我们也会使用socket来推送数据。在使用socket作为所有业务传输的协议时,协议格式一般都是开源协议,比如msgpack、protobuf之类,或者自定义的协议。使用自定义协议时,务必检测整个消息包的每一个参数,类型范围,避免个别超大数值、边界数值出现,导致主程序内存越界,以至于服务宕机,无法正常服务的情况发生。&br&&br&&b&金币复制-整型溢出&/b&&br&&blockquote&上周周六开周会时,听到其他项目组的一个关于整型溢出导致产生刷金币的问题。在这里,我抽象该案例,分享一下。商城出售开启背包格子的所需道具『梧桐木』。在游戏中,用户包裹格子数量一般都会作为一个收费点,一款游戏的格子大约为每行7格子,一共8行这样。比如前面3行是默认开放的,第4行是收费的,而且第一个格子所需品梧桐木的价格1个银子,第二个梧桐木是2个银子,第三个是4个银子。依次类推,意味着这些梧桐木的价格总和其实就是一个第一项为1,公比为2,项为35的等比数列。 当用户选择购买梧桐木数量大于31时,比如32-36中这些数字时,这些等比数列的和就是大于。(只是举例,实际上不会以这样的价格出售物品)&br&&/blockquote&在java中,4字节的存放int型变量的范围是-至。在java、c的有符号int型中存储时,数的最高位描述符号位,4字节共32位,除去最高位的符号位,剩下31位,每个位上能表示2个数字,4字节的有符号的整数表示范围为:负整数2^31个,范围为『-1至-』;正整数2^31个,范围为『至1』。 比如下图(注意十进制数字跟二进制表示的变化顺序):&figure&&img src=&https://pic2.zhimg.com/44c0d4bcab80589bdb6dc563e5fbf441_b.jpg& data-rawwidth=&1325& data-rawheight=&366& class=&origin_image zh-lightbox-thumb& width=&1325& data-original=&https://pic2.zhimg.com/44c0d4bcab80589bdb6dc563e5fbf441_r.jpg&&&/figure&&br&当开启格子数字为大于31时,比如32,那么所需费用就是个银两,再买点其他物品,凑成超过的数字,比如又买了3个银子的其他道具,总共花费个银子,在4字节的有符号int中表示出来的结果,变成符号位为1,即负整数。数值位为00 ,也就是00 ,对应十进制的-。程序逻辑上,再判断现有银两是否足够支付此笔花费时,是通过的。当使用当前余额减去这笔花费时,将变成减去一个负数,那么实际上就是加上一个正整数。变成了自己银两账户余额的增长。而余额字段类型是long,则正确的存储了这些余额,溢出漏洞被利用。在C中,使用无符号的数值类型,即可完成数值类型溢出刷钱的行为,但在java中,好像没有无符号的类型。这也可以先确定所有参与计算的数值必须为正整数作为必要条件(游戏业务特性,游戏内所有数字,肯定全为正整数,甚至都不包括零),先做大小判断,再做正正相加,不能得负;负负相加,不能得正。来判断是否发生了溢出问题。&br&&br&&b&金币复制-并发请求&/b&&br&Rpg类型的网页游戏中,多数都有道具出售的功能,直接卖到商店,以及道具材料从商店买入功能。当玩家同时针对买入、卖出两个操作,瞬间大量并发请求时,在服务器的处理逻辑一般有分别的两个进程处理,共享数据分别数据库中的对应账户余额表,如下图:&figure&&img src=&https://pic1.zhimg.com/eff094a140d4b6f424a4_b.jpg& data-rawwidth=&883& data-rawheight=&404& class=&origin_image zh-lightbox-thumb& width=&883& data-original=&https://pic1.zhimg.com/eff094a140d4b6f424a4_r.jpg&&&/figure&&br&&div class=&highlight&&&pre&&code class=&language-php&&&span class=&x&&//卖出&/span&
&span class=&x&&// startTrans&/span&
&span class=&x&&$iBalance = $obj-&getBalance('user1');
//余额50&/span&
&span class=&x&&//UPDATE `role_gold` SET gold = 150 WHERE role_id = 1&/span&
&span class=&x&&if(!$obj-&setBalance('user1',$iBalance + 100))&/span&
&span class=&x&&{&/span&
&span class=&x&&
//rollback&/span&
&span class=&x&&}&/span&
&span class=&x&&//扣除物品&/span&
&span class=&x&&if (!$obj-&delItems($items))&/span&
&span class=&x&&{&/span&
&span class=&x&&
//rollback&/span&
&span class=&x&&}&/span&
&span class=&x&&//commit&/span&
&span class=&x&&//买入&/span&
&span class=&x&&// startTrans&/span&
&span class=&x&&$iBalance = $obj-&getBalance('user1');
//余额50&/span&
&span class=&x&&//UPDATE `role_gold` SET gold = 0 WHERE role_id = 1&/span&
&span class=&x&&if(!$obj-&setBalance('user1',$iBalance - 50))&/span&
&span class=&x&&{&/span&
&span class=&x&&
//rollback&/span&
&span class=&x&&}&/span&
&span class=&x&&//发放物品&/span&
&span class=&x&&if (!$obj-&addItems($items))&/span&
&span class=&x&&{&/span&
&span class=&x&&
//rollback&/span&
&span class=&x&&}&/span&
&span class=&x&&//commit&/span&
&/code&&/pre&&/div&&br&卖出请求的处理进程为1,买入请求的处理进程为2。在进程1还没将结果写入到DB时,进程2也从DB读取到余额为50。这是,两个进程拿到的余额信息都是50。进程1按照逻辑代码,计算出剩余余额是150;进程2计算出的剩余余额是0。最后,不管那个进程最后写入余额,都是错误的结果。(注:这里的代码逻辑操作,跟mysql事务无任何关系,事务只能保证单个进程的事务范围内多条语句都正确执行,或回滚。比如能保证扣钱成功,且物品删除掉的两个语句都正确执行。能保证其中之一的语句执行失败时,都正确回滚。)&br&其实,在事物开启时候,SELECT语句是否可以取到最新的数据,或者是否需要等待锁释放,取决于MYSQL的事务隔离级别。在MYSQL的事务隔离级别中,有一下几种隔离级别:&figure&&img src=&https://pic3.zhimg.com/64ca8fda136e39a792a12_b.jpg& data-rawwidth=&780& data-rawheight=&271& class=&origin_image zh-lightbox-thumb& width=&780& data-original=&https://pic3.zhimg.com/64ca8fda136e39a792a12_r.jpg&&&/figure&&br&&ol&&li&READ-UNCOMMITTED(读取未提交内容)级别&br&&/li&&li&READ-COMMITTED(读取提交内容&br&&/li&&li&REPEATABLE-READ(可重读)&br&&/li&&li&SERIERLIZED(可串行化)&br&&/li&&/ol&对于READ-UNCOMMITTED,可以读取其他事务中未提交的数据,而且据说性能还高不到哪里去,几乎没有在实际应用中使用;对于READ-COMMITTED,在同一事务中,会因为其他事务随时可能有新的commit,导致同一select可能返回不同结果。这个也不适合游戏业务;再说第四个SERIERLIZED,只要事务开启,所有其他查询,均排队等待该事务提交之后,对于上面提到的卖出买入情况,第二个事务的SELECT操作,不会立刻返回,会处于锁等待状态,一直到前一个事务结束。这个隔离级别,虽然能避免上面的问题,但性能较差,一般不会去使用。而REPEATABLE-READ隔离级别,也是mysql默认的隔离级别,从功能上,比较符合游戏业务需要,也应该是广大webgame架构中mysql的默认隔离级别。&br&&br&对于这个问题,你可能很快就给出解决办法,把UPDATE语句改为UPDATE `role_gold` SET gold = gold
+ 100 WHERE role_id = 1或者UPDATE `role_gold` SET gold = 150 WHERE role_id = 1 AND gold = 100来解决,但这种多个事务同时操作修改多个表的多条记录时,还容易引发死锁问题,比如: &a href=&//link.zhihu.com/?target=http%3A//www.cnxct.com/error--deadlock-found-when-trying-to-get-lock-try-restarting-transaction/& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&webgame中Mysql Deadlock ERROR )错误的排查历程&/a& 而且,当条件为跨表内数据是否存在,或者另外条件不在MYSQL中,而在其他网络接口的响应中时,如何做呢?&br&&br&&b&金币复制--逻辑漏洞&/b&&br&引用DNF的漏洞新闻 《利用网游漏洞狂刷游戏币赚钱 玩家自曝3天赚17万》&br&&blockquote&玩家曝出刷币漏洞 一个游戏道具可刷400人民币&br&该漏洞到底是什么?原来游戏中“云幂袖珍罐”这个道具,可以开出2件一样的游戏装备,还有极少几率开出游戏币,开出的装备不值钱,但如果开出金币了,则分为5000万、8000万以及1亿游戏币。而1亿游戏币,按正常市场行情,可在交易网上卖400多元人民币。据玩家称,在游戏中,角色的装备是需要用包裹来存放的,不过目前角色的包裹最多只有48格,也就是只能存放最多48件装备。漏洞就是利用包裹的有限空间,存放47件装备(存放满了又无法开罐子),只留下一格空位,而在开“云幂袖珍罐”出装备时,就会因包裹空间不足,而导致开罐失败,而罐子还存在。玩家继续开罐,直到出现金币,但金币不会占据包裹的空间,因此开罐成功,然后罐子消失。发现这个漏洞后,部分玩家狂刷游戏币,然后马上在第三方交易平台出售游戏币,兑换成现金。&/blockquote&这种问题,都是研发人员逻辑不严谨导致,这种问题,也较难发现。规避方式可以依赖下面提到的『运营数据监控』。&br&&br&&b&道具复制--背包整理&/b&&br&跟上面的卖出、买入一样,同时穿装备、整理包裹。在设计时,可能会将身上装备设计在装备表中;将不在身上的装备,设计到背包表中。当同时进行穿装备跟整理包裹的请求并发时,也会发生跟上面卖出,买入的情况,线程1读取DB,发现包裹里有这装备,然后准备删除背包表的这条记录,当准备写入到装备表时,另外一个整理包裹请求的线程来了,读取了整个背包表,进行道具的合并、排序。这时,之前的线程将这个装备写入到装备表,并删除了背包表里的数据,并提交事务。这个穿装备的所有操作都是合理、正常,且正确执行的。但另外一个整理背包的线程读取了之前的背包表里的数据,包括那件被穿上的装备。在游戏中,整理背包需要对可堆叠道具做堆叠操作的,意味着需要合并多个道具,删除部分道具。这意味着这里的操作,当前cgi线程的内存中的数据,将都会以覆盖的形式,写入到DB中,那么意味着,之前被穿到身上的那件装备,也会重新被写入到背包中。那就变成两张表里出现了两个相同唯一ID的相同属性的道具。玩家就可以把背包中的这个道具出售给其他玩家。&br&&br&在java或者C之类程序中,数据放内存中的游戏,也会存在这个问题,除非做读锁,但读锁会带来锁等待,锁等待会导致线程被占用,阻塞后面请求的处理,堆积大量请求。导致系统负载升高,服务器繁忙,以至于无法响应。好了,大约理解道具复制的形成原因了吗?这个问题,我们从根本原因想想,问题到底出现在哪里?如何规避呢?细心的同学不难发现,对于穿装备的操作结果,会对下一个请求产生影响的,当前操作未得到服务端响应之前,服务端是不能处理下一个响应的。对此,我们做了响应处理锁--『用户并发请求锁』。&br&&br&用户并发请求锁的实现,php中session以文件形式存储时,php会对session文件加锁,不释放(如果不特意执行session_write_close),知道当前响应完成。另外一个线程才可以正常读取,这简介的形成了单个用户的并发请求锁,但是,后面的进程一直处于等待状态,也会占用一个php-fpm进程,阻塞其他用户的正常请求对php线程的使用。为此,我们使用NOSQL的K-V形式结构,以user_name为key的形式,实现用户并发请求锁,比如 redis的setnx接口,原子性判断操作有则返回false,,没有就添加一个,返回true。那么,对于下一个请求,setnx时,返回false,有这个key了,那么立刻抛出异常,结束响应,FLASH根据异常内容,提醒用户不要进行恶意操作。即不会发生并发请求,又不会阻塞请求处理。同时,在请求结束的析构函数里,对这个锁进行删除操作,不影响下一个正常请求。若因为程序异常,发生语法错误,导致析构函数没法执行,没有删除用户锁时,可以在生成锁的时候,设置过期时间,比如5秒,甚至2秒,利用nosql的过期机制,实现用户解锁,避免用户长时间无法正常游戏。&br&&br&&b&类CC攻击-多用户共享资源锁的timebomb&/b&&br&我们现在研发的项目,是以NOSQL Redis作为DB,来存储数据的,redis并没有成熟的事务处理机制,watch甚至算不上关系型数据库中的事务处理。对此,更需要对表进行加锁解锁。java之类语言的项目,很多都是直接操作内存的,更是需要资源锁,来解决并发问题,解决多个请求操作同一份数据的问题。公司有另外一个项目,出现过一次因为锁的颗粒度较大,带来的锁等待timebomb的问题,也导致了线程繁阻塞忙,请求堆积,系统负载上升,导致宕机的问题。这个项目的锁是针对所有用户的锁,每个用户的请求发来时,当前线程会对所有用户的数据加锁,直到响应完成,才释放掉。这么做,是为了解决因当前操作,会影响到其他用户数据,比如多人PK,多个玩家之间的交互。&figure&&img src=&https://pic3.zhimg.com/3e703f8e590e545e0fa0b5a46183fa5e_b.jpg& data-rawwidth=&746& data-rawheight=&363& class=&origin_image zh-lightbox-thumb& width=&746& data-original=&https://pic3.zhimg.com/3e703f8e590e545e0fa0b5a46183fa5e_r.jpg&&&/figure&&br&当其他请求一并发来时,那么资源会立刻被锁住,直到上一个请求结束,才释放锁,那么其他线程都处于等待状态。用户基数小时,是看不出来锁带来的影响的,内存操作都比较快。当用户基数大时,或者说请求数增大时,后面的请求的等待时间会越来越长,超过webserver的等待时间,直接返回timeout,不能正常提供服务。&br&&br&这种问题的发生,是因为锁的颗粒太大了,不应该将所有用户都锁住,最好细化到当前请求所影响到的单个用户,只锁住单个用户的数据。这样,才减少timebomb的发生。&br&&br&另外,&a class=&member_mention& data-editable=&true& data-title=&@余弦& data-hash=&afea1df0fc2b1fd51e9ca04d8b166f80& href=&//www.zhihu.com/people/afea1df0fc2b1fd51e9ca04d8b166f80& data-hovercard=&p$b$afea1df0fc2b1fd51e9ca04d8b166f80&&@余弦&/a& 提到很多webgame 的前端做了判断,而后端没做判断的问题,这种问题,实属不该存在。在我们的项目中,后端做的验证判断,远比前端多的多。有时候,为了界面上的动画表现,前端flash一般会在用户操作之后,立刻渲染,然后,再根据后端响应,决定是否继续做界面元素改动。比如脱装备,玩家操作时,会先渲染装备从角色面板,跳到背包里的动画,然后,再根据后端响应结果,决定是否回滚动画。这样,避免显得操作后,一定时间的反映迟钝假象,以提高用户体验。当然,后端是一定会做判断的,判断角色背包是否有空格之类。现在的webgame研发,一般都不会存在前端判断,而后端不判断的做法了。如果有,也应该是个别遗漏情况。&br&&br&&b&其他&/b&&br&去年的time33算法的hash dos的问题,使用json消息格式的webgame一定要注意,php只是在接收请求时,做了最大数量的限制。但在json解码之后的数据中,是没有处理的。这里千万别忘记了。&br&&br&&b&运营数据异常监控&/b&&br&再完善的防御措施,都仍会有安全漏洞。适当的监控措施,也一定要有,监控等级、金币、游戏币、经验、珍贵物品的变化等等,一旦发现,立刻报警,在漏洞未扩散之前,第一时间去修复漏洞,以减少损失,维护游戏平衡。&br&&br&--------------------------------10-16 21:08补充---------------------&br&&b&日志&/b&&br&日志系统一定不能漏掉,所有操作,必须写入日志,当安全事件发生后,可以作为各种数据回滚,交易纠纷处理的可靠数据。也是作为数据监控的最准确的数据来源。&br&&br&--------------------------------10-18 16:05补充---------------------&br&补一个真实的故事。去年6月份,我们项目新上线一个系统,以及腾讯充值接口V2升级V3,涉及充值代码改动,研发测试、策划测试、QA测试完毕之后,上线到个别服务区,观察情况。每次新版本上线,整个项目组都会持续观察数据情况,尤其是充值总额, 10、50、100的涨,突然,总额下降了。充值总额下降了,这可是总额啊,只能增加,是不会下降的。肯定哪里有BUG,DBA直接看binlog,查充值记录相关的SQL语句,最后发现充值系统的sql语句为&b&UPDATE table set gold_num = $num,is_pay =1&/b& ,没有WHERE,没有WHERE啊,多么弱智的BUG,这尼玛能容忍么?肯定要拉出来,弹JJ弹到死。当我从SVN日志中看到涉及这个文件的修改者时,我立刻石化了,悄悄的修了bug,默默的上传......&br&&figure&&img src=&https://pic1.zhimg.com/7edc931b5268afa2d51f594_b.jpg& data-rawwidth=&1280& data-rawheight=&801& class=&origin_image zh-lightbox-thumb& width=&1280& data-original=&https://pic1.zhimg.com/7edc931b5268afa2d51f594_r.jpg&&&/figure&&br&事情是发生了,原因很弱智。是我自己,忘记写where条件,由于框架封装了,问题并不容易被发现。而且自己测试充值都是正确的,包括后来的策划测试、QA测试,都没有发现问题。所属功劳,就是“&b&数据监控系统&/b&“,所以,我们游戏开发商,第一时间,比用户还早的发现了问题所在。数据监控,一定必不可少。&br&&br&至于修复方案,那要感谢日志系统。每笔充值,都有双份日志,一是各个游戏大区自己的DB中。二是充值中心中。充值中心负责跟其他各大平台对接。这次的事故,影响游戏大区的数据,并未影响到充值中心数据,故可以有据可查。这样,伟大的DBA可以更便捷、放心的修复数据了。对此,不管发生其他刷钱、刷装备、盗号之后的交易纠纷,都可以依赖日志来做处理。日志系统,也一定必不可少。&br&&br&从新功能发布,到发现BUG,到修复BUG,总共历时不到1小时(svn时间可以看出来,16:30对外的),可想而知,数据监控的效果多么值得称赞。新功能上线时,各个测试环节要去做。按大区服务器,做灰度发布也可以更小的减少bug影响范围,减少损失。&br&&br&---------------------------------10月22日 更新------------------------------------------------&br&&b&协议与外挂&/b&&br&本问题的目标是『网页游戏』,『网页』意味着的用浏览器。使用浏览器与服务器通讯的游戏,才能成为浏览器。而浏览器支持普通的http协议的基本网页数据通讯,支持以socket协议的flash插件或者java Applet实现,以及unity3D开发的游戏。就目前业界流行做法上,flash插件做客户端的占了一多半。unity3D的客户端源码安全性我不清楚。flash的源码,众所周知,很容易逆向出来。比如回答者&a class=&member_mention& data-editable=&true& data-title=&@Seraph& data-hash=&6d7d82ec45bc6a3bcc630& href=&//www.zhihu.com/people/6d7d82ec45bc6a3bcc630& data-hovercard=&p$b$6d7d82ec45bc6a3bcc630&&@Seraph&/a& 提到博客 &a href=&//link.zhihu.com/?target=http%3A//www.gungov.com/%3Fp%3D10& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&Seraph's Blog&/a& 中所写方式逆向还原出flash源码。当源码很容易被拿到后,那么通讯协议的消息格式,也是一览无余,那么各种外挂实现起来,易如反掌。&br&&br& 在我们项目中,对于外挂的态度,一般都是放在最后考虑。当项目前期,没有多少知名度,也没盈利时,我们开发商是不会把精力放到防外挂上的。而且,天下攘攘,皆为利往,同样的外挂编写者也会认为这无利,会无视它。当然,也不是无视外挂的存在,我们一般这么对待它:&br&&ol&&li&策划初期,减少重复性劳动的玩法,以免玩家感到疲劳,给与外挂市场。若重复性不高,那么外挂可用性也就差。&/li&&li&游戏本身提供一些内挂,替玩家实现重复性劳动。&/li&&li&前后端都是统一网关,后期实现协议加密也比较简单,快捷。抵挡住初级外挂编写者,当然,中国人才济济,大牛多多。&/li&&li&定时更换通讯密钥,增加外挂编写者的劳动量。&/li&&li&游戏内部机制限制,比如体力值。&/li&&li&人肉判断,验证码之类(网页游戏目前还没火到这个地步)。&/li&&/ol&&br&其实,网页游戏中的最大、最多的漏洞,几乎都是游戏业务金币复制、装备复制等技术性漏洞,以及游戏业务判断不严谨、条件不合法之类的逻辑漏洞,而常见的web漏洞在网页游戏里,显得并不突出,种类数量也少,也较为容易发现,修复。&br&&br&=============================日更新================&br&&br&&b&我不知道这个回答会不会还有人看到,因为这不是一个新的回复,不会出现在各位的消息提醒中,可能不会涨粉,但我觉得还是有必要在更新一下,希望以后通过搜索,看到这篇文章的同学,对你们有帮助。SO,我还是更新一下。&/b&&br&&br&又一起webgame中的刷道具的问题,问题发生在背包内道具移动的业务上:&br&&br&&ol&&li&当从A位置移动到B位置时,若两者是同一类型,且可叠加(两者都是绑定或不绑定),则叠加数量到其中一个上,并销毁另外一个。&br&&/li&&li&若不是同一类型,或不可叠加,则两者呼唤位置。&br&&/li&&li&若目标位置为空,则把物品从A移动到B。&/li&&/ol&如果这个功能由你看写,你如何写?&br&&br&程序员的代码在 &a href=&//link.zhihu.com/?target=https%3A//gist.github.com/cfc4n/b7d794d3cb663dd86aad& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&gist:b7d794d3cb663dd86aad&/a&&br&&br&&div class=&highlight&&&pre&&code class=&language-php&&&span class=&x&& /**&/span&
&span class=&x&&
&span class=&x&&
* 背包内道具移动&/span&
&span class=&x&&
* @param string $former
源位置格子号&/span&
&span class=&x&&
* @param int $target
目标位置格子号&/span&
&span class=&x&&
&span class=&x&&
public function MoveItemAction($former, $target)&/span&
&span class=&x&&
&span class=&x&&
$former = trim($former);&/span&
&span class=&x&&
$target = intval($target);&/span&
&span class=&x&&
if(!$former || $target & 1)&/span&
&span class=&x&&
&span class=&x&&
return $this-&_view-&error(MLanguages::COM__PARAMETERS_NOT_LEGAL);
//参数异常;&/span&
&span class=&x&&
&span class=&x&&
&span class=&x&&
//获取道具信息&/span&
&span class=&x&&
$former_item = $this-&__mItems-&getNumberItem($former);&/span&
&span class=&x&&
if(!$former_item)&/span&
&span class=&x&&
&span class=&x&&
return $this-&_view-&error(MLanguages::PACK__NOT_EXIST);
//使用道具不存在;&/span&
&span class=&x&&
&span class=&x&&
&span class=&x&&
$former_md5id = $former_item['md5_id'];&/span&
&span class=&x&&
$former_num = $former_item['number'];&/span&
&span class=&x&&
$former_base = $former_item['item_id'];&/span&
&span class=&x&&
$former_sup = $former_item['superpose'];&/span&
&span class=&x&& &/span&
&span class=&x&& &/span&
&span class=&x&&
//开启事务&/span&
&span class=&x&&
$this-&__mRole-&beginTrans();&/span&
&span class=&x&&
$target_item = $this-&__mItems-&getNumberItem($target);&/span&
&span class=&x&&
......&/span&
&/code&&/pre&&/div&&br&其出现问题部分就是如上这些,大家看代码时,会认为这前期的判断都是正确的,符合业务逻辑的,问题却发生了“来源格子”跟“目标格子”的判断,没有判断这两个参数是不是同一个位置,若是同一个位置,那么上面的所有业务逻辑判断都是合法的,下面的代码中,将根据这“两个”道具的属性进行堆叠,然后就发生了道具复制的问题.....&br&&br&修复方法想必大家都会,就是判断来源位置目标位置是否相同&div class=&highlight&&&pre&&code class=&language-php&&&span class=&x&&if(!$former || $target & 1 || ($former == $target)) //判断来源位置目标位置是否相同&/span&
&/code&&/pre&&/div&&br&&b&如果是你,你会犯这样的错误吗?&/b&&br&&br&以上为鄙人的游戏研发经验,欢迎讨论。时间仓促,再加上感冒前期症状,轻微头晕,难免思维混乱,敬请谅解。 此回答禁止转载,代码高亮效果不理想,其他格式没有。鄙人BLOG中文章
&a href=&//link.zhihu.com/?target=http%3A//www.cnxct.com/experience-with-webgame-of-security-and-defense/& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&webgame中常见安全问题、防御方式与挽救措施&/a& 可以转载,但务必遵循博客所示协议。&br&&a class=&member_mention& data-editable=&true& data-title=&@葛巾& data-hash=&2dfc9eff7542eb& href=&//www.zhihu.com/people/2dfc9eff7542eb& data-hovercard=&p$b$2dfc9eff7542eb&&@葛巾&/a& 大神能给个赞么?&br&&br&葛大神点赞了,居然看到了,居然点赞了。内牛满面。
谢邀。。。。 压根没人邀请我,好像这个词成为知乎的通用开篇词语。 十月一的假期间,看到这个问题,当时没怎么在意。几天后,在微博上,又看到这个问题,冥冥中,有种想回答的冲动。上周六时,研发部门内部周会时,听到其他项目组的一个整型溢出问题,导致…
拜托,最重要的原因,难道你们不觉得Chrome真的很快吗&br&&br&这里有一篇文章,来自google的程序员&a href=&//link.zhihu.com/?target=https%3A//www.igvita.com/& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&lya Grigorik&/a&谈Chrome的高性能网络设计(需要翻墙):&br&&a href=&//link.zhihu.com/?target=https%3A//www.igvita.com/posa/high-performance-networking-in-google-chrome/& class=& external& target=&_blank& rel=&nofollow noreferrer&&&span class=&invisible&&https://www.&/span&&span class=&visible&&igvita.com/posa/high-pe&/span&&span class=&invisible&&rformance-networking-in-google-chrome/&/span&&span class=&ellipsis&&&/span&&/a&&br&&br&专业方面的优化就不在这里阐述了,可能大部分人对计算机网络原理还不是那么理解。摘一些大家能看得懂的优化:&br&&br&1. Chrome's Predictor:&br&&br&Chrome内置有非常强大的预测系统,当你在浏览器的地址栏上(Omnibox)一边输入网址的时候,Chrome就已经根据你的输入尝试去判断你要访问网址,并且开始加载(或者DNS解析,与其他浏览器不一样,Chrome使用的并非是系统自带的DNS解析Client,而是使用自己内置的DNS解析工具)。&br&&br&所以有时候难道你们不觉得网页是秒开吗(至少我自己有这样的体会),在输入完网址敲回车的时候就能立即打开网页。因为Chrome早就猜到你要访问,已经在后台加载好了(你可以这么理解:Chrome已经为你打开了一个你想访问网站的标签页,但是暂时隐藏了,在你敲击回车的那一刹那,Chrome把它显示出来了,所以你会觉得秒开)。&br&&br&既然是预测,当然不是百分之百准确的。它也会根据用户的输入不断的调整,并记录不同关键字访问站点的概率,并且这些你都可以看到,对用户是透明的:&br&&br&请在浏览器中输入 chrome://predictors&br&&br&&figure&&img src=&https://pic4.zhimg.com/bec3b3aa09fb81a05e01d7_b.jpg& data-rawwidth=&744& data-rawheight=&310& class=&origin_image zh-lightbox-thumb& width=&744& data-original=&https://pic4.zhimg.com/bec3b3aa09fb81a05e01d7_r.jpg&&&/figure&&br&然后你就能看到这样的图片(这个截图不是我个人的),右边的Confidence栏就代表了对这个关键字和用户可能访问的对应网站的概率,当然是越高越好,hit count和miss count字段的含义当然也不言而喻了&br&&br&不仅仅是在地址栏输入的时候,甚至在你鼠标悬浮到某一个链接上的时候,Chrome就开始去对链接的主机进行DNS解析和TCP连接&br&&br&&br&2. Chrome目前采用的是 process-per-site模式,也就是说同一站点(可能多个tab标签)共用同一个进程(其他的模式有:一个标签使用一个进程,整个浏览器使用一个进程),这样的好处是保证了某一个站点的崩溃不会影响到其他站点。在IE下面你肯定有这样的经验:打开某个网站的时候卡住了,导致了整个IE都不能响应了。但是Chrome这么做避免了这个问题。并且多个站点共用的话能够保证建立起来传输数据的管道重复利用,不用发出新的请求,建立新的链接。&br&&br&BTW:知道为什么访问一个网址为什么慢吗,时间去哪儿了吗?&br&&br&&figure&&img src=&https://pic2.zhimg.com/eca1f0cb32cb777d726b3fd_b.jpg& data-rawwidth=&930& data-rawheight=&529& class=&origin_image zh-lightbox-thumb& width=&930& data-original=&https://pic2.zhimg.com/eca1f0cb32cb777d726b3fd_r.jpg&&&/figure&&br&&br&大部分的时间都花在了网络请求上。这也是为什么Chrome要在网络优化上下大功夫。排名第二位的是Javascript脚本的执行。个人觉得现在的V8引擎似乎不算很大的优势了,IE也说自己的执行效率高,Safari也说自己的执行效率高。但个人没有用banchmark(性能基准测试)测过。&br&&br&&br&你们想看看自己Chrome里有关网络的一切?请在地址栏里输入:chrome://net-internals&br&什么DNS、Cache、Prerender(上面说的预先加载的页面)、目前可用的Socket都一览无遗。当然要你们感兴趣和看的懂才行。&br&&br&暂时觉得以上两点还算比较通俗的,可以和大家分享。&br&&br&注意,以上的优化方案主要存在于桌面端,而并非在移动端。移动端的浏览器架构和桌面端会有不同&br&&br&&b&最后:这一切都是需要代价的!代价就是占用内存和CPU资源!但这样就有可能拖慢你的电脑!所以见仁见智吧!&/b&&br&&br&其他浏览器有没有做这方面的工作呢?我不知道,或许有吧,但是我没有找到其它浏览器优化方面的文献和资料。&br&&br&给大家一些浏览器厂商自己的官方博客,每次有了更新和提升一般他们都会发布在博客上。但说实话都不如Chrome来的这么彻底、透明:&br&&br&Chrome:&a href=&//link.zhihu.com/?target=http%3A//chrome.blogspot.com/& class=& external& target=&_blank& rel=&nofollow noreferrer&&&span class=&invisible&&http://&/span&&span class=&visible&&chrome.blogspot.com/&/span&&span class=&invisible&&&/span&&/a&&br&IE:&a href=&//link.zhihu.com/?target=http%3A//blogs.msdn.com/b/ie/& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&IEBlog - Site Home&/a&&br&Wekit:&a href=&//link.zhihu.com/?target=http%3A//www.webkit.org/& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&The WebKit Open Source Project&/a&&br&&br&--------更新1.0:&br&&br&针对楼主的几个问题回答一下:&br&&br&&blockquote&但 Chrome for Mac 的页面滚动效能、耗电量、内存和 CPU 占用、JS 加载速度都没有 Safari 好&br&&/blockquote&Chrome的确耗电量和使用内存和CPU更多,但就像我上面说的,这些消耗都是为了保证更好的浏览体验,在后台做了非常多的优化动作。IE的占用资源想必很少吧,但用IE浏览网页也一定很不爽吧&br&&br&JS加载速度这个并不准确,个人理解应该为JS的执行效率。我上面也提到了,这两年IE和Safari的脚本执行引擎都有突飞猛进,在各种大会上都在展示和其他引擎执行的效率对比,并不输于Chrome。但我想说的是,其他的浏览器厂商仅仅是在这一两年追赶上来而已。如果拿倒退一些年的数据相比,Chrome还是非常有优势的,也是它因为“快”而积累了相当一部分用户的原因。&br&&br&&blockquote&什么插件是 Chrome 独占而且不可替代不可或缺的吗&br&&/blockquote&&br&必须承认Safari的拓展一定不如Chrome丰富。但我觉得这可能和苹果没有做这方面的推广和鼓励开发有关(应该不会存在开发方面的技术问题)。其实Firefox的拓展做的非常好,相对Firefox,Chrome也只是后来居上而已,楼主可以不必纠结于Safari与Chrome,Firefox也是一个不错的选择。&br&&br&--------更新2.0:&br&&br&有的朋友在答案下给我留言,告诉我搜狗浏览器和Safari也有类似于预先加载的功能。我还没有去关注过,所以在文章里暂不给出肯定的回答——我相信有。其实有也并不稀奇,看这么多年的浏览器发展,不同浏览器衍生出来的优化方案都是很相似的,比如从IE8开始的Pre-loader机制(没错,这一次IE遥遥领先于其他浏览器喔):&a href=&//link.zhihu.com/?target=http%3A//andydavies.me/blog//how-the-browser-pre-loader-makes-pages-load-faster/& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&How the Browser Pre-loader Makes Pages Load Faster&/a& ;又比如使用GPU加速。&br&&br&&b&但是:&/b&所谓的优化不仅仅是这一项而已,我在开头举例的哪一篇文章中描述了非常多Chrome底层做的优化,在知乎上不是三言两语能够说清楚的,而我相信这些细节并不是大多数浏览器可以做到的;可能还涉及到Chrome架构的设计,有兴趣的同学可以看一看整个Chrome内部的架构设计文档:&a href=&//link.zhihu.com/?target=http%3A//www.chromium.org/developers/design-documents& class=& external& target=&_blank& rel=&nofollow noreferrer&&&span class=&invisible&&http://www.&/span&&span class=&visible&&chromium.org/developers&/span&&span class=&invisible&&/design-documents&/span&&span class=&ellipsis&&&/span&&/a&。其中一些机制设计是非常值得借鉴的。&br&&br&Chrome的优秀也不仅仅体现在对用户的友好。如果你对google的每年I/O大会和 &a href=&//link.zhihu.com/?target=http%3A//html5rocks.com& class=& external& target=&_blank& rel=&nofollow noreferrer&&&span class=&invisible&&http://&/span&&span class=&visible&&html5rocks.com&/span&&span class=&invisible&&&/span&&/a& 有关注的话,每年它们都不间断会针对开发者提出一些新的开发者工具特性。这也是其它浏览器所已不能及的。&br&&br&&br&--------更新3.0&br&&br&很多朋友都提到了flash的原因,个人理解是不是Chrome内置的Flash比较占资源,而使用操作系统内的比较不占? 那么可以停用Chrome内置的flash:&br&&br&请在浏览器地址栏输入 chrome://plugins/
在打开的页面中搜索 Adobe Flash Player&br&&br&&figure&&img src=&https://pic3.zhimg.com/7e6c95aa95ba80c13c931fe_b.jpg& data-rawwidth=&598& data-rawheight=&400& class=&origin_image zh-lightbox-thumb& width=&598& data-original=&https://pic3.zhimg.com/7e6c95aa95ba80c13c931fe_r.jpg&&&/figure&&br&把上面一个内置flash停用吧&br&&br&--------更新4.0&br&&br&1. 很多朋友在留言我的IE也很快呀,但请问你使用的是那个版本的IE呢?我承认IE10或者IE11或许不会比Chrome逊色,但是要知道至少在大陆境内使用率最高的仍然是IE8,IE9的市场份额甚至都不如IE6或者7,来源请参考:&a href=&//link.zhihu.com/?target=http%3A//tongji.baidu.com/data/browser/& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&浏览器市场份额&/a& ,那么你再使用IE6-8之后和Chrome对比一下吧,这应该是大部分人的体验。&br&&br&2. 如果你只是做一些简单的网页浏览,比如看文章,上知乎,上百度。或许看不出什么差别,但是如果你浏览的网页是富交互类型,有非常多的脚本(甚至页面小广告),速度还是有区别的。这就好比5k的电脑和10k的电脑上网办公都没多大差别,但是玩起大型游戏来就立马见高低了。&br&&br&3. 最后,各位总是谈感觉感觉感觉……我觉得咱们还是以数据说话吧,请使用这一个网站:&a href=&//link.zhihu.com/?target=http%3A//www.webpagetest.org/& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&WebPagetest - Website Performance and Optimization Test&/a& 对你浏览的网页做一些测试,可以选择不同浏览器的不同版本,测试完以后可以对比一下比如DOMContentLoaded,SpeedIndex等(非常多的)参数指标,相信结果会一目了然的
拜托,最重要的原因,难道你们不觉得Chrome真的很快吗 这里有一篇文章,来自google的程序员谈Chrome的高性能网络设计(需要翻墙):
专业方面的优化就不在这里阐述了,可能大部分人对计算机网络原理还不是那么理解。摘一…
指令饥饿更多的是硬件上的概念. 它指处理器核心中, 前端取址译码的速度小于后端执行的速度, 造成了处理器空有执行单元等待, 却没有指令供其执行的状态.&br&&br&为了讲清前后端的问题, 贴一张Core处理器架构的简图: (取自Intel架构优化手册)&br&&figure&&img src=&https://pic3.zhimg.com/3b7d5bba85fe19b823c2_b.jpg& data-rawwidth=&649& data-rawheight=&480& class=&origin_image zh-lightbox-thumb& width=&649& data-original=&https://pic3.zhimg.com/3b7d5bba85fe19b823c2_r.jpg&&&/figure&简化的说, 绿色部分是前端, 主要包括&取址&, &译码&, &重命名&这么几个单元. 它最重要的特征是指令在此是顺序执行的, 且指令宽度一般大于1. 以这个Core架构为例, 译码器每个周期最多可以翻译4条指令, 并顺序送给后端.&br&&br&而橙色部分是后端, 主要包括&保存站&, &重排序&, &执行命令&这么几个单元. 它最重要的特征是指令在这里是乱序执行的. 乱序执行非常显著的提升了处理器的运行效率. 如上图所示, 这里的Core架构有5个执行单元, &ALU Branch&, &ALU FAdd&, &ALU FMul&, &Load&, &Store&, 因此Core结构的后端每个周期允许5个指令并行执行.&br&&br&&b&指令饥饿, 就是说后端比前端执行的更快.&/b&&br&简单的说, 假设有一段代码在Core架构上运行. 前端每个周期只能翻译出来2条指令, 而后端每个周期却能执行5条指令. 那么很快前端的输出就无法满足后端的需求了. 后端有一些执行单元处于无事可做的状态, 于是就发生了&指令饥饿&.&br&&br&指令饥饿的原因我想到的有二&br&一个是频繁的Taken Branch. 由于每次分支预测失败都会刷新全部前端流水线, 所以造成前端效率急剧下降, 甚至ICache Miss. 频繁的预测失败会导致后端无事可做.&br&另一个是指令格式, 比如非对齐的指令就会执行的很慢. 比如Core架构如果遇到改变操作数长度的指令(OS/AS前缀), 前端就会执行的很慢. 根据处理器前端设计的不同, 有处理器会对特殊指令敏感, 比如有处理器每一个周期只能翻译一条Branch指令. &br&&br&其中, 指令格式的问题, 更应该由编译器来解决.&br&&br&为了从软件上解决Taken Branch的问题, 我想应该注重优化Branch相关指令. 比如减少不必要的分支; 将频率高的代码放在if而不是 尽量把冗余的条件判断条件提到for循环的外面等等. 这里仅知一二, 还请各位高人补充.&br&&br&===============================(完)================================&br&另提一句, 指令饥饿之所以是一种异常状态, 是因为大部分情况下, 处理器核心的后端效率是小于前端的. 一个好处理器应该尽量让后端满载, 而不是前端. &br&2011年AMD发布的Bulldozer处理器架构, 就是因为2核心共用一个4发射的前端, 才使得后端空有性能却无事可做.
指令饥饿更多的是硬件上的概念. 它指处理器核心中, 前端取址译码的速度小于后端执行的速度, 造成了处理器空有执行单元等待, 却没有指令供其执行的状态. 为了讲清前后端的问题, 贴一张Core处理器架构的简图: (取自Intel架构优化手册) 简化的说, 绿色部分是前…
既然楼主提到“低电平”,看来对数字电路是有一点了解的。&br&&br&那么,翻开数字电路相关教材,最前面几页。&br&&br&一般它都会告诉你,三极管/场效应管类似继电器(一种通过线圈产生磁场、然后用磁场控制物理开关的通断与否的设备);在它一个管脚上输入/切断电压信号,另一个管脚就会出现高/低电平。&br&&br&PS:继电器是一种利用电磁铁控制的开关;当向电磁铁通电时就产生磁场,而这个磁场就会吸合或者分离开关,从而实现“以微弱电流控制另一条电路的通断”这个功能。&br&其中,平常触点接触使得被控制电路导通、给控制它的电磁铁通电后就使得开关断开的那种继电器,就等效于非门。三极管拿来当开关使用时,和这种继电器效果几乎一样。&br&&br&&br&以上,就是数字电路的基础。&br&&br&你敲入的任何东西,最终就是通过类似的东西/机制储存的;所谓“指令”,其实就是“某个命令码“(一般叫机器码),这个”命令码”会改变CPU内部一堆“开关”的状态,以激活不同的电路;然后数据(前面提到过,它也是用三极管/场效应管的导通与否“记忆”的)利用类似的机制,被送入这个被“指令”激活的电路——这些电路是工程师们利用最最基础的三极管控制原理,用一大堆三极管组合出来的:当数据(某种高低电平的组合)经过这些电路后,就会变成另外一组高低电平的组合:这个组合刚好和“指令”代表的功能所应该给出的结果一致。&br&&br&这段话可能有点难以理解。那么,看下最简单的与门吧:数据有两个,分别通过两条不同的线路进入与门;输出只有一个,必须给它输入两个高电平,它才会输出高电平;否则就输出低电平(这一般简化表述为:只有输入两个1,它才输出1,否则输出0)。&br&&br&——这就是所谓的“与”逻辑;一组这样的“与”逻辑就与计算机指令/高级语言里的“按位与”直接对应。&br&——而按位与这个指令,意思就是选择一组线路,把数据导通到这组“与”逻辑电路之上;然后这组与逻辑电路就会输出两组数据的按位与的结果。&br&&br&&br&&br&——类似的,二进制加法,1+1=0(同时进位);1+0=1;0+1=1;0+0=0:这可以用一个异或电路来模拟(因为异或电路的规则就是1+1=0、1+0=1、0+1=1、0+0=0);但这样(同时进位)这个说明就会丢失了,所以需要同时用一个与门模拟高位进位(前面说过,与门就是只有两个1才会输出1,其它输出0;综合异或的说明:这是不是就和二进制加法的规则刚好一致了呢?)&br&&br&然后更高一位就成了两根输入线上的数据相加、再加上进位数据……依此类推:这就是用开关做加法的思路。&br&&br&更多位数的数字的加法,只不过是对应位的二进制加法再加上前一位的进位位罢了,没什么特别的——这样堆起来的一组开关,就叫加法器。&br&&br&——add指令呢,就是选中上面做的那一堆用来做加法的开关们;然后给它们输入数据(不要忘了,两组高低电平而已),这些数据就驱动着构成加法器的那些开关们,噼里啪啦一阵乱响之后(嗯,如果是老掉牙的继电器计算机的话:还记得BUG的故事吗?),电路就稳定在某个状态了:此时,加法器的输出,恰恰就是输入数据的和(当然是这样了。前面讲过,我们是刻意用异或门和与门精心组合,让它们刚好和加法的效果一致)。&br&&br&&br&&br&——其它种种指令,莫不大同小异(更复杂/高级的时钟、流水线啥的……暂时就无视吧)&br&&br&&br&你可以翻翻课本。上面讲过加法器的实现。&br&&br&而加法器和另外一些逻辑电路加起来,就是所谓的ALU(算术逻辑单元,一下子就高大上了有木有)。(当然了,实际上没这么简单。比如至少还要加上时钟信号来打拍子协调开关们的动作、加上锁存器来暂存数据之类——前面提到过,给加法器输入数据,构成加法器的一堆开关需要噼里啪啦一阵才能进入稳定态,然后就可以读出答案:时钟信号就是用来协调这些开关,保证它们都能得到足以达到稳定态的时间用的)&br&&br&&br&简而言之,代码在计算机内部,本身就是一组特定的高低电平组合;而计算机是精心设计的、海量的、用高低电平控制通断的开关组;当给这个开关组输入不同的电平组合时,就会导致它内部出现复杂的开关动作,最终产生另外一组高低电平的组合作为输出;这些开关动作经过精心设计,使得它的行为是可解释、可预测的——解释/预测的规则,就是CPU的指令集。&br&&br&——换言之,在机器内部,一切本来就是高低电平,不存在转换问题。&br&——反而是键盘/鼠标/mic的输入要经过机械过程到数字信号的转换;而视频、音频之类的输出,要经过数模转换再通过其它机制才能变成人可辨识的信息&br&&br&&br&&br&&br&————————————————————————————————&br&我知道,很多人困惑的,可能并不是开关的原理;而是:如果CPU不过是一堆开关的话,它为什么能“听懂”类似“加法”“do...while”这类高大上的复杂指令、甚至做出office、photoshop甚至人工智能这样神奇的

我要回帖

更多关于 华为帐号密码找回 的文章

 

随机推荐