[被迫参加的hackathon] 在线编译系统开发小记

[被迫参加的hackathon] 在线编译系统开发小记

吐槽

因为某种原因我没有参加MSC举办的Hackathon, 不过我的部员们参加了 0.0 然后我本来想去蹭点Pizza之类的东西吃,结果(我感觉他们是给我诱拐过去了) 到了比赛现场之后告诉我, 这里封馆了 Orz 于是乎我就被迫开始写代码(连电脑都没有带只为了去蹭口pizza吃的我TAT[结果最后还没吃到pizza])

实现的是一个简单的在线编译系统 TOC (Toy Online Compiler) 支持多种语言, 下面简单说一下实现的思路和核心的代码

实现思路

简单分析一下这个系统,由两部分组成 ,代码提交模块和代码编译运行模块 ,前者负责将代码交给后者,后者编译运行完毕将结果返回给前者,然后前者将结果展示给用户.

具体实现方式是: 代码提交模块负责将代码文件保存到服务器, 然后通过socket将消息通知到代码编译模块,然后代码编译模块执行完毕的结果再通过通信的方式传递回来, 很显然实现代码提交模块实现难度很容易,主要的问题在后者的编译模块的通信和编译问题, 下面给出编译模块的核心代码

这段代码就是编译模块的核心逻辑, 我使用了一个队列来维护所有在队列内的通信, 队列最多有5个元素,   代码运行起来会持续检查队列是否为空,如果队列不为空,就会取出队列头的socket,处理socket传来的消息,编译相应的代码,然后产生结果通过封装好的socket_write SockWrite,将运行结果回传给代码提交模块,重复上述操作.

为了保证代码的可读性, 封装了一个编译类 actionClass 来进行编译和运行, 编译和运行的方式是通过exec调用系统命令,下面会具体说明

 

实现细节

  •  编译和运行的方式是使用exec调用系统指令, exec指令可以直接将执行结果作为返回值,不过他的结果只能返回stdout里的内容,stderr的内容不能返回, 因此我使用的方式是 将stderr的内容重定向到文件 cerr.txt 然后检测此文件是否为空, 如果不为空的话说明出错了
  • 不过在最初实现的时候出现了这样的问题, 当你提交一个编译错误的代码之后,再提交一个可以正常运行的代码,编译模块仍然会报编译错误, 而如果手动查看这个文件发现这个文件大小为0 , 通过打印filesize(“cerr.txt”)的返回值,发现了 filesize的返回值不为0 ,但是此时cerr.txt文件大小为0 (du -h cerr.txt )  查资料发现,  filesize 的结果会调用缓存的结果  “Note: The results of this function are cached. See clearstatcache() for more details.“[php.net 原文] . 因此在每次取得filesize之后, 都要使用clearstatcache()将之前的状态清除掉
  • 另外遇到的一个问题就是在停止掉后端编译模块之后, 再次启动这个模块会报错 Unable to bind , address already in use , 这个问题在php.net 的官网也有说明

If you want to reuse address and port, and get rid of error: unable to bind, address already in use, you have to use socket_setopt (check actual spelling for this function in you PHP verison) before calling bind:

设置上   SO_REUSEADDR

 

  • 另外代码运行时限是使用Linux中的timeout的功能实现的
  • 当程序执行结果为空的时候(不产生任何输出) 代码提交模块会一直在socket_read()处等待到来消息, 因此通过自己实现了封装好的SockRead和SockWrite 来避免这个问题

 

 

目前实现阶段

  • 支持PHP C++ C Python Ruby 的编译(解释)运行
  • 可以在Raspberry Pi上搭建整个系统

待解决问题

  • 编译时使用sandbox来保证安全性 , 暂定使用chroot来实现sandbox功能
  • 登录和注册没有完全实现
  • 将这个代码布置到拥有防火墙的CentOS服务器上, 两个模块之间不能互相通信(这个问题一直还没解决)

 

Comments are closed.