my_judged.cc 的初稿

#include <time.h>
#include <stdio.h>
#include <string.h>
#include <ctype.h>  
#include <stdlib.h>
#include <unistd.h>   /* fork(), read函数、write函数和getpid函数*/
#include <syslog.h>   /*int syslog(int priority, string message); 本函数将 message 字符串写到系统纪录中 */
#include <errno.h>    // 出错码
#include <fcntl.h>    /* fcntl.h定义了很多宏和open */
#include <stdarg.h>   /* 让函数能够接收可变参数 */
#include <mysql/mysql.h>

#include <sys/wait.h>  // 进程控制
#include <sys/stat.h>  // 文件状态  2-> waitpid().
#include <signal.h>    // 信号

#define BUFFER_SIZE 1024
#define LOCKFILE "/var/run/judged.pid"  // 文件路径宏定义  /var/run/judged.pid
#define LOCKMODE (S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH) // 所有者可以 r 和 w, g 和 o 可以 r 。
#define STD_MB 1048576

#define OJ_WT0 0 // Pending // 正等待...
#define OJ_WT1 1 // Pending_Rejudging
#define OJ_CI 2  // Compiling
#define OJ_RI 3  // Running_Judging
#define OJ_AC 4  // Accepted
#define OJ_PE 5  // Presentation Error
#define OJ_WA 6  // Wrong Answer
#define OJ_TL 7  // Time Limit Exceeded
#define OJ_ML 8  // Memory Limit Exceeded
#define OJ_OL 9  // Output Limit Exceeded
#define OJ_RE 10 // Runtime Error
#define OJ_CE 11 // Compilation Error
#define OJ_CO 12 // Compile_OK

static char host_name[BUFFER_SIZE];
static char user_name[BUFFER_SIZE];
static char password [BUFFER_SIZE];
static char db_name  [BUFFER_SIZE];
static char oj_home  [BUFFER_SIZE];
static char oj_lang_set[BUFFER_SIZE];  // 
static int port_number;
static int max_running; // 可以运行的 judge_client 的数量上限。
static int sleep_time;  // 数据库轮询间隔
static int sleep_tmp;
static int oj_tot; // 老式并发处理中总的 judged 数量
static int oj_mod; // 老式并发处理中,本 judged 负责处理 solution_id 按照 TOTAL 取模后余数为几的任务。

static bool STOP = false;

static MYSQL *conn;   /************* 数据库 类型 *****************/
static MYSQL_RES *res;
static MYSQL_ROW row;

static char query[BUFFER_SIZE];

void call_for_exit(int s) {
	STOP = true;
	printf("Stopping judged...\n");
}
void write_log(const char *fmt, ...) {
	va_list ap;
	char buffer[4096];
//	time_t          t = time(NULL);
//	int             l;
	sprintf(buffer,"%s/log/client.log",oj_home);
	FILE *fp = fopen(buffer, "a+");
	if (fp == NULL){
		 fprintf(stderr, "openfile error!\n");
		 system("pwd");
	} va_start(ap, fmt);
	vsprintf(buffer, fmt, ap);
	fprintf(fp,"%s\n",buffer);
	va_end(ap);
	fclose(fp);
}

