HUSTOJ problems

来源:互联网 发布:c语言顺序表的初始化 编辑:程序博客网 时间:2024/05/16 07:43

一:资源限制,程序资源的意义,与具体代码的关系,内存空间的分配,CPU时间的计算

二:守护进程的常用技术

*******************************************************************************************************************

executesql("set names utf8");

设置数据库字符编码格式,防止出现乱码

*******************************************************************************************************************

sprintf(query,"SELECT solution_id FROM solution WHERE language in (%s) and result<2 and MOD(solution_id,%d)=%d ORDER BY result ASC,solution_id ASC limit %d",oj_lang_set,oj_tot,oj_mod,max_running*2)

这条查询语句信息量很大,最重要的是对

1)solution所用语言的限定 (language in (%s))

2)solution当前状态的限定(result<2)

3)查询结果排序的限定 (ORDER BY result ASC,solution_id ASC)

4)查询结果数量的限定(limit %d)

*******************************************************************************************************************

waitpid(-1,NULL,WNOHANG)

waitpid相比wait,多了三个功能

1)waitpid可以等待一个特定的进程

2)waidpid提供了wait的非阻塞版本

3)waitpid支持作业控制

需要注意的是,一般把waitpid放到while循环中。如果只是单独的一个waitpid,在指定的子进程没有结束时,waitpid会立即返回,程序接着执行下一条指令。即使过了一段时间,子进程结束了,但已经与前边的waitpid没有任何关系,如果没有再次调用wait或者waitpid来获取该终止子进程的状态,该子进程就成为僵尸进程。

*******************************************************************************************************************

C/C++编译语句命令如下

gcc Main.c -o Main -fno-asm -Wall -lm --static -std=c99 -DONLINE_JUDGEg++ Main.cc -o Main -fno-asm -Wall -lm --static -std=c++0x -DOLINE_JUDGE

需要注意的是静态编译,这样在chroot的时候,就不用复制很多的共享库到work_dir目录了,因此在judge_client.cc文件中没有copy_c_runtime和copy_cpp_runtime函数

另外chroot有命令和系统调用之分,命令版本变更根目录后会自动执行“sh -i”,而sh需要相应的命令和共享库支持,而系统调用版本可以直接chroot到一个目录,因为它无需交互,只是在运行程序的时候需要共享库支持,不过,我们可以运行静态编译的程序^_^

*******************************************************************************************************************

execl("/usr/bin/judge_client","/usr/bin/judge_client",runidstr,buf,oj_home,(char *)NULL);

execl执行一个程序,参数表从第二项开始,即argv[0]为“/usr/bin/judge_client”,argv[1]为"runidstr",argv[2]为buf,argv[3]为oj_home,最后一个(char *)NULL是参数结束标志,不计入参数,参数个数argc为4。也可以省略最后的空指针结束标志,参数个数argc不会变,还是4。为了清晰,一般还是要加上空指针!

与execl类似的还有其它五个函数

int execl(const char *path, const char *arg, ...);int execlp(const char *file, const char *arg, ...);int execle(const char *path, const char *arg,..., char * const envp[]);int execv(const char *path, char *const argv[]);int execvp(const char *file, char *const argv[]);int execvpe(const char *file, char *const argv[],char *const envp[]);

需要注意的是

1)execl,execle,execlp要求将新程序的每个命令行参数都说明为一个单独的参数,这种参数表以空指针结尾。对于另外三个参数,则应先构造一个指向各参数的指针数组,然后将该数组地址作为这三个函数的参数

2)以e结尾的两个函数可以传递一个指向环境字符串指针数组的指针,其他四个函数则使用调用进程中的environ变量对新程序复制现有的环境

*******************************************************************************************************************

