2008年4月28日星期一

广义表的实现

实现广义表,具体没有做习题,感觉有些累了。呵呵。不知再往下看,脑细胞是否经得起折腾。先把源文件贴 上来。
//glist.h
//----------------------- 广义表 ---------------------------------------
typedef enum...{ATOM, LIST} ElemTag; //ATOM==0:原子,LIST==1:子表
typedef int AtomType; //原子的数据类型

typedef struct GLNode...{
ElemTag tag; //公共部分,区分原子节点和表节点
union...{ //原子节点和表节点的联合部分
AtomType atom; //原子节点的值
struct GLNode *hp; //表节点的表头指针
};
struct GLNode *tp; //相当于线形链表的next,指向下一个元素
}*GList;

//----------------- 基本操作的算法实现 -------------------------------
Status Sever(HString &str, HString &hstr)...{
//将非空串str分割成两部分:hstr为第一个','之前的自串,str为之后的子串
int n = StrLength(str), i=1, k=0; //k记尚未配对的左括号个数
HString ch;StrInit(ch);StrAssign(ch,"a");
for(i=1, k=0; i<=n&&ch.ch[0]!=','||k!=0;i++)...{ //搜索最外层的第一个','
SubString(ch,str,i,1);
if(ch.ch[0]=='(') ++k;
else if(ch.ch[0]==')')--k;
}
if(i<=n)...{ SubString(hstr,str,1,i-2); SubString(str,str,i,n-i+1); }
else...{StrCopy(hstr,str); ClearString(str);}
return OK;
}

Status CreateGList(GList &L, HString S)...{
//采用头尾链接存储结构,由广义表的书写形式串S创建广义表L
HString emp; StrInit(emp); StrAssign(emp,"()");
GList p,q;
if( !StrCompare(S,emp) ) L=NULL; //创建空表
else...{
if(!(L=(GList)malloc(sizeof(GLNode)))) exit(OVERFLOW); //创建节点
if(StrLength(S)==1) ...{L->tag = ATOM; L->atom = *(S.ch);} //创建原子节点
else...{
L->tag = LIST; p=L;
HString sub,hsub; StrInit(sub); StrInit(hsub);
SubString(sub,S,2,StrLength(S)-2); //脱外层扩号
do...{
Sever(sub,hsub);
CreateGList(p->hp,hsub); q=p;
if(!StrEmpty(sub))...{
if(!(p=(GLNode *) malloc(sizeof(GLNode))))
exit(OVERFLOW);
p->tag = LIST; q->tp = p;
}
}while(!StrEmpty(sub));
q->tp = NULL;
}
}
return OK;
}

Status CopyGList(GList &T, GList L)...{
//L复制给T
if(!L) T=NULL;
else...{
if(!(T=(GList)malloc(sizeof(GLNode)))) exit(OVERFLOW);
T->tag = L->tag;
if(L->tag ==ATOM ) T->atom = L->atom;
else...{
CopyGList(T->hp, L->hp);
CopyGList(T->tp, L->tp);
}
}
return OK;
}

int GListDepth(GList L)...{
//求广义表的深度
if(!L) return 1;
if(L->tag==ATOM) return 0;
int max;
GList pp;
for(max=0,pp=L;pp;pp=pp->tp)...{
int dep = GListDepth(pp->hp);
if(dep>max) max= dep;
}
return max+1;
}

Status PrintGList(GList L)...{
//显示广义表
GList p = L;
if(!L) ...{printf("()"); return OK; }
if(L->tag== ATOM) printf("%c",L->atom);
else...{
printf("(");
while(p!=NULL)...{
PrintGList(p->hp);
p=p->tp;
if(p) ...{ printf(","); }
}
printf(")");
}
return OK;
}
2.

//hstring.h
//----------------- 串的堆分配存储表示 ---------------------------
typedef struct{
char *ch; //若是非空串,则按串长分配存储区,否则ch为NULL
int length; //串长度
}HString;

//----------------- 栈的基本操作的算法实现 --------------------------------
Status StrInit(HString &T){
//初始化串
T.ch=NULL; T.length=0;
return OK;
}

