1、连接arm开发板
将arm开发板电源线接好,保持开发板开关处于闭合状态。再分别将串口线、并口线和网线与pc机连接好。
2、建立超级终端
运行windows XP系统下“开始”、“所有程序”、“附件”、“通讯”、“超级终端”。
新建一个通信终端,取名为arm。在属性对话框中,将波特率设为115200,数据位设为8,无奇偶校验,停止位为1,无数据流控制。另存为在桌面。
3、启动实验平台
打开超级终端,打开arm机电源开关。等待一分钟,arm机的信息会显示在超级终端的窗口中。继续等待,直至出现界面。输入ifconfig命令,记录下arm机的ip为:192.168.0.121。
4、修改xp系统与redhat虚拟机的ip,使得它们均与arm机的ip在同一网段
本次实验中将pc机的ip设为192.168.0.55,redhat虚拟机ip为192.168.0.234。
修改完IP,重启一下虚拟机,IP才能启作用。重启后在命令行中使用ifconfig确认修改正确。
5、安装arm编译器。
在pc机中“开始”、“运行”,输入虚拟机的ip。\192.168.0.234,输入用户名bc,密码123456然后确定就可以访问虚拟机的文件了。然后把所需文件解压缩拷贝到共享文件夹bc中。
进入虚拟机,在命令行中输入./install.sh,安装脚本程序将会自动建立目录,配置编译环境。
6、配置环境变量
在虚拟机中使用 vi 修改/root/.bash_profile文件中的PATH变量为 PATH=\(PATH:\)HOME/bin:/opt/host/armv4l/bin/(因为该文件隐藏系统, 所以使用 ls 命令不可见),存盘后执行: source/root/.bash_profile,则以 后 armv4l-nknown-linux-gcc 会自动搜索到,可以在终端上输入 。注意空格!
保存退出后,重启虚拟机
7、建立hello.c文件并编译
在虚拟机中进入文件夹/root/bc,在此目录下编写hello.c文件。利用命令armv4l-unknown-linux-gcc对hello.c进行编译,生成hello可执行文件。
8、下载调试
在超级终端中将共享文件夹挂载好,建立开发板与虚拟机之间的通讯。输入命令“mount -t nfs -o nolock 192.168.0.234:/home/bc /host”(注意bc后面要有空格!)在超级终端中运行编译通过的hello可执行文件。
pthread.c源代码
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include "pthread.h"
#define BUFFER_SIZE 16
/* 设置一个结构体 */
struct prodcons {
int buffer[BUFFER_SIZE]; /* 缓冲区数组 */
pthread_mutex_t lock; /* 互斥锁 */
int readpos, writepos;/* 读写的位置 */
pthread_cond_t notempty; /* 缓冲区非空信号 */
pthread_cond_t notfull; /* 缓冲区非满信号 */
};
/* Initialize a buffer */
void init(struct prodcons * b)
{
pthread_mutex_init(&b->lock, NULL);
pthread_cond_init(&b->notempty, NULL);
pthread_cond_init(&b->notfull, NULL);
b->readpos = 0;
b->writepos = 0;
}
/*--------------------------------------------------------*/
/* Store an integer in the buffer */
void put(struct prodcons * b, int data)
{
pthread_mutex_lock(&b->lock);
/*等待缓冲区非满的时候,才可以向其中写入数据 */
while ((b->writepos + 1) % BUFFER_SIZE == b->readpos) {
printf("wait for not full\n");
pthread_cond_wait(&b->notfull, &b->lock);
}
/* 写过数据之后,将指针向前移动一位 */
b->buffer[b->writepos] = data;
b->writepos++;
if (b->writepos >= BUFFER_SIZE) b->writepos = 0;
/* 设置非空信号 */
pthread_cond_signal(&b->notempty);
pthread_mutex_unlock(&b->lock);
}
/* 其实读和写的代码基本上差不多,只不过最后要把非空设置为非满 */
int get(struct prodcons * b)
{
int data;
pthread_mutex_lock(&b->lock);
/* Wait until buffer is not empty */
while (b->writepos == b->readpos) {
printf("wait for not empty\n");
pthread_cond_wait(&b->notempty, &b->lock);
}
/* Read the data and advance read pointer */
data = b->buffer[b->readpos];
b->readpos++;
if (b->readpos >= BUFFER_SIZE) b->readpos = 0;
/* Signal that the buffer is now not full */
pthread_cond_signal(&b->notfull);//设置状态信号量,这个是为了“我们”(操作系统)方便地“看出来”这个单元到底可不可用
pthread_mutex_unlock(&b->lock);//释放互斥锁。这一步是很重要的,因为某一个单元在同一时刻只能被一个消费者/生产者使用(不可能同时写数据或者读数据,甚至也不可能同时有两个“人”读或者写数据)
return data;
}
/*--------------------------------------------------------*/
#define OVER (-1)
struct prodcons buffer;
/*下面的函数是生产者和消费者的主要的调用函数(调用上面的函数)*/
void * producer(void * data)
{
int n;
for (n = 0; n < 1000; n++) {
printf(" put-->%d\n", n);
put(&buffer, n);
}
put(&buffer, OVER);
printf("producer stopped!\n");
return NULL;
}
/*--------------------------------------------------------*/
void * consumer(void * data)
{
int d;
while (1) {
d = get(&buffer);
if (d == OVER ) break;
printf(" %d-->get\n", d);
}
printf("consumer stopped!\n");
return NULL;
}
/*主函数:可以注意到先有生产者再有消费者*/
int main(void)
{
pthread_t th_a, th_b;
void * retval;
init(&buffer);
pthread_create(&th_a, NULL, producer, 0);
pthread_create(&th_b, NULL, consumer, 0);
/* Wait until producer and consumer finish. */
pthread_join(th_a, &retval);
pthread_join(th_b, &retval);
return 0;
}
(一)基本开发环境
1.连接实验箱电源,用串口线、并口线、网线、连接实验箱和主机
2.安装ADS
安装文件在00-ads1.2目录下,破解方法00-ads1.2\Crack目录下
3.安装GIVEIO驱动
安装文件在01-GIVEIO目录
(1)把整个GIVEIO目录拷贝到C:\WINDOWS下,并把该目录下的giveio.sys文件拷贝到c:/windows/system32/drivers下。
(2)在控制面板里,选添加硬件>下一步>选-是我已经连接了此硬件>下一步>选中-添加新的硬件设备>下一步>选中安装我手动从列表选择的硬件>下一步>选择-显示所有设备>选择-从磁盘安装-浏览,指定驱动为C:\WINDOWS\GIVEIO\giveio.inf文件,点击确定,安装好驱动
4.安装JTAG驱动
安装文件在02-UArmJtag2.0目录下,步骤如下:
(1)添加驱动
UarmJtag2.0安装完后,请连接好UP-NETARM3000一简易仿真器(C Jtag)一并口线一PC机,然后添加硬件,其步骤如下(以Windows XP为例):开始一控制面板一添加硬件一下一步一选一是我己经连接了此硬件一下一步一选中一添加新的硬件设备一下一步一选中安装我手动从列表选择的硬件一下一步一选择一显示所有设备一选择(过程比较长)一从磁盘安装一浏览,指定驱动为C:\Program Files\UArmJtag\driver\LPTJtag文件,点击确定,然后点击下一步,安装好驱动。
(2)仿真调试
点击“初始化配置”,在弹出的对话框中,“处理器类型”,选择相应的ARM7或ARMS(默认为ARM7,点击确定;在AXD中执行菜单Options I Configure Target对AXD进行设置;选择ADP即远程调试,点Configure按钮进一步设置具体参数;点Select按钮选择远程连接为ARM ethernet driver,点Configure按钮输入简易并行口仿真器的IP地址,输入127.0.0.1即可,即可进行仿真调试。
5.配置超级终端:波特率为 115200,数据位 8,无奇偶校验,停止位 1,无数据流控制。
6.测试基本安装是否正确,参考文档“03-测试文档.doc”,测试程序在04-Test目录下;
(1)首先运行ADS1.2集成开发环境,单击File->Open选项,选择工程文件我们以光盘中的/ucos v6.0/Exp11 绘图的API函数为例选择Exp11.mcp后点击打开。
(2)选择需要编译的文件,在相应文件夹前的空白位置点击即可选中,头文件库文件是不能选中的,因为已经被包含到了源代码中。
(3)点击make进行编译链接;编译后出现编译结果,显示没有错误,4个警告和15条其他信息。
(4)在调试程序运行之前务必先开启Uarmjtag点击初始化配置选择ARM9点击确定。最小化到托盘即可,并且保证我们的平台停止在VIVI状态下。
(5)点击Debug按钮进行仿真调试,弹出调试界面;状态不可用时,请点击Options->Configure Target,弹出对话框,点击Configure,主要更改了两个地方Name:选择ARM Ethernet driver,Configuration:配置为:127.0.0.1,点击确定,之后选择ADP点击确定。
如果加载完毕在文本去没有任何显示,那么则需要将其关闭再运行一次Debug即可。
这样后再点击Debug这次就是正常的加载状态。加载完成后,点击run(运行)按钮,之后正常状态下他会停止在main函数,这是系统自动的。
之后继续点击RUN按钮即可运行绘图实验。这时液晶屏上会出现我们绘制好的图形。在结束前请先暂停再退出程序。
参考驱动代码 demo.c 如下,其中的 demo_read,demo_write 函数完成驱动的读写接口功能,do_write 函数实现将用户写入的数据逆序排列,通过读取函数读取转换后的数据。
这里只是演示接口的实现过程和内核驱动对用户的数据的处理。 Demo_ioctl函数演示ioctl调用接口的实现过程。
test_demo.c
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>//其中定义了很多宏和诸如open,close函数
#include <unistd.h>
#include <sys/ioctl.h>//ioctl函数的头文件
void showbuf(char *buf);
int MAX_LEN=32;
int main()
{
int fd;
int i;
char buf[255];
for(i=0; i<MAX_LEN; i++){//给数组元素依次赋值
buf[i]=i;
}
fd=open("/dev/demo",O_RDWR);//以既可以读又可以写的方式打开文件
if(fd < 0){
printf("####DEMO device open fail####\n");
return (-1);
}
printf("write %d bytes data to /dev/demo \n",MAX_LEN);
showbuf(buf);//先显示一下要写入什么,然后写入
write(fd,buf,MAX_LEN);
printf("Read %d bytes data from /dev/demo \n",MAX_LEN);
read(fd,buf,MAX_LEN);
showbuf(buf);//先读出来字符串到buf中,再显示
ioctl(fd,1,NULL);
ioctl(fd,4,NULL);
close(fd);
return 0;
}
void showbuf(char *buf)
{
int i,j=0;
for(i=0;i<MAX_LEN;i++){
if(i%4 ==0)
printf("\n%4d: ",j++);
printf("%4d ",buf[i]);
}
printf("\n*****************************************************\n");
}
demo.c
linux driver example for UP-netarm3000 & UP-netarm2410
It can be compiled for x86 PC
//#define CONFIG_DEVFS_FS
#ifndef __KERNEL__
# define __KERNEL__
#endif
#ifndef MODULE
# define MODULE
#endif
#include <linux/config.h>
#include <linux/module.h>
#include <linux/devfs_fs_kernel.h>
#include <linux/init.h>
#include <linux/kernel.h> /* printk() */
#include <linux/slab.h> /* kmalloc() */
#include <linux/fs.h> /* everything... */
#include <linux/errno.h> /* error codes */
#include <linux/types.h> /* size_t */
#include <linux/proc_fs.h>
#include <linux/fcntl.h> /* O_ACCMODE */
#include <linux/poll.h> /* COPY_TO_USER */
#include <asm/system.h> /* cli(), *_flags */
#define DEVICE_NAME "demo"
#define demo_MAJOR 254
#define demo_MINOR 0
static int MAX_BUF_LEN=1024;
static char drv_buf[1024];
static int WRI_LENGTH=0;
/*************************************************************************************/
/*逆序排列缓冲区数据*/
static void do_write()
{
int i;
int len = WRI_LENGTH;
char tmp;
for(i = 0; i < (len>>1); i++,len--){
tmp = drv_buf[len-1];
drv_buf[len-1] = drv_buf[i];
drv_buf[i] = tmp;
}
}
/*************************************************************************************/
static ssize_t demo_write(struct file *filp,const char *buffer, size_t count)
{
if(count > MAX_BUF_LEN)count = MAX_BUF_LEN;
copy_from_user(drv_buf , buffer, count);
WRI_LENGTH = count;
printk("user write data to driver\n");
do_write();
return count;
}
/*************************************************************************************/
static ssize_t demo_read(struct file *filp, char *buffer, size_t count, loff_t *ppos)
{
if(count > MAX_BUF_LEN)
count=MAX_BUF_LEN;
copy_to_user(buffer, drv_buf,count);
printk("user read data from driver\n");
return count;
}
/*************************************************************************************/
static int demo_ioctl(struct inode *inode, struct file *file,
unsigned int cmd, unsigned long arg)
{
printk("ioctl runing\n");
switch(cmd){
case 1:printk("runing command 1 \n");break;
case 2:printk("runing command 2 \n");break;
default:
printk("error cmd number\n");break;
}
return 0;
}
/*************************************************************************************/
static int demo_open(struct inode *inode, struct file *file)
{
sprintf(drv_buf,"device open sucess!\n");
printk("device open sucess!\n");
return 0;
}
/*************************************************************************************/
static int demo_release(struct inode *inode, struct file *filp)
{
MOD_DEC_USE_COUNT;
printk("device release\n");
return 0;
}
/*************************************************************************************/
static struct file_operations demo_fops = {
owner: THIS_MODULE,
write: demo_write,
read: demo_read,
ioctl: demo_ioctl,
open: demo_open,
release: demo_release,
};
/*************************************************************************************/
#ifdef CONFIG_DEVFS_FS
static devfs_handle_t devfs_demo_dir, devfs_demoraw;
#endif
/*************************************************************************************/
static int __init demo_init(void)
{
#ifdef CONFIG_DEVFS_FS
devfs_demo_dir = devfs_mk_dir(NULL, "demo", NULL);
devfs_demoraw = devfs_register(devfs_demo_dir, "0", DEVFS_FL_DEFAULT,
demo_MAJOR, demo_MINOR, S_IFCHR | S_IRUSR | S_IWUSR,
&demo_fops, NULL);
#else
int result;
SET_MODULE_OWNER(&demo_fops);
result = register_chrdev(demo_MAJOR, "demo", &demo_fops);
if (result < 0) return result;
// if (demo_MAJOR == 0) demo_MAJOR = result; /* dynamic */
#endif
printk(DEVICE_NAME " initialized\n");
return 0;
}
/*************************************************************************************/
static void __exit demo_exit(void)
{
unregister_chrdev(demo_MAJOR, "demo");
//kfree(demo_devices);
printk(DEVICE_NAME " unloaded\n");
}
/*************************************************************************************/
module_init(demo_init);
module_exit(demo_exit);
在PC机上编写简单的虚拟硬件驱动程序并进行调试, 实验驱动的各个接口函数的实现,
分析并理解驱动与应用程序的交互过程。
1.gcc编译和使用交叉编译的区别
Gcc编译就是只是linux下的编译,需要建立设备节点,使用交叉编译器的话,不需要建立设备节点。
2.驱动程序设计
驱动程序是应用程序和硬件之间的一个软件层,为(许多个)应用程序提供硬件的所有功能。为了处理并发的情况,还需要考虑互斥量和锁等机制。
1.阅读理解源码
进入07_httpd所在的目录,使用vi编辑器理解源代码。
2.编译应用程序
使用gcc编译器,分别对文件夹下的copy.c和httpd.c进行编译,出现copy和httpd的可执行文件。
3.下载调试
使用NFS服务方式将HPPTD下载到开发板上,并拷贝测试用的网页进行调试
4.本机测试
在台式机的浏览器中输入http://192.168.0.121,观察在客户机的浏览器中的链接请求结果在开发板服务器上的打印信息。
httpd.c代码分析
/ * httpd.c: A very simple http server
* Copyfight (C) 2003 Zou jian guo <[email protected]>
* Copyright (C) 2000 Lineo, Inc. (www.lineo.com)
* Copyright (c) 1997-1999 D. Jeff Dionne <[email protected]>
* Copyright (c) 1998 Kenneth Albanowski <[email protected]>
* Copyright (c) 1999 Nick Brok <[email protected]>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
*/
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <errno.h>
#include <sys/stat.h>
#include <dirent.h>
#include <signal.h>
#include <unistd.h>
#include <ctype.h>
#include "pthread.h"
#define DEBUG
int KEY_QUIT=0;
int TIMEOUT=30; //设置闹钟秒数;
#ifndef O_BINARY
#define O_BINARY 0
#endif
char referrer[128];
int content_length;
#define SERVER_PORT 80
int PrintHeader(FILE *f, int content_type) //发送HTTP协议数据头
{
alarm(TIMEOUT);
fprintf(f,"HTTP/1.0 200 OKn"); //服务器回应http协议数据头的状态行;发送请求成功;
switch (content_type)
{
case 't':
fprintf(f,"Content-type: text/plainn"); //发送纯文本文件信息;
break;
case 'g':
fprintf(f,"Content-type: image/gifn"); //发送gif格式图片信息;
break;
case 'j':
fprintf(f,"Content-type: image/jpegn"); //发送gpeg格式图片信息;
break;
case 'h':
fprintf(f,"Content-type: text/htmln"); //发送html信息;
break;
}
fprintf(f,"Server: uClinux-httpd 0.2.2n"); //发送服务器版本信息;
fprintf(f,"Expires: 0n"); //发送文件永不过期信息;
fprintf(f,"n"); //打印换行符;
alarm(0);
return(0);
}
int DoJpeg(FILE *f, char *name) //对jpeg格式的文件进行处理;
{
char *buf;
FILE * infile;
int count;
if (!(infile = fopen(name, "r"))) { //通过文件名打开一个文件,只读属性;
alarm(TIMEOUT);
fprintf(stderr, "Unable to open JPEG file %s, %dn", name, errno);
fflush(f);
alarm(0);
return -1;
}
PrintHeader(f,'j');//发送j类型的http协议数据头信息;
copy(infile,f); /* prints the page */
alarm(TIMEOUT);
fclose(infile);
alarm(0);
return 0;
}
int DoGif(FILE *f, char *name) //对gif格式的文件进行处理;
{
char *buf;
FILE * infile;
int count;
if (!(infile = fopen(name, "r"))) { //通过文件名打开一个文件,只读属性;
alarm(TIMEOUT);
fprintf(stderr, "Unable to open GIF file %s, %dn", name, errno);
fflush(f);
alarm(0);
return -1;
}
PrintHeader(f,'g'); //发送g类型的http协议数据头信息
copy(infile,f); /* prints the page */
alarm(TIMEOUT);
fclose(infile);
alarm(0);
return 0;
}
int DoDir(FILE *f, char *name) //对目录进行处理;
{
char *buf;
DIR * dir;
struct dirent * dirent; //dirent不仅仅指向目录,还指向目录中的具体文件,dirent结构体存储的关于文件的信息很少,所以dirent起着一个索引的作用
if ((dir = opendir(name))== 0) { //打开一个目录;
fprintf(stderr, "Unable to open directory %s, %dn", name, errno);
fflush(f);
return -1;
}
PrintHeader(f,'h'); //发送h类型的http协议数据头信息
alarm(TIMEOUT);
fprintf(f, "<H1>Index of %s</H1>nn",name);
alarm(0);
if (name[strlen(name)-1] != '/') { //若名字的后面没有/则默认加上 /;
strcat(name, "/");
}
while(dirent = readdir(dir)) { //读取目录;
alarm(TIMEOUT);
fprintf(f, "<p><a href="/%s%s">%s</p>n", name, dirent->d_name, dirent->d_name);
alarm(0); //发送目录信息;
}
closedir(dir);
return 0;
}
int DoHTML(FILE *f, char *name)
{
char *buf;
FILE *infile; //定义文件流指针
int count;
char * dir = 0;
if (!(infile = fopen(name,"r"))) { //通过文件名打开一个文件,只读属性;
alarm(TIMEOUT);
fprintf(stderr, "Unable to open HTML file %s, %dn", name, errno); //打印打开文件失败信息;
fflush(f);
alarm(0);
return -1;
}
PrintHeader(f,'h'); //发送http协议数据报;f表示客户连接的文件流指针用于写入http协议数据头信息;
copy(infile,f); /* prints the page */ //将打开的文件内容通过发送回客户端;
alarm(TIMEOUT);
fclose(infile);
alarm(0);
return 0;
}
int DoText(FILE *f, char *name) //纯文本文件的处理;
{
char *buf;
FILE *infile; //定义文件流指针;
int count;
if (!(infile = fopen(name,"r"))) { //通过文件名打开一个文件,只读属性
alarm(TIMEOUT);
fprintf(stderr, "Unable to open text file %s, %dn", name, errno);
fflush(f);
alarm(0);
return -1;
}
PrintHeader(f,'t'); //发送t类型的http协议数据头信息;
copy(infile,f); /* prints the page */
alarm(TIMEOUT);
fclose(infile);
alarm(0);
return 0;
}
int ParseReq(FILE *f, char *r)
{
char *bp; //定义指针bp;
struct stat stbuf;
char * arg; //参数指针;
char * c;
int e;
int raw;
#ifdef DEBUG
printf("req is '%s'n", r); //打印请求命令;例如:GET /img/baidu_sylogo1.gif HTTP/1.1rn
#endif
while(*(++r) != ' '); /*skip non-white space*/ //判断buf中的内容是否为空跳过非空白;
while(isspace(*r)) //判断r所在位置的字符是否为空格若为空格则r指向下一个字符;
r++;
while (*r == '/') //判断r所在位置的字符是否为/若为空格则r指向下一个字符;
r++;
bp = r; //将r所指向的内容赋值给bp bp指向/之后的内容;img/baidu_sylogo1.gif HTTP/1.1rn
while(*r && (*(r) != ' ') && (*(r) != '?'))
r++;//当r不为空,并求 r不为?时r指向下一个字符
#ifdef DEBUG
printf("bp='%s' %x, r='%s' n", bp, *bp,r); //打印 r和bp的值;
#endif
if (*r == '?') //判断 r是否为 ?若为?则执行以下语句;
{
char * e; //定义指针变量;
*r = 0; //将r所在位置处的字符设为; 的ASCII码值是0
arg = r+1; //arg指向下一个参数;
if (e = strchr(arg,' '))
{
*e = ''; //如果arg为空则将arg所在位置置为复制给e;
}
} else
{ // 如果当前r指向字符不为 '?', 将r指向字符置为 '',
arg = 0;
*r = 0; // r处设为;
}
c = bp;//将bp赋值给c;
/*zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz*/
if (c[0] == 0x20){ //判断c中的字符内容是否为空格;若为空格
c[0]='.'; //将.和放入c数组中;
c[1]='';
}
/*zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz*/
if(c[0] == '') strcat(c,"."); //若 c中为则将.链接在c后;
if (c && !stat(c, &stbuf)) //通过文件名c获取文件信息,并保存在stbuf中
//返回值: 执行成功则返回0,失败返回-1,错误代码存于errno
{
if (S_ISDIR(stbuf.st_mode))//判断结果是否为特定的值
{
char * end = c + strlen(c); //end指向c的末尾;
strcat(c, "/index.html"); //将/index.html加到c后,后面追加;
if (!stat(c, &stbuf)) //通过文件名c获取文件信息,并保存在stbuf中 ;成功返回0;
{
DoHTML(f, c); //对html文件进行处理;
}
else
{
*end = ''; //将end指向;
DoDir(f,c); //若c中没有"/index.html" 则跳到目录处理目录代码处去执行;
}
}
else if (!strcmp(r - 4, ".gif")) //判断r中的后四个字符,即判断文件类型;
DoGif(f,c); //若是 gif格式的文件则跳转到DoGif对其进行处理;
else if (!strcmp(r - 4, ".jpg") || !strcmp(r - 5, ".jpeg"))
DoJpeg(f,c); //若是 jpg或jpeg格式的文件则跳转到DoJpeg对其进行处理;
else if (!strcmp(r - 4, ".htm") || !strcmp(r - 5, ".html"))
DoHTML(f,c); //若是 htm格式的文件则跳转到DoHTML处对其进行处理;
else
DoText(f,c);//若是 纯文本格式的文件则跳转到DoText对其进行处理
}
else{
PrintHeader(f,'h'); //发送h类型的http协议数据头
alarm(TIMEOUT);
fprintf(f, "<html><head><title>404 File Not Found</title></head>n"); //打印出错信息
fprintf(f, "<body>The requested URL was not found on this server</body></html>n");
alarm(0);
}
return 0;
}
void sigalrm(int signo) //定时器终止时发送给进程的信号;
{
/* got an alarm, exit & recycle */
exit(0);
}
int HandleConnect(int fd)
{
FILE *f;//定义文件流FILE结构体指针用来表示与客户连接的文件流指针;
char buf[160]; //定义缓冲区buf用来存放客户端的请求命令;
char buf1[160]; //定义缓冲区buf用来存放客户端的各字段信息;
f = fdopen(fd,"a+"); //以文件描述符的形式打开文件; a+ 以附加方式打开可读写的文件。若文件不存在,则会建立该文件,如果文件存在,写入的数据会被加到文件尾后,即文件原先的内容会被保留。
if (!f) {//若文件打开失败则打印出错信息;
fprintf(stderr, "httpd: Unable to open httpd input fd, error %dn", errno);
alarm(TIMEOUT); // 闹钟函数成功则返回上一个闹钟时间的剩余时间,否则返回0。 出错返回-1
close(fd);//关闭文件描述符;
alarm(0); //将闹钟时间清0;
return 0;
}
setbuf(f, 0); //将关闭缓冲区;
alarm(TIMEOUT); //启用闹钟;
if (!fgets(buf, 150, f)) { //直接通过f读取150个字符放入以buf为起始地址中,不成功时返回0则打印出错信息;否则fgets成功返回函数指针打印buf的内容;
fprintf(stderr, "httpd: Error reading connection, error %dn", errno);
fclose(f); //关闭文件描述符;
alarm(0);
return 0;
}
#ifdef DEBUG
printf("buf = '%s'n", buf); //打印客户机发出的请求命令;
#endif
alarm(0); //将闹钟时间清0;
referrer[0] = '';//初始化referrer数组;
content_length = -1; //将信息长度初始化为-1;
alarm(TIMEOUT); //设置定时器;
//read other line to parse Rrferrer and content_length infomation
while (fgets(buf1, 150, f) && (strlen(buf1) > 2)) { //直接通过f读取150个字符放入以buf1为起始地址的空间中;
alarm(TIMEOUT);
#ifdef DEBUG
printf("Got buf1 '%s'n", buf1); //打印buf1中的信息;
#endif
if (!strncasecmp(buf1, "Referer:", 8)) { //将buf1中的前八个字符与字符串Referer:若相等则将将指针指向buf1中的Referer:之后;
char * c = buf1+8;
while (isspace(*c)) //判断c处是否为空格若为空格则c指向下一个字符;
c++;
strcpy(referrer, c); //将c所指的内存单元的内容复制到referrer数组中;
}
else if (!strncasecmp(buf1, "Referrer:", 9)) { //将buf1中的前九个字符与字符串Referrer:若相等则将将指针指向buf1中的Referrer:之后;
char * c = buf1+8;
char * c = buf1+9;
while (isspace(*c)) //判断c处是否���空格若为空格则c指向下一个字符;
c++;
strcpy(referrer, c); //将c所指的内存单元的内容复制到referrer数组中;
}
else if (!strncasecmp(buf1, "Content-length:", 15)) { )) { //将buf1中的前15个字符与字符串Content-length:若相等则将将指针指向buf1中的Content-length:之后;
content_length = atoi(buf1+15); //atoi类型转换将buf1中的内容转换为整型赋值给content_length;
}
}
alarm(0);
if (ferror(f)) { //错误信息输出;
fprintf(stderr, "http: Error continuing reading connection, error %dn", errno);
fclose(f);
return 0;
}
ParseReq(f, buf); //解析客户请求函数;
alarm(TIMEOUT); //打开计时器;
fflush(f); //刷新流;
fclose(f); //关闭文件流;
alarm(0);
return 1;
}
void* key(void* data)
{
int c;
for(;;){
c=getchar(); //从键盘输入一个字符
if(c == 'q' || c == 'Q'){
KEY_QUIT=1;
exit(10); //若输入q则退出程序;
break;
}
}
}
int main(int argc, char *argv[])
{
int fd, s; //定义套接字文件描述符作为客户机和服务器之间的通道;
int len;
volatile int true = 1; //定义volatile类型的变量用来作为指向缓冲区的指针变量;
struct sockaddr_in ec;
struct sockaddr_in server_sockaddr; //定义结构体变量;
pthread_t th_key;//定义线程号;
void * retval; //用来存储被等待线程的返回值。
signal(SIGCHLD, SIG_IGN); //忽略信号量;
signal(SIGPIPE, SIG_IGN);
signal(SIGALRM, sigalrm); //设置时钟信号的对应动作;
chroot(HTTPD_DOCUMENT_ROOT); //改变根目录;在makefile文件中指定;
printf("starting httpd...n"); //打印启用服务器程序信息;
printf("press q to quit.n");
// chdir("/");
if (argc > 1 && !strcmp(argv[1], "-i")) {// 若argv【1】等于-i strcmp返回0 并且 argc大于1 执行if下的语句快即关闭文件描述符;
/* I'm running from inetd, handle the request on stdin */
fclose(stderr);
HandleConnect(0); //向HandleConnect函数传入0文件描述符即标准输入;
exit(0);
}
if((s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) == -1) { //若获取套接字出错则将错误信息输出到标准设备;
perror("Unable to obtain network");
exit(1);
}
if((setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (void *)&true, //此函数用于设置套接口,若成功返回0,否则返回错误
sizeof(true))) == -1) {
perror("setsockopt failed"); //输出错误信息;
exit(1);
}
server_sockaddr.sin_family = AF_INET; //设置ip地址类型;
server_sockaddr.sin_port = htons(SERVER_PORT); //设置网络端口;
server_sockaddr.sin_addr.s_addr = htonl(INADDR_ANY); //INADDR_ANY表示本地任意ip;
if(bind(s, (struct sockaddr *)&server_sockaddr, //将所监听的端口号与服务器的地址、端口绑定;
sizeof(server_sockaddr)) == -1) {
perror("Unable to bind socket");//若绑定失败则打印出错信息;
exit(1);
}
if(listen(s, 8*3) == -1) { //listen()声明服务器处于监听状态,并且最多允许有24个客户端处于连接待状态;
perror("Unable to listen");
exit(4);
}
pthread_create(&th_key, NULL, key, 0); //创建线程;
/* Wait until producer and consumer finish. */
printf("wait for connection.n"); //打印服务器等待链接信息;
while (1) {
len = sizeof(ec);//ec结构体变量的长度;
if((fd = accept(s, (void *)&ec, &len)) == -1) { //接受客户机的请求,与客户机建立链接;
exit(5);
close(s);
}
HandleConnect(fd); //处理链接函数调用fd 为客户连接文件描述符;;
}
pthread_join(th_key, &retval); //以阻塞的方式等待thread指定的线程结束。当函数返回时,被等待线程的资源被收回。如果进程已经结束,那么该函数会立即返回。成功返回0;该语句不会执行到;
}