本文主要是介绍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_OKstatic 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; // 文件指针类型 fpchar 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;} elsereturn 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;elsereturn 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 runningtmp_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 idif (ID[i] == tmp_pid) break; // got the client idID[i] = 0;} else { // have free clientfor (i = 0; i < max_running; i++) // find the client idif (ID[i] == 0) break; // got the client id}if(workcnt < max_running && check_out(runid, OJ_CI)) { // 要判题了,将result置为 OJ_CIworkcnt++; // client 增加一个。ID[i] = fork(); // start to forkif (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 idif (ID[i] == tmp_pid) break; // got the client idID[i] = 0;printf("tmp_pid = %d\n", tmp_pid);}mysql_free_result(res); // free the memoryexecutesql("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.pidwrite(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, daemonreturn 1; // syslogd守护进程用于解决守护进程的日志记录问题,openlog、syslog和closelog,日志信息会写入syslog.conf文件指定的位置}init_mysql_conf(); // set the database infosignal(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 runwhile( 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.
这篇关于my_judged.cc 的初稿的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!