Status StrAssign(HString &T, char *chars){
//生成一个其值等于串常量chars的串T
//if(T.ch) free(T.ch); //释放原有的串空间
int len; char *c;
for(len=0, c=chars;*c;++len,++c); //求chars的长度i;
if(!len) {T.ch=NULL; T.length=0;}
else{
if( !(T.ch = (char *) malloc(len*sizeof(char) ) ))
exit(OVERFLOW);
for(int i=0;i<len;i++) T.ch[i] = chars[i];
T.ch[i]='\0'; //结尾加上\0
T.length = len;
}

return OK;
}

int StrLength(HString s){
//返回串长度
return s.length;
}

void StrPrint(HString s){
//打印串
for(int i=0;i<s.length;i++)
printf("%c",s.ch[i]);
printf("\n");
}

int Index(HString s,HString t, int pos){
//返回子串t在主串s中第pos个字符之后的位置。如不存在,则返回0
int i=pos,j=1;
while(i<=s.length && j<=t.length){
if(s.ch[i-1]==t.ch[j-1]) i++,j++;
else i=i-j+2,j=1;
}
if(j>t.length) return i-t.length;
else return 0;
}

int Next(HString s,int j){
//KMP模式匹配的next函数
if(j==1) return 0;

for(int k=j-1;k>1;k--){
for(int i=1;i<k;i++){
if(s.ch[i-1] != s.ch[j-k+i-1])
break;
}
if(i==k) break;
}
return k;
}

int Index_KMP(HString s,HString t, int pos){
//KMP算法
int i=pos,j=1;
while(i<=s.length && j<=t.length){
if(j==0 || s.ch[i-1]==t.ch[j-1]) i++,j++;
else j=Next(t,j);
}
if(j>t.length) return i-t.length;
else return 0;
}

Status SubString(HString &sub, HString s, int pos, int len){
//用sub返回串S的第pos个字符起长度为len的子串
if(pos<1 || pos>s.length || len<0 )
return ERROR;
if( len>s.length-pos+1 ) len=s.length-pos+1; //如果所取长度超过实际长度,输出实际长度
// if(sub.ch) free(sub.ch);
if(!len){ sub.ch=NULL; sub.length=0; }
else{
if( !(sub.ch = (char *) malloc(len*sizeof(char) ) ))
exit(OVERFLOW);
for(int i=0;i<len;i++) sub.ch[i] = s.ch[pos+i-1];
sub.ch[i]='\0'; //结尾加上\0
sub.length = len;
}
return OK;
}

Status ClearString(HString &s){
//清空s
if(s.ch) {
// realloc(s.ch,0);
s.ch=NULL;
}
s.length =0;
return OK;
}

int StrCompare(HString s,HString t){
//比较s,t
for(int i=0;i<s.length&& i<t.length;i++)
if(s.ch[i]!=t.ch[i]) return s.ch[i]-t.ch[i];
return s.length - t.length;
}

Status Concat(HString &t, HString s1, HString s2){
//用t返回由s1和s2联接而成的新串
//if(t.ch) free(t.ch);
if(!(t.ch=(char *)malloc((s1.length+s2.length)*sizeof(char))))
exit(OVERFLOW);
for(int i=0;i<s1.length;i++) t.ch[i]=s1.ch[i];
for(i=0;i<s2.length;i++) t.ch[i+s1.length]=s2.ch[i];
t.ch[s1.length+s2.length]='\0';
t.length = s1.length+s2.length;
return OK;
}

Status StrCopy(HString &t,HString s){
if(!(t.ch=(char *)malloc( (s.length)*sizeof(char))))
exit(OVERFLOW);
for(int i=0;i<s.length;i++) t.ch[i]=s.ch[i];
t.ch[s.length] = '\0';
t.length = s.length;
return OK;
}

Status StrEmpty(HString s){
return s.length==0;
return OK;
}

ipc 通信机制

所谓进程间通讯,顾名思义,就是在2个(多数情况下)或多个进程间传递信息。方法大致如下几种:

