基本思路如下:
先创建两个匿名管道(一个输入,一个输出),然后用CreateProcess新建一个进程,然后用WriteFile函数通过输入管道写入输入数据,下面就是一个循环,判断程序运行时间及内存,如果一定时间内没有执行完(记为TLE,time limit exceed)或执行时占用的内存超过内存上限(在函数中指定)(记为MLE,memory limit exceed)或不停地输出超过一定限制(记为OLE,output limit exceed),就强行终止程序.
先把存在的问题说下:
一,假如程序是个死循环而且死循环中有输出,程序就无法检测出超时;但是如果仅仅是个死循环但没有输出,程序就能检测超时.
二,测出来的程序占用的内存不标准.一个原本最多应该不超过300K的程序,测出来竟然700K以上(这个问题我猜是控制台程序默认启动了一个Console Shell,而Shell占用了内存).
三,通过管道的数据最后写入文件后与直接运行该程序而不通过管道有不同,主要是回车那里,普通的方法会把文件中的'\n'解释成'\13'和'\10'两个字节,但是通过管道后,变成了'\13','\13','\10'三个字节,多出了一个字节.
四,MLE问题没解决,不知道怎样解决.下面将代码帖出.很长,但不难.请仔细看完.
#include <windows.h>
#include <stdio.h>
#include <stdlib.h>
#include <process.h>
#include <mmsystem.h>
#include "psapi.h"#pragma comment(lib,"psapi.lib")
#pragma comment(lib,"winmm.lib")//struct parameter passed to getProcessInfo function below
struct Param
{
int pid;
int time;
int memory;
};
//get problem id from source filename
//eg. for 1000.c, we get '1000'
char* getPid(char* sourceFile,char* pid)
{
int i=strlen(sourceFile);
for(int j=0;j<i && sourceFile[j]!='.';j++)
pid[j]=sourceFile[j];
pid[j]=0;
return pid;
}//get input filename from source filename
char* getInputFile(char* sourceFile,char* inFile)
{
char pid[10];
getPid(sourceFile,pid);
sprintf(inFile,"%s\\\\%s.in",pid,pid);
inFile[strlen(inFile)]=0;
return inFile;
}//get output filename from source filename
char* getOutputFile(char* sourceFile,char* outFile)
{
char pid[10];
getPid(sourceFile,pid);
sprintf(outFile,"%s.out",pid);
outFile[strlen(outFile)]=0;
return outFile;
}//get time and memory statistics of a process
unsigned long WINAPI getProcessInfo(LPVOID param)
{
HANDLE hProcess;
PROCESS_MEMORY_COUNTERS pms;
hProcess=OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ,FALSE,((Param*)param)->pid);

if(GetProcessMemoryInfo(hProcess,&pms,sizeof(pms)))
{
((Param*)param)->memory=pms.PeakWorkingSetSize/1024;
}

FILETIME ftCreation,ftExit,ftKernel,ftUser;
SYSTEMTIME stUser; if(GetProcessTimes(hProcess,&ftCreation,&ftExit,&ftKernel,&ftUser))
{
FileTimeToSystemTime(&ftUser,&stUser);
((Param*)param)->time=stUser.wMilliseconds+stUser.wSecond*1000;
}