int after_equal(char * c){
	int i=0;
	for(; c[i] != '\0' && c[i] != '='; i++);
	return ++i;
}
void trim(char * c){
    char buf[BUFFER_SIZE];
    char * start,*end;
    strcpy(buf,c);
    start=buf;
    while(isspace(*start)) start++;
    end=start;
    while(!isspace(*end)) end++;
    *end='\0';
    strcpy(c,start);
}
bool read_buf(char * buf,const char * key,char * value){
   if (strncmp(buf, key, strlen(key)) == 0) {
		strcpy(value, buf + after_equal(buf));
		trim(value);	
		return 1;
   }
   return 0;
}
void read_int(char * buf,const char * key,int * value){
	char buf2[BUFFER_SIZE];
	if (read_buf(buf, key, buf2))
		sscanf(buf2, "%d", value);
}
// read the configue file
void init_mysql_conf() {
	FILE *fp = NULL; // 文件指针类型 fp
	char buf[BUFFER_SIZE];
	host_name[0] = 0;
	user_name[0] = 0;
	password[0] = 0;
	db_name[0] = 0;
	port_number = 3306;
	max_running = 3; // 最多允许 3 个 judge_client 同时存在。
	sleep_time = 3; 
	oj_tot = 1;  // 老式并发处理中总的 judged 数量  
	oj_mod = 0;  // 老式并发处理中,本 judged 负责处理 solution_id 按照 TOTAL 取模后余数为几的任务。
	strcpy(oj_lang_set, "0, 1");
	fp = fopen("./etc/judge.conf", "r");
	if(fp != NULL){
		while(fgets(buf, BUFFER_SIZE - 1, fp)) {
			read_buf(buf, "OJ_HOST_NAME", host_name);	
			read_buf(buf, "OJ_USER_NAME", user_name);
			read_buf(buf, "OJ_PASSWORD", password);
			read_buf(buf, "OJ_DB_NAME", db_name);
			read_int(buf , "OJ_PORT_NUMBER", &port_number);
			read_int(buf, "OJ_RUNNING", &max_running);
			read_int(buf, "OJ_SLEEP_TIME", &sleep_time);
			read_int(buf , "OJ_TOTAL", &oj_tot);
			read_int(buf,"OJ_MOD",&oj_mod);
			read_buf(buf,"OJ_LANG_SET", oj_lang_set);
		}
		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);
		sleep_tmp = sleep_time;
	//	fclose(fp);
    }
}
void run_client(int runid, int clientid){
    char buf[BUFFER_SIZE],runidstr[BUFFER_SIZE];
    struct rlimit LIM;
	LIM.rlim_max=800;
	LIM.rlim_cur=800;
	setrlimit(RLIMIT_CPU,&LIM);

	LIM.rlim_max=80*STD_MB;
	LIM.rlim_cur=80*STD_MB;
	setrlimit(RLIMIT_FSIZE,&LIM);

	LIM.rlim_max=STD_MB<<11;
	LIM.rlim_cur=STD_MB<<11;
	setrlimit(RLIMIT_AS,&LIM);
		
    LIM.rlim_cur=LIM.rlim_max=200;
    setrlimit(RLIMIT_NPROC, &LIM);
	//buf[0]=clientid+'0'; buf[1]=0;
	sprintf(runidstr, "%d", runid);
	sprintf(buf, "%d", clientid);
	execl("/usr/bin/judge_client", "/usr/bin/judge_client", runidstr, buf, oj_home, (char *)NULL);
}
int executesql(const char * sql) {
	if (mysql_real_query(conn, sql, strlen(sql))) { // 成功,函数返回零
		sleep(20);
		conn = NULL;
		return 1;
	} else
		return 0;
}
int init_mysql(){
    if(conn == NULL){
		conn = mysql_init(NULL);		// init the database connection
		/* connect the database */
		const char timeout = 30;
		mysql_options(conn, MYSQL_OPT_CONNECT_TIMEOUT, &timeout);

		if(!mysql_real_connect(conn, host_name, user_name, password, db_name, port_number, 0, 0)) {
			sleep(20);
			return 1; // 连接失败,mysql_real_connect 返回为 0.
		}
	}
	if (executesql("set names utf8"))
        return 1; // 执行失败
	return 0;
}
int  _get_jobs_mysql(int * jobs) {
	if (mysql_real_query(conn, query, strlen(query))) { // 成功返回 0, 读取未判的那些 solution_id 们。
		sleep(20); // 不存在 pending 中的 solution_id , 进程挂起 20 秒。
        return 0;
    }
    res = mysql_store_result(conn);
    int i = 0;
    int ret = 0;
    while((row = mysql_fetch_row(res)) != NULL) {
        jobs[i++] = atoi(row[0]); 
    }
    ret = i;
    while(i <= max_running*2) jobs[i++] = 0;
    return ret;
}
int get_jobs(int * jobs) {
	return _get_jobs_mysql(jobs);
}
bool _check_out_mysql(int solution_id, int result) { // workcnt < max_running && check_out(runid, OJ_CI)
	char sql[BUFFER_SIZE];
	sprintf(sql, "UPDATE solution SET result=%d,time=0,memory=0,judgetime=NOW() WHERE solution_id=%d and result<2 LIMIT 1"
			,result, solution_id);
	if (mysql_real_query(conn, sql, strlen(sql))) {
		syslog(LOG_ERR | LOG_DAEMON, "%s",mysql_error(conn));
		return false;
	} else {
		if(mysql_affected_rows(conn) > 0ul)
			return true;
		else
			return false;
	}
}
bool check_out(int solution_id, int result) {
	return _check_out_mysql(solution_id, result);
}
int work() {
	static  int retcnt = 0;
    int i = 0;
    static pid_t ID[100];  // 声明 100 大小的pid数组。
    static int workcnt = 0;
    int runid = 0;
    int jobs[max_running*2+1];  // max_running 是启动 judge_client 的上限
    pid_t tmp_pid = 0;
        /* get the database info */
    if(!get_jobs(jobs)) retcnt = 0; //  检索出的 solution_id 们,全都存在 jobs[] 中。retcnt 为 pending的solution_id的数量。
        /* exec the submit */
	for (int j = 0; jobs[j] > 0; j++) {
		runid = jobs[j];
        if (workcnt >= max_running) {    // if no more client can running
			tmp_pid = waitpid(-1, NULL, 0);     // wait 4 one child exit, pid=-1时,等待任何一个子进程退出,没有任何限制,此时waitpid和wait的作用一模一样
            workcnt--; retcnt++; // waitpid 返回收集到的子进程的进程ID, retcnt++, 这是hp?
            for (i = 0; i < max_running; i++)     // get the client id
				if (ID[i] == tmp_pid) break; // got the client id
            ID[i] = 0;
        } else {                                                  // have free client
			for (i = 0; i < max_running; i++)     // find the client id
				if (ID[i] == 0) break;    // got the client id
        }
        if(workcnt < max_running && check_out(runid, OJ_CI)) { // 要判题了,将result置为 OJ_CI
			workcnt++;                                        // client 增加一个。
            ID[i] = fork();                                   // start to fork
            if (ID[i] == 0) {
                run_client(runid, i);    // if the process is the son, run it,儿子你去判题吧,父亲继续运行。
                exit(0);
            }
        } else ID[i] = 0;
    }
    //下面回收运行完的进程号
    while ( (tmp_pid = waitpid(-1, NULL, WNOHANG) ) > 0) { // 如果使用了WNOHANG(wait no hung)参数调用waitpid,即使没有子进程退出,它也会立即返回
		workcnt--; retcnt++;   // 可能是立即返回,子进程没有主动退出的,也会立即终止一个子进程。
		for (i = 0; i < max_running; i++)     // get the client id
			if (ID[i] == tmp_pid) break;      // got the client id
        ID[i] = 0;
        printf("tmp_pid = %d\n", tmp_pid);
    }
	mysql_free_result(res);                         // free the memory
    executesql("commit");
    //free(ID);
    //free(jobs);
    return retcnt;
}
int lockfile(int fd) {
	struct flock fl;
	fl.l_type = F_WRLCK; // 独占性写锁定
	fl.l_start = 0;
	fl.l_whence = SEEK_SET; // SEEK_SET 文件头
	fl.l_len = 0;
	return (fcntl(fd, F_SETLK, &fl)); // F_SETLK 设置文件锁定的状态, 如果无法锁定返回 -1.
}
int already_running(){
	int fd;
	char buf[16];
	fd = open(LOCKFILE, O_RDWR|O_CREAT, LOCKMODE); // "/var/run/judged.pid" (S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH)Permits the file's owner to read it.
	if (fd < 0){
		syslog(LOG_ERR|LOG_DAEMON, "can't open %s: %s", LOCKFILE, strerror(errno));
		exit(1);
	}
	if(lockfile(fd) < 0){      
		if (errno == EACCES || errno == EAGAIN){
			close(fd);
			return 1;
		}
		syslog(LOG_ERR|LOG_DAEMON, "can't lock %s: %s", LOCKFILE, strerror(errno));
		exit(1);
	}
	ftruncate(fd, 0); // 以写模式打开的文件,就理解为清空文件吧!先!
	sprintf(buf, "%d", getpid()); // getpid() 返回当前进程标识,将当前进程标识写入到 /var/run/judged.pid
	write(fd, buf, strlen(buf)+1);
	return (0);
}
int daemon_init(void) {
	pid_t pid;
	if((pid = fork()) < 0) return(-1);
	else if(pid != 0) exit(0);  /* parent exit */
	/* child continues */
	setsid(); /* become session leader */
	chdir(oj_home);  /* change working directory */
	umask(0);        /* clear file mode creation mask */
	close(0); /* close stdin */  // 切后台关闭这三个干嘛呀!
	close(1); /* close stdout */
	close(2); /* close stderr */
	return(0); 
}
int main(int argc, char** argv){
	strcpy(oj_home, "/home/judge");
	chdir(oj_home);

	daemon_init(); // 切后台
	if (strcmp(oj_home, "/home/judge") == 0 && already_running() ){
		syslog(LOG_ERR|LOG_DAEMON, "This daemon program is already running!\n"); // err, daemon
		return 1;  // syslogd守护进程用于解决守护进程的日志记录问题,openlog、syslog和closelog,日志信息会写入syslog.conf文件指定的位置
	}
	init_mysql_conf();	// set the database info
	signal(SIGQUIT, call_for_exit); // 输入Quit Key的时候(CTRL+\)发送给所有Foreground Group的进程
	signal(SIGKILL, call_for_exit); // 无法处理和忽略。中止某个进程
	signal(SIGTERM, call_for_exit); // 请求中止进程,kill命令缺省发送
	int j = 1;
	while (!STOP){			// start to run
	    while( j && (!init_mysql()) ){        
			    j = work();		
		}
		sleep(sleep_time); // judged 通过轮询数据库发现新任务,轮询间隔的休息时间,单位秒, 对应流程图。
		j = 1;
	}
	return 0;
}
all: my_judged2.cc
	g++ -Wall -c -I/usr/local/mysql/include/mysql -I/usr/include/mysql  my_judged2.cc
	g++ -Wall -o my_judged2 my_judged2.o -L/usr/local/mysql/lib/mysql -L/usr/lib/mysql  -lmysqlclient
// Special thanks to teacher Zhang for helping me.
posted @ 2013-03-29 21:04  小尼人00  阅读(224)  评论(0编辑  收藏  举报