1, 文件(file),匿名管道(anonymous pipe),命名管道(named pipe),信号(signal).

2、 System V IPC 包括消息队列(message queue),共享内存(shared
memory),信号量(semaphore)。这种形式的ipc首先在UNIX分支system V中使用,现在多数unix系统都支持。


文件形式的IPC:


进程(process) A写信息到文件1,进程B读文件1。文件的内容,由进程自己决定。

匿名管道:


command1 args1 | command2 args2. 最常见的例子:ls �l |more
由于管道操作由shell代替完成,没有产生有名字的实体,所以称为匿名管道。
Shell做的事情是调用pipe(),产生一个管道,然后把command1的输出连接到管道的出入端,把command2的输入连接到管道的输出端。

命名管道


首先,建立一个特殊文件,mkfifo pipe1或者mknod fifo1 p

然后,就当作正常文件读写pipe1。例如: ls > fifo1 (写入)。

while read a

do

echo $a

done <fifo1 (读出)

由于产生有名字的实体,所以被称为命名管道。

信号:


简单的用法: kill �USER2
pid,也就是通过kill()系统调用或者kill命令,发送信号到别的进程。各个进程对于信号的处理过程是自己定义的(除了9,也就是KILL是强制的)。比如自己可以忽略HUP,TERM,INT(按control-C),
等。


消息队列(message queue)


消息队列,是一个队列的结构,队列里面的内容由用户进程自己定义。实际上,队列里面记录的是指向用户自定义结构的指针和结构的大小。要使用message
queue,首先要通过系统调用(msgget)产生一个队列,然后,进程可以用msgsnd发送消息到这个队列,消息就是如上所说的结构。别的进程用msgrcv读取。消息队列一旦产生,除非明确的删除(某个有权限的进程或者用ipcrm命令)或者系统重启。否则,产生的队列会一直保留在系统中。而且,只要有权限,就可以对队列进行操作。消息队列和管道很相似,实际上,管道就是用户消息为1个字节的队列。

ipcs �aq命令可以查看message queue的状况:

Message Queues:

T ID KEY MODE OWNER GROUP CREATOR CGROUP
CBYTES QNUM QBYTES LSPID LRPID STIME RTIME CTIME

q 256 0x417d0896 --rw------- root daemon root daemon
0 0 16384 97737 210466 14:31:14 14:31:14 9:52:53

其中:

T: 类型, q 表明这是个消息队列

ID: 用户自己定义的,在调用msgget时传送的参数。

Key: 系统返还的全局唯一的ID。

Mode: 权限,含义和文件权限基本一致

Owner, group: 队列建立者的名字和组

CREATOR, CGROUP:队列建立者和组的ID

CBYTES : 目前queue在队列里的字节数

QNUM, 目前queue在队列里的消息数

QBYTES: 队列中消息最大允许字节数

LSPID: 最后发送者PID

LRPID: 最后接受者PID

STIME: 最后发送时间

RTIME: 最后接受时间。.

CTIME: 建立或者最后修改的时间


共享内存(shared memory)


共享内存是一段可以被多个进程共享的内存段。首先,用shmget系统调用产生指定大小的共享内存段,然后需要访问此共享内存的进程调用shmat系统调用,把这个内存段附加到自己的地址空间,然后就可以像访问自己私有的内存一样访问这个内存段了。等到访问完毕,用shmdt脱离。同message
queue一样,共享内存一旦产生,除非明确的删除(某个有权限的进程或者用ipcrm命令)或者系统重启。否则,产生的共享内存会一直保留在系统中。而且,只要有权限,就可以对共享内存进行操作。共享内存的内容由进程自己定义。为了防止多个进程在同一时间写同样一段共享内存,一般程序会使用信号量来控制对某一段地址的读写。

ipcs �am命令可以查看share memory的状况:

Shared Memory:

T ID KEY MODE OWNER GROUP CREATOR CGROUP
NATTCH SEGSZ CPID LPID ATIME DTIME CTIME

m 258 0 --rw-r----- oracle dba oracle dba
12 8388608 106303 106329 16:28:54 16:48:36 16:28:49