if(p_id==0){  //custom input runningprintf("running a custom input...\n");get_custominput(solution_id,work_dir);

problem_id等于0的solution,都是Test的solution,可以自己给出测试数据,界面如下


提交的时候把problem id标记为0

*******************************************************************************************************************

 while(setgid(1536)!=0) sleep(1); while(setuid(1536)!=0) sleep(1); while(setresuid(1536, 1536, 1536)!=0) sleep(1);

设置solution的运行级别

*******************************************************************************************************************

ptrace(PTRACE_TRACEME,0,NULL,NULL);

While being traced, the tracee will stop each time a signal is delivered, even if the signal is being ignored. (An exception is SIGKILL, which has its usual effect.) The tracer will be notified at its next call to waitpid(2) (or one of the related "wait" system calls); that call will return a status value containing information that indicates the cause of the stop in the tracee. While the tracee is stopped, the tracer can use various ptrace requests to inspect and modify the tracee. The tracer then causes the tracee to continue, optionally ignoring the delivered signal (or even delivering a different signal instead).

ptrace(PTRACE_SYSCALL,child_pid,NULL,NULL);ptrace(PTRACE_SINGLESTEP,child_pid,NULL,NULL);

注意PTRACE_SYSCALL和PTRACE_SINGLESTEP的区别,两者粒度不同,SYSCALL是系统调用层面,而SINGLESTEP是汇编指令层面

另外经常用下面的命令取得进程当前寄存器的值

ptrace(PTRACE_GETREGS,child_pid,NULL,®);

需要注意的是,某些寄存器里存的是地址,某些寄存器里存的是值,在应用时,一定要区分清楚。

存储的是地址的寄存器,一般被拿来做PTRACE_PEEKTEXT和PTRACE_POKETEXT的参数。如下所示,在PTRACE_GETREGS后,下面的指令,取出内存地址为reg.eip的

unsigned info=ptrace(PTRACE_PEEKTEXT,child_pid,reg.eipL,NULL);

值,赋给info

*******************************************************************************************************************

for (i = 0; LANG_CC[i]; i++) {        call_counter[LANG_CV[i]] = LANG_CC[i];}

注意if,for和while执行的条件是,条件判断结果非0,即正数和负数都不会结束循环!!!

*******************************************************************************************************************

man 2 syscall

在linux中查询syscall,出现如下的syscall调用方法,其中number是调用号

int syscall(int number, ...);

man手册中关于系统调用,还有很重要的一项,就是系统调用时,各个寄存器存储状态,如下所示

Every architecture has its own way of invoking and passing arguments       to the kernel.  The details for various architectures are listed in       the two tables below.       The first table lists the instruction used to transition to kernel       mode, (which might not be the fastest or best way to transition to       the kernel, so you might have to refer to the VDSO), the register       used to indicate the system call number, and the register used to       return the system call result.       arch/ABI   instruction          syscall #   retval Notes       -----------------------------------------------------------------------------------       arm/OABI   swi NR               -           a1     NR is syscall #       arm/EABI   swi 0x0              r7          r0       blackfin   excpt 0x0            P0          R0       i386       int $0x80            eax         eax       ia64       break 0x100000       r15         r10/r8       parisc     ble 0x100(%sr2, %r0) r20         r28       s390       svc 0                r1          r2     NR may be passed directly with       s390x      svc 0                r1          r2     "svc NR" if NR is less than 256       sparc/32   t 0x10               g1          o0       sparc/64   t 0x6d               g1          o0       x86_64     syscall              rax         rax       The second table shows the registers used to pass the system call       arguments.       arch/ABI   arg1   arg2   arg3   arg4   arg5   arg6   arg7       ----------------------------------------------------------       arm/OABI   a1     a2     a3     a4     v1     v2     v3       arm/EABI   r0     r1     r2     r3     r4     r5     r6       blackfin   R0     R1     R2     R3     R4     R5     -       i386       ebx    ecx    edx    esi    edi    ebp    -       ia64       r11    r9     r10    r14    r15    r13    -       parisc     r26    r25    r24    r23    r22    r21    -       s390       r2     r3     r4     r5     r6     r7     -       s390x      r2     r3     r4     r5     r6     r7     -       sparc/32   o0     o1     o2     o3     o4     o5     -       sparc/64   o0     o1     o2     o3     o4     o5     -       x86_64     rdi    rsi    rdx    r10    r8     r9     -       Note that these tables don't cover the entire calling convention--       some architectures may indiscriminately clobber other registers not       listed here.

比如在i386体系的机器上,系统调用号存储在eax寄存器中,调用参数依次存在ebx、ecx、edx……

对于系统调用,一定要理解的是:

1)为什么需要系统调用(系统保护,比如禁止程序直接读写内存,硬盘等硬件,保护其他程序的内存和硬盘数据)

2)系统调用怎么实现(软中断,用户态程序执行TRAP)

我们可以通过ptrace取得这些寄存器的值

关于CPU、操作系统、系统调用,用户态,内核态以及系统保护的实现可以参考以下资料

http://minnie.tuhs.org/CompArch/Lectures/week05.html

http://stackoverflow.com/questions/16738636/linux-system-calls-kernel-mode

*******************************************************************************************************************

linu中几个特殊头文件的位置

信号编号定义:/usr/include/asm/signal.h

系统调用号定义:/usr/include/asm/unistd.h    /usr/include/asm/unistd_32.h   /usr/include/asm/unistd_64.h

gdb,ptrace调试用数据结构的定义:/usr/include/sys/user.h

Errno错误号定义:/usr/include/asm-generic/errno.h(相当于扩展部分,定义了35-133号)    /usr/include/asm-generic/errno-base.h(基础部分,定义了1-34号)

*******************************************************************************************************************

*******************************************************************************************************************

*******************************************************************************************************************

1.import problem失败

权限问题,把/home/judge/data所属组修改为apache,即web服务器所属组,还有可能是/etc/php.ini中open_basedir没有设置

2.判题编译失败,出现如下错误

/bin/ld: cannot find -lm/bin/ld: cannot find -lccollect2: error: ld returned 1 exit status

需要安装glibc-static和libstdc++-static

yum -y install glibc-static libstdc++-static

3.判题状态一直处于

”Running & Judging"

需要检查测试数据放置是否正确,我在测试1000题的时候,一直处于这个状态,检查后才发现/home/judge/data/目录下没有1000题的测试数据目录

4.problemset页面无法打开,显示“服务器错误”

看到这个消息首先要想到是php脚本出了问题,而apache负责php,那么在/var/log/httpd/error_log文件中查看http请求的错误信息,搜索'problemset.php",发现如下错误

PHP Fatal error: Call to undefined function mb_substr() in /var/www/html/JudgeOnline/problemset.php on line 117,referer: http://10.1.130.4/JudgeOnline

可以发现在调用mb_substr函数时,出现了未定义错误,在网上搜索mb_substr函数,发现是php一个extension中的函数,那么基本可以确定包含这个函数的extension组件没有装,用命令php -m|grep "mb"查询可知确实没有安装,网上查一下,找到了php-mbstring这个extension,装上后,在php.ini文件中查找mbstring的extension,去掉前边的注释(如果php.ini中没有找到mbstring相关选项,例如fedora19中就没有,但是可以在/etc/php.d/中找到mbstring.ini,这个文件中自动启用了extension=mbstring.so),然后重启apache即可(每次对php.ini改动后都要重启apache)

fedora中有很多设置都是在/etc/下有一个conf文件,然后还有一个同名的文件夹,里面存放着各种需要处理的文件,需要注意这种结构!!!

原创粉丝点击