CloseHandle(hProcess);
return 0;
}// 0: accepted
// 1: presentation error
// 2: wrong answer
// 3: time limit exceed
// 4: memory limit exceed
// 5: output litmit exceed
// 6: compile error
//      7: internal error
int runApp(char* appName,Param* p,int time,int memory)
{
char inFile[64],outFile[64];
SECURITY_ATTRIBUTES sa,sa2;
HANDLE hInputRead,hInputWrite,hOutputRead,hOutputWrite;

sa.nLength = sizeof(SECURITY_ATTRIBUTES);
sa.lpSecurityDescriptor = NULL;
sa.bInheritHandle = TRUE;

    if (!CreatePipe(&hInputRead,&hInputWrite,&sa,0))
    {
printf("Error creating input pipe\n");
return 7;
    }

sa2.nLength=sizeof(SECURITY_ATTRIBUTES);
sa2.lpSecurityDescriptor=NULL;
sa2.bInheritHandle=TRUE;

if(!CreatePipe(&hOutputRead,&hOutputWrite,&sa2,0))
{
printf("Error creating output pipe\n");
return 7;
}
////////////////////////////////////////////
PROCESS_INFORMATION pi;
STARTUPINFO si; GetStartupInfo(&si);
si.cb=sizeof(STARTUPINFO); //redirect stdin,stdout and stderr
si.hStdError = hOutputWrite;
si.hStdOutput = hOutputWrite;
si.hStdInput = hInputRead; si.wShowWindow=SW_HIDE;
si.dwFlags =  STARTF_USESTDHANDLES;

p->time=0;p->memory=0; char buffer[1024]={0},tmp[80]={0};
if(CreateProcess(appName,NULL,
NULL,NULL,TRUE,
NORMAL_PRIORITY_CLASS | CREATE_NO_WINDOW,
NULL,NULL,&si,&pi))
{
p->pid=pi.dwProcessId;

//get input filename
getInputFile(appName,inFile);                //get input data
FILE *fp;
if((fp=fopen(inFile,"r"))==NULL)
{
printf("Error opening input file");
return 7;
}
while(!feof(fp))
{
fgets(tmp,80,fp);
strcat(buffer,tmp);
}
fclose(fp);

int len=strlen(buffer);
buffer[len]=0;
//printf("Buffer content:%s",buffer);

DWORD bytesRead;
  
CloseHandle(hInputRead);
CloseHandle(hOutputWrite);
 
if(WriteFile(hInputWrite,buffer,len+1,&bytesRead,NULL)==NULL)
{
printf("Nothing written\n");
return 7;
} while(1)
{
HANDLE hProcess;
unsigned long i;
if((hProcess=OpenProcess(PROCESS_QUERY_INFORMATION,TRUE,pi.dwProcessId)))
{
if(WaitForSingleObject(hProcess,1000)!=WAIT_OBJECT_0)
{
if(GetExitCodeProcess(hProcess,&i) && i==0)
break;
getProcessInfo(p);
//test if time limit exceed
if(p->time>time+15)
{
TerminateProcess(OpenProcess(PROCESS_ALL_ACCESS,TRUE,pi.dwProcessId),1);
return 3;
}
//test if memory limit exceed
else if(p->memory>memory)
{
TerminateProcess(OpenProcess(PROCESS_ALL_ACCESS,TRUE,pi.dwProcessId),1);
return 4;
}
printf("Time: %dms\tMemory: %dKB\n",p->time,p->memory);
_sleep(10);
}
CloseHandle(hProcess);
}
else
break;
}

memset(buffer,0,1024*sizeof(char)); //get output filename
getOutputFile(appName,outFile); if((fp=fopen(outFile,"w"))==NULL)
{
printf("Error opening output file");
return 7;
}

while (true) 
{
memset(tmp,0,80*sizeof(char));
if(ReadFile(hOutputRead,tmp,80,&bytesRead,NULL)==NULL)
break;
strcat(buffer,tmp);
}
buffer[strlen(buffer)]=0; //test if output limit exceed
if(strlen(buffer)>10*1024)
{
fclose(fp);
return 5;
} fprintf(fp,"%s",buffer);
fclose(fp); CloseHandle(hInputWrite);
CloseHandle(hOutputRead);
} char standardOutput[64];
//      get standard output filename of the correct output
getStandardOutputFile(appName,standardOutput);// printf("Comparing files...\n");
//      diff function compares two files,omitted here
return diff(outFile,standardOutput);
}

解决方案 »

  1.   

    对内存那一块的猜测,我再说详细些,我运行程序的主进程也是一个控制台程序,如果子进程继承父进程的属性的话,那也是一个控制台程序,它可能会首先启动一个控制台Shell,这会占一定内存.或者,假如不继承父进程的属性,但程序本身是一个直接控制台程序,这也会启动一个控制台Shell,导致占用内存.我是这样猜的,假如是这样,有没有什么解决方法呢?
      

  2.   

    可是CPU分配给线程的时间片是不固定的,有可能出现的情况是,进程早已经结束,这时CPU分配的时间片才刚刚轮到那个线程来运行.这样的话它什么都无法检测到.