T: 类型 m 表明这是个共享内存

ID: 用户自己定义的,在调用shmget时传送的参数。

Key: 系统返还的全局唯一的ID。

Mode: 权限,含义和文件权限基本一致

Owner, group: 队列建立者的名字和组

CREATOR, CGROUP:队列建立者和组的ID

NATTCH: 有几个进程挂接(attach)在这段共享内存上

SEGSZ: 共享内存段大小(字节)

CPID: 产生者PID

LPID: 最后挂接(attach)或者脱离(detach)者PID

ATIME: 最后挂接(attach)时间

DTIME: 最后脱离(detach)时间。.

CTIME: 建立或者最后修改的时间


信号量(semaphore)


在操作系统中,有些资源数量是有限的,在同一时间,只能由有限(一个或几个)的进程使用和访问。例如磁带机,同一时间,只能由一个进程使用。这样的资源被称为关键(critical)资源。信号量就是用来记录关键资源的使用情况的。首先,利用系统调用semget产生一个信号量。当需要使用关键资源时,调用semop,传递的参数为需要使用的资源的数量,例如2个,参数就为+2。如果这个资源有2个或者更多可用,进程就获得了使用权,否则就必须等待,直到有足够的资源可用。当进程使用资源结束的时候,也用semop释放关键资源。参数为需要释放的数量,例如2,参数为-2。同message
queue一样,共信号量一旦产生,除非明确的删除(某个有权限的进程或者用ipcrm命令)或者系统重启。否则,信号量会一直保留在系统中。而且,只要有权限,就可以对其进行操作。

ipcs �as命令可以查看Semaphore的状况:

Semaphores:

T ID KEY MODE OWNER GROUP CREATOR CGROUP
NSEMS OTIME CTIME

s 0 0x696e6974 --ra-r--r-- root system root system
8 9:52:53 9:59:30


T: 类型 s 表明这是个信号量

ID: 用户自己定义的,在调用semget时传送的参数。

Key: 系统返还的全局唯一的ID。

Mode: 权限,含义和文件权限基本一致

Owner, group: 队列建立者的名字和组

CREATOR, CGROUP:队列建立者和组的ID

NSEMS: 本信号量上信号的数量。

OTIME: 最后一次操作(semop)的时间

CTIM: 建立或者最后修改的时间

jni 简介

1.简介

  JNI是Java Native Interface的缩写,它的设计目的是:

  The standard Java class library may not support the
platform-dependent features needed by your application.

  You may already have a library or application written in another
programming language and you wish to make it accessible to Java
applications

  You may want to implement a small portion of time-critical code in a
