您的位置:首页 > 运维架构 > Linux

基于Linux系统调用使用php实现一个在线编译运行C语言程序的系统

2011-04-11 21:42 1181 查看
前段时间给学校做一个在线练习C语言程序和C语言考试的系统,服务器架构是LAMP的。因为其他的都没什么好多的,要实现在线编译只要exec()函数直接调用gcc进行编译就行了,
$compile_str = "gcc ".$filename." -o ".$prog_name." 2>"."compile_result.txt;iconv -f UTF-8 -t GB2312 "."compile_result.txt -o compile_res.txt";



这里解释一下命令:$filename是你的C语言源程序的地址,-o指定生成可执行文件名,2>compile_result.txt是将编译结果输出到compile_result.txt文件里面去(gcc的编译结果是输出到标准错误2的而不是标准输出1)分号是本语句结束,下一个语句是编码转换的命令,因为我要返回的是gb2312编码的字符,所以就将之前的编译结果文件转换成GB2312编码放到compile_res.txt里面去了。这里实现其实很多余,因为你完全可以就enconv -L zh_CN -x UTF-8 filename直接搞定,但是蛋疼的是服务器居然没有这个工具。没办法之下才用的iconv。如果客户要的就是UTF-8编码的话,那直接 2>&1就OK了,都不用临时文件咯~

接下来是要在客户端上运行我们的C程序,并且将程序的输出输出到浏览器上。(并且能够读入用户端在浏览器输入的输入)这才是我们的重头戏!

首先,我们的实现还是基于临时文件和系统调用的基础之上实现的。不错如果要把用户的输入给弄到程序里面去的话,仅仅这样是不行的,下面我来说下我的实现方法:

$input = $_POST['input'];	//取得用户输入
		$fp=fopen($path.'input.txt','w+');//打开文件
  		if(!$fp)			//错误处理
  		{
	  		echo "打开文件失败";
			echo $path.'input.txt';
	 	}
      		$temp=fwrite($fp, $input);	//用户输入写入文件
	  	fclose($fp);
		if(chdir($path) == false){	//进入目录执行命令
			echo "进入目录失败:";
		}
		if(!chmod("run_me", 0777)){	//run_me程序我们接下来讲
			echo "改变文件权限失败";
		}
		$cmd_str = "./run_me ".$prog_name." input.txt";//命令语句
		echo "运行结果:";
		exec($cmd_str, $arr);	//执行命令将输出结果放到数组arr里面
		for($i = 0; $i < count($arr); $i++)
		{
			echo $arr[$i]."/n";
		}



主要的代码就是这样,这里的run_me程序是一个用C语言写的,将用户程序作为一个子进程运行,并且将用户程序的标准输入关闭,通过管道把input.txt的内容输入到用户程序中。(这里需要写过Linux下的进程最后输出运行结果的操作。

run_me的源代码如下:

#include 	<stdio.h>
#include 	<stdlib.h>
#include 	<string.h>
#include 	<unistd.h>
#include 	<sys/types.h>
#include 	<sys/stat.h>
#include 	<errno.h>
void get_file_buf(char (*buf)[81], char *filename)//取得文件的缓存到数据缓冲区
{
	FILE *fp;
	int i = 0;
	if( (fp = fopen(filename, "r")) == NULL)
	{
		perror("can't open the file");
		exit(EXIT_FAILURE);
	}
	while(fgets(buf[i], 81,fp) != NULL)
	{
		i++;
	}
}
int main(int argc, char **argv)		//主函数
{
	int file_pipe[2], i = 0;	//定义管道
	pid_t fork_result;		//fork结果
	char row_input[100][81];	//定义用户输入缓存大小
	char progname[255];		//定义程序名称
	char *filename;
	if(argc > 3){
		printf("usage :run_me $programname $filename");
		exit(EXIT_FAILURE);
	}
	
	//Initiaze buffer
	for(i = 0; i < 100; i++)
	{
		memset(progname, '/0', 81);
		memset(row_input[i],'/0',81);
	}
	//
	strncat(progname, "./", strlen("./")); //这里将用户程序加入./指定当前用户
	strncat(progname, argv[1], strlen(argv[1]));//链接参数传递的程序名称
	if(argc == 2)
	{
		system(progname);
		return 0;
	}
	filename = argv[2];		//输入文件名
	get_file_buf(row_input, filename);
	i = 0;
	if(pipe(file_pipe) == 0)		//管道初始化
	{
		fork_result = fork();		//分叉程序生成子进程
		if(fork_result == (pid_t) -1){	//错误处理
			perror("Fork failure");
			exit(EXIT_FAILURE);
		}
		if(fork_result == (pid_t) 0 )	//子进程操作
		{
			close(0);
			dup(file_pipe[0]);
			close(file_pipe[0]);
			close(file_pipe[1]);
			execlp(progname, progname, NULL, NULL);
			exit(EXIT_FAILURE);
		}
		else				//父进程操作
		{
			close(file_pipe[0]);
			while( *(row_input[i]) != '/0')//向子进程写入缓冲流
			{
				write(file_pipe[1], row_input[i], strlen(row_input[i]));
				i++;
			}
			close(file_pipe[1]);
			usleep(10000);		//只让程序执行10毫秒
			execlp("killall", "killall", argv[1], NULL);//结束子进程
		}
	}
	return 0;
}





如果熟悉在Linux下操作的朋友门一定觉得我上面的操作有点多余,直接就运行./prog_name < input.txt不就完了么?呵呵,是这样的,但是你要想到,如果用户是一个死循环程序,那么,你的服务器估计活不了太长的时间,如果只在系统层面limit对系统做限制,又不能保证大量用户同时使用这个系统。所以我才自己手动实现了一个重定向操作,并且加入了只让用户程序执行10毫秒的元素,这样就解决问题了~呵呵,如果有更好的方法希望大家推荐一下。也欢迎大家指出我的错误和问题。有什么不明白的地方也可以说出来大家讨论讨论~~
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