zoukankan      html  css  js  c++  java
  • 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.
  • 相关阅读:
    一年来把自己从学.Net到用.Net,收集的资料共享出来B/s中的存储过程(二)
    收集的.Net文章(十五)ASP.NET 2.0 Caching For performance
    收集的.Net文章(十六)SQL Server日期计算
    P.V操作原语和信号量
    2004年2008年系分论文题目整理,考SA的可以看一下
    2010年个人总结
    MASM,NASM和AT&T汇编格式备注
    Unity Application Block 学习笔记之一使用配置文件
    Javascript 学习笔记之String类测试
    javascript学习笔记之Object类型测试
  • 原文地址:https://www.cnblogs.com/robbychan/p/3786698.html
Copyright © 2011-2022 走看看