lower-level programming language, such as assembly, and then have your
Java application call these functions

  2.JNI的书写步骤

  编写带有native声明的方法的java类

  使用javac命令编译所编写的java类

  使用javah ?jni java类名生成扩展名为h的头文件

  使用C/C++实现本地方法

  将C/C++编写的文件生成动态连接库

  ok

  1) 编写java程序:

  这里以HelloWorld为例。

  代码1:

  class HelloWorld {

  public native void displayHelloWorld();

  static {

  System.loadLibrary("hello");

  }

  public static void main(String[] args) {

  new HelloWorld().displayHelloWorld();

  }

  }

  声明native方法:如果你想将一个方法做为一个本地方法的话,那么你就必须声明改方法为native的,并且不能实现。其中方法的参数和返回值在后面讲述。

  Load动态库:System.loadLibrary("hello");加载动态库(我们可以这样理解:我们的方法displayHelloWorld()没有实现,但是我们在下面就直接使用了,所以必须在使用之前对它进行初始化)这里一般是以static块进行加载的。同时需要注意的是System.loadLibrary();的参数"hello"是动态库的名字。

  main()方法

  2) 编译没有什么好说的了

  javac HelloWorld.java

  3) 生成扩展名为h的头文件

  javah ?jni HelloWorld

  头文件的内容:

  /* DO NOT EDIT THIS FILE - it is machine generated */

  #include <jni.h>

  /* Header for class HelloWorld */

  #ifndef _Included_HelloWorld

  #define _Included_HelloWorld

  #ifdef __cplusplus

  extern "C" {

  

虚拟主机原理

虚拟主机原理


--------------------------------------------------------------------------------

发表日期:2006年5月20日 作者:独孤吟 已经有246位读者读过此文


虚拟主机是指在一台服务器里运行几个网站、提供WEB、FTP、Mail等服务。本文主要介绍WEB服务的虚拟主机设置。
虚拟主机有两种实现方法:基于IP的方法和基于主机名的方法。

基于IP的方法:

首先,在服务器里绑定多个IP,然后配置WEB服务器,把多个网站绑定在不同的IP上。访问不同的IP,就看到不同的网站。

基于主机名的方法:

首先,设置多个域名的A记录,使它们解析到同一个IP地址上,即同一个服务器上。然后,在服务器上配置WEB服务端,添加多个网站,为每个网站设定一个主机名。因为HTTP协议访问请求里包含有主机名信息,当WEB服务器收到访问请求时,就可以根据不同的主机名来访问不同的网站。

基本IP的方法在局域网中比较常用,基于主机名的方法在Internet中比较常用。下面以两个最常用的WEB服务器IIS和Apache为例,介绍基于主机名的虚拟主机的设置方法。

设置虚拟主机的主要步骤:

1、在动态域名客户端软件里添加多个域名。这一步的目的,是让这些域名都解析到同一个服务器上。(注:公网客户端和内网专业版TrueHost客户端可添加多个域名,内网标准版不支持多域名)。

2、在用户机器的WEB服务器(IIS、Apache等)上添加域名配置虚拟主机。


IIS虚拟主机设置

1、打开"控制面板"->"管理工具"->"Internet服务管理器"->"默认web站点"。

2、在"默认web站点"上按鼠标右键,选择"新建"->"站点"。按"下一步"。

3、输入站点说明,如"站点1"。按"下一步"。

4、在"站点的主机头"上输入域名,如"abc.dns0755.net"。按"下一步"。

5、在路径里指定站点的根目录路径。按"下一步"。

6、在权限里选择适当的权限。按"下一步",即可完成。

如果配置的是顶级域名的虚拟主机,例如在上面第4步主机头里输入"abc.com",而同时又希望用户使用www.abc.com"也能访问。设置步骤如下:

1、在"Internet服务管理器"的"站点1"上按鼠标右键,选择"属性"。

2、在IP地址右边点击"高级"。

3、点击"添加",输入端口号(一般用80),再输入主机头名www.abc.com"。

如果有多个站点要添加,请重复执行上面的步骤。


Apache虚拟主机设置

1、打开Apache配置文件"httpd.conf",查找"#NameVirtualHost *",把这行前面的"#"去掉。

2、在"NameVirtualHost *"这行下面,增加虚拟主机站点。示例如下:

<VirtualHost *>
ServerAdmin webmaster@comexe.cn
DocumentRoot /export/home/dns0755
ServerName dns0755.net
ServerAlias *.dns0755.net
ScriptAlias /cgi-bin/ /export/home/dns0755/cgi-bin/
ErrorLog "| /usr/local/sbin/rotatelogs
/var/log/http/dns0755-err.log 604800"
CustomLog "| /usr/local/sbin/rotatelogs
/var/log/http/dns0755.log 604800" combined
</VirtualHost>

说明:

ServerAdmin webmaster@comexe.cn
站点管理员Email地址

DocumentRoot /export/home/dns0755
站点根目录

ServerName dns0755.net
站点主机名

ServerAlias *.dns0755.net
站点别名,"*"表示任意字符

ScriptAlias /cgi-bin/ /export/home/dns0755/cgi-bin/
执行脚本文件存放路径

ErrorLog "| /usr/local/sbin/rotatelogs /var/log/http/dns0755-err.log 604800"
错误日志控制

CustomLog "| /usr/local/sbin/rotatelogs /var/log/http/dns0755.log
604800" combined
访问日志

/usr/local/sbin/rotatelogs是日志管理程序
/var/log/http/dns0755.log是日志文件名
604800的单位是秒,这种写法表示每隔7天产生一个日志文件


如果有多个站点要添加,请重复执行第2步操作

一个网卡绑定多个IP和多个网卡用一个ip的设置

一个网卡绑定多个IP和多个网卡用一个ip的设置

常用到的是"一个网卡绑定多个IP"


一个网卡绑定多个IP

linux的网络设备配置文件存放在/etc/sysconfig/network-scripts里面,
对于以太网的第一个网络设备,配置文件名一般为ifcfg-eth0。
如果需要为第一个网络设备多绑定一个IP地址,只需要在
/etc/sysconfig/network-scripts目录里面创建一个名为ifcfg-eth0:0的文件,
内容样例为:

DEVICE="eth0:0"
IPADDR="211.100.10.119"
NETMASK="255.255.255.0"
ONBOOT="yes"

其中的DEVICE为设备的名称,
IPADDR为此设备的IP地址,
NETMASK为子网掩码
ONBOOT 表示在系统启动时自动启动。
如果需要再绑定多一个IP地址,
只需要把文件名和文件内的DEVICE中的eth0:x加一即可。
LINUX最多可以支持255个IP别名


多个网卡绑定一个IP

使用多块网卡虚拟成为一块网卡,具有相同的IP地址。
这项技术其实在sun和cisco中已经存在,分别称为Trunking和etherchannel技术,
在linux中,这种技术称为bonding。
因为bonding在内核2.4.x中已经包含了,
只需要在编译的时候把网络设备选项中的 Bonding driver support选中就可以了。
  然后,重新编译核心,重新起动计算机,执行如下命令:

  ismod bonding
  ifconfig eth0 down
  ifconfig eth1 down
  ifconfig bond0 ipaddress
  ifenslave bond0 eth0
  ifenslave bond0 eth1

  现在两块网卡已经象一块一样工作了,这样可以提高集群节点间的数据传输。
  你最好把这几句写成一个脚本,再由/etc/rc.d/rc.local调用,
以便一开机就生效。
  bonding对于服务器来是个比较好的选择,在没有千兆网卡时,
用两三块100兆网卡作 bonding,可大大提高服务器到交换机之间的带宽。
但是需要在交换机上设置连接bonding 网卡的两个口子映射为同一个虚拟接口。

为一个网卡绑定多个IP地址

为一个网卡绑定多个IP地址?

  Linux的网络设备配置文件存放在/etc/sysconfig/network-scripts里面,对于以太网的第一个网络设备,配置文件名一般为
ifcfg-eth0 如果需要为第一个网络设备绑定多一个IP地址,只需要在/etc/sysconfig/network-scripts目录里面创建一个名为ifcfg-eth0:0的文件,内容样例为:

DEVICE="eth0:0"
IPADDR="211.100.10.119"
NETMASK="255.255.255.0"
ONBOOT="yes"

  其中的DEVICE为设备的名称,IPADDR为此设备的IP地址,NETMASK为子网掩码,ONBOOT表示在系统启动时自动启动。
  如果需要再绑定多一个IP地址,只需要把文件名和文件内的DEVICE中的eth0:x加一即可。LINUX最多可以支持255个IP别名。

什么是IP地址?

什么是IP地址?


  尽管互联网上联接了无数的服务和电脑,但它们并不是处于杂乱无章的无序状态,而是每一个主机都有惟一的地址,作为该主机在Internet上的唯一标志。我们称为IP地址(Internet
Protocol Address)。它是一串4组由圆点分割的数字组成的,其中每一组数字都在0-256之间,如:0-255.0-255.0-255.0-255.0-255;如,202.202.96.33就是一个主机服务器的IP地址。

  另一种表示方法摆脱了数字的单调和难记的缺点,用域名DN(Domain
Name)来表示,即代表该主机的一个文字名称,如www.lg.com.cn是一家公司主机服务器的域名。DNS(Domain Name
System)域名服务器系统将形象的文字型域名翻译成对应的数字型IP地址。通过上述IP,域名DN,域名系统DNS,就把每一台主机在Internet上给予了惟一的定位。