Archive

Archive for February, 2017

如何实现一个真正高性能的spin_lock?

February 21st, 2017 No comments

应用层用spinlock的最大问题是不能跟kernel一样的关中断(cli/sti),假设并发稍微多点,线程1在lock之后unlock之前发生了时钟中断,一段时间后才会被切回来调用unlock,那么这段时间中另一个调用lock的线程不就得空跑while了?这才是最浪费cpu时间的地方。所以不能关中断就只能sleep了,怎么着都存在巨大的冲突代价。

尤其是多核的时候,假设 Kernel 中任务1跑在 cpu1上,任务 2跑在 cpu2上,任务1进入lock之前就把中断关闭了,不会被切走,调用unlock的时候,不会花费多少时间,cpu2上的任务2在那循环也只会空跑几个指令周期。

看看 Kernel 的 spinlock:

#define _spin_lock_irq(lock) \
do { \
    local_irq_disable(); \
    preempt_disable(); \
    _raw_spin_lock(lock); \
    __acquire(lock); \
} while (0)

看到里面的 local_irq_disable() 了么?实现如下:

#define local_irq_disable() \
    __asm__ __volatile__("cli": : :"memory") 

倘若不关闭中断,任务1在进入临界区的时候被切换走了,50ms以后才能被切换回来,即使原来临界区的代码只需要0.001ms就跑完了,可cpu2上的任务2还会在while那里干耗50ms,所以不能禁止中断的话只能用 sleep来避免空跑while浪费性能。

所以不能关闭中断的应用层 spinlock 是残废的,nop都没大用。

不要觉得mutex有多慢,现在的 mutex实现,都带 CAS,首先会在应用层检测冲突,没冲突的话根本不会不会切换到内核态,直接用户态就搞定了,即时有冲突也会先尝试spinlock一样的 try 几次(有限次数),不行再进入休眠队列。比傻傻 while 下去强多了。

Categories: 编程技术 Tags: ,

游戏机模拟器的具体原理是什么?

February 8th, 2017 1 comment

“游戏机模拟器” 注重的是 “严格模拟硬件”,要精确,可以对照 MAME代码,所有问题都能在里面找到对应答案:

第一:模拟 CPU

MAME里实现了各种 68000, z80,mips, sparc, arm,pic16c5x,nec, alpha,等 100 多款你见过的或者没见过的主从协处理器的模拟,虽然都是 switch case opcode,但是不像 lua虚拟机。MAME的 CPU模拟重点在 “精确实现硬件”,除了指令集实现外,还有各种软硬终端/trap/异常处理/IO实现。举个简单例子,一个游戏主机需要 4MHz 的 z80芯片,你就得给我真的按照 4Mhz来跑,每条指令计算周期,不能多也不能少,你要把 4Mhz跑成 8Mhz,游戏玩起来节奏就不一样了。比如以前老游戏机上敌人一多,就会慢下来,你实现模拟器,也得把这种慢下来给实现了。另外很多街机是双处理器,比如一块 68000 + z80,你不能复原老主机的运行速度,一些写的粗糙的游戏 ROM可能会出错。

模拟 CPU重点是 “精细”,比如浮点数误差最好一致,比如中断优先级你得模拟出来,模拟器由于按照 interval 来运行,更容易产生同时多个硬件中断被触发,比如 “手柄按键” ,多核通信之类各种东西加在一起,某个核满负荷运行的情况下,优先级低的可能永远得不到处理,弄错了可能游戏就没法玩了。

第二:模拟总线

总线也有好多规格需要实现,不同基板的总线链接不同cpu 和外设的方式都不一样,还是需要 “精确模拟”,比如 ROM /RAM / IO 地址映射,一些大容量游戏需要 ROM 的 BANK 切换,还有一些游戏会在卡带上带有扩展内存,除此之外还要正确模拟各种异常,比如某些 RAM,读写奇数地址会出错,要给对应 CPU发送异常信号,某些老点的 RAM只能读写 16bit的 WORD,不能读写 DWORD或者 BYTE,否则都无效。这些你都得模拟到位了,有些有 BUG的游戏,错误的写了内存,在真实主机上,写操作直接被硬件忽略掉了,没有损伤,但软件模拟不注意执行了那条指令结果就不一致了。

第三:外设模拟

音频芯片,I/O,图形加速芯片,随机数发生器,音视频输出,摇杆,时钟等。有些外设是有bug的,你也要把这些历史上的硬件bug给模拟进去,不然游戏可能行为不一致。

第四:调试系统

提供终端和接口可以内存 DUMP,反汇编,修改指令和数据,保存现场之类的。

有史以来出现过的游戏机硬件数不胜数,但是他们用到的芯片或者硬件是有限的,比如z80和 68000这类流行的芯片,具体每台主机其实就是一份配置文件,包含使用那种总线,哪些cpu,分别按照什么速度来运行,内存I/O布局,关联哪些外设,BIOS和启动加载等信息。

总之是个辛苦活,你需要一本硬件手册,然后边查边弄。

如果你嫌 MAME太复杂庞大,再推荐一个 gens 的代码,只针对世嘉16位机的 Windows实现,条理很清晰,很多比世嘉简单的 FC模拟器写的都没有 gens那么结构清晰,简单易读。它就不像MAME那么大而全,很多步骤实现的很直接不需要配置那么多,代码量也不大。

现在新进的模拟器很多,没机会逐一查看他们的实现细节,只记得有几款比较新的模拟器都是直接裁剪 MAME的部分代码来弄的,因为 MAME里面几乎实现了所有游戏能用的芯片了,拿出来改改参数加点指令集就可以用,比如 MAME里面模拟了 mips,我们裁剪出来实现 PSP模拟器,个别指令有些区别需要改一下,然后我们着重自己实现 PSP里面 MAME没有的硬件部分。

Categories: 游戏开发 Tags:
Wordpress Social Share Plugin powered by Ultimatelysocial