这段时间大部分在看Go语言的一些东西,为写 判题 和 判题队列管理 做着准备。这几天将 C++ 写的沙箱以动态链接库的形式与Go语言进行整合,这里记录一些遇到的问题。
大改
review 了代码之后发现 沙箱核心代码中并没有用到 std::string 的东西,为了性能就砍掉了一部分,将原有使用string的改为使用const char * , 但是在main函数中仍然在使用(为了偷懒。
接着是 Go语言只支持直接使用 C 的代码,这并不是说C++不行,编译成动态链接库之后, 提供一个C语言类型的接口即可,但如果是使用 C++ 进行编译,则应该使用
#ifdef __cplusplus
extern "C" {
#endif
// funtion define here
#ifdef __cplusplus
}
#endif
的文段将接口包起来,这样编译完成后的链接库中的接口函数前面才能在 链接的时候找到。
改动大概就是这部分。
编译与安装
编译是应该开启 -fPIC
选项。
cmake中加入 add_compile_options(-fPIC)
即可。
在cmake 文件中加入指令 INSTALL 参数为 FILES ${GENERATE_NAME} PERMISSIONS OWNER_EXECUTE OWNER_WRITE OWNER_READ GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE DESTINATION /usr/lib/
最后那个参数是地址没什么好说的,但前面的权限控制(PERMISSIONS)我给了 755 的权限,如果不是 755 的权限,使用的时候会出现找不到链接库的问题;或者编译时直接无法报错:无法找到文件。
使用
由于最先设计的时候就考虑了将其作为一个单独的程序可供线下测试,所以也编写了main函数供调用。编译加了 -fPIC
参数也可以作为动态链接库使用。这里只介绍作为动态链接库的使用。
首先精简了 runner.h 只留下极少的一部分,配置结构体,运行结果结构体和 run函数声明。其他的错误提示之类的则会根据实际进行修改。
//sandbox.h
//
// Created by Boxjan on Dec 15, 2018 11:59.
//
#ifndef SANDBOX_RUNNER_H
#define SANDBOX_RUNNER_H
struct RuntimeConfig {
int max_cpu_time;
int max_stack;
int max_memory;
int max_output_size;
int max_open_file_number;
char *exec_path;
char *exec_env;
char *exec_args;
char *input_path;
char *output_path;
char *error_path;
char use_rlimit_to_limit_memory;
char * log_path;
char is_debug;
char *scmp_name;
int uid;
int gid;
};
struct RuntimeResult {
int cpu_time;
int clock_time;
int memory_use;
int exit_code;
int signal;
int result;
int status;
};
#ifdef __cplusplus
extern "C" {
#endif
int run(const RuntimeConfig *config, RuntimeResult *result);
#ifdef __cplusplus
}
#endif
#endif //SANDBOX_RUNNER_H
我是在go语言中使用,示例代码如下
package main
/*
#cgo CFLAGS: -I.
#cgo LDFLAGS: -lsandbox
#include "sandbox.h"
*/
import "C"
import (
"unsafe"
)
func main() {
config := RuntimeConfig{
max_cpu_time: -1,
max_stack: -1,
max_memory: -1,
max_output_size: -1,
max_open_file_number: -1,
exec_path: C.CString("echo"),
exec_env: C.CString(""),
exec_args: C.CString("hello"),
input_path: C.CString("/dev/stdin"),
output_path: C.CString("/dev/stdout"),
error_path: C.CString("/dev/stderr"),
use_rlimit_to_limit_memory: 0,
log_path: C.CString("/dev/stderr"),
is_debug: C.char(1),
scmp_name: C.CString(""),
uid: -1,
gid: -1,
}
result := RuntimeResult{}
C.run( (*C.struct_RuntimeConfig)(unsafe.Pointer(&config)), (*C.struct_RuntimeResult)(unsafe.Pointer(&result)))
用于run 接受的是两个结构体,用了 unsafe 这个包以使用指针。
Go 的中 struct 不一定能做到与 C 语言所定义的一样,所以我定义了一个两个与之相对应的 type struct,之后类型转换后传入指针给run。
定义如下
// struct_define
import "C"
type RuntimeConfig struct {
max_cpu_time, max_stack, max_memory, max_output_size, max_open_file_number C.int
exec_path, exec_env, exec_args *C.char
input_path, output_path, error_path *C.char
use_rlimit_to_limit_memory C.char
log_path *C.char
is_debug C.char
scmp_name *C.char
uid, gid int32
}
type RuntimeResult struct {
cpu_time, clock_time, memory_use, exit_code, signal, result, status C.int
}
实际使用中,结构体严格一一对应后,sandbox.h中的 struct 的详细定义是可以去掉的,但还是保留了这一部分。以防止发生一些意外。
其他语言请去查找对应的资料。
其他部分同时慢慢在推进,会及时更新。以上~~