2009年6月17日
#
If you are building standalone application in Java, Maven is your friend when packing your application,
There are two way to let Maven package your application, either as a single jar with all your dependencies jar.
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-assembly-plugin</artifactId>
<configuration>
<descriptorRefs>
<descriptorRef>jar-with-dependencies</descriptorRef>
</descriptorRefs>
</configuration>
</plugin>
One advantage if you choose to do this way is if you need to sign your application jar.
This is needed if you are building a Java Web Start client and you need more access than connecting back to the server.
To read more about have Maven signing your jar read http://maven.apache.org/plugins/maven-jar-plugin/usage.html.
But if you choose to go this way, make sure that all license agreement are shipped with your one single jar.
Another way is to let Maven package your source code only and then referring the dependent jar file from the MANIFEST file.
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<configuration>
<archive>
<manifest>
<addClasspath>true</addClasspath>
<mainClass>se.msc.adapter.Main</mainClass>
<classpathPrefix>lib/</classpathPrefix>
</manifest>
</archive>
</configuration>
</plugin>
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.LineNumberReader;
public class Main {
public static void main(String[] args) throws IOException {
InputStreamReader isr = new InputStreamReader(new FileInputStream("15370720.pdf4"), "utf-16");
LineNumberReader lnr=new LineNumberReader(isr);
String line = null;
while((line=lnr.readLine())!=null){
System.out.println(lnr.getLineNumber()+"\t"+line);
}
}
}
两个方法的区别是资源的定义不同, 一个主要用于相对与一个object取资源,而另一个用于取相对于classpath的
资源,用的是绝对路径。
在使用Class.getResourceAsStream 时, 资源路径有两种方式, 一种以 / 开头,则这样的路径是指定绝对
路径, 如果不以 / 开头, 则路径是相对与这个class所在的包的。
在使用ClassLoader.getResourceAsStream时, 路径直接使用相对于classpath的绝对路径。
举例,下面的三个语句,实际结果是一样的:
com.explorers.Test.class.getResourceAsStream("abc.jpg")
= com.explorers.Test.class.getResourceAsStream("/com/explorers/abc.jpg")
= ClassLoader.getResourceAsStream("com/explorers/abc.jpg")
There's a variety of clients for CAS. The
Java-based clients (JA-SIG, Yale, see JA-SIG
website) typically handle the browser-based client interaction with
CAS very well through ServletFilter implementations.
Now what
about programmatic authentication, i.e. achieving authentication through
non-browser based applications? There exists a CAS
.NET client but I did not manage to find the appropriate Java
implementation. So here goes - it is based on the Apache HttpClient.
In
case I missed any existing implementation achieving the same purpose,
let's look at the bright side: at least now I understand the CAS
protocol :-)
My CAS client works within any application. It uses
the HttpClient and behaves like a browser client as CAS requires cookie
support.
Here's the code:
import org.apache.commons.httpclient.Header;
import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.httpclient.HttpMethod;
import org.apache.commons.httpclient.HttpStatus;
import org.apache.commons.httpclient.methods.GetMethod;
import org.apache.commons.httpclient.methods.PostMethod;
import org.apache.log4j.Logger;
/**
* The CasClient allows users to programmatically login
* to CAS protected services based on the CAS 2 protocol.
* This client behaves like a browser-client in terms of
* cookie handling.<br>
*
* @author Mathias Richter
*/
public class CasClient
{
public static Logger LOG = Logger.getLogger( CasClient.class );
public static final String LOGIN_URL_PART = "login";
public static final String SERVICE_VALIDATE_URL_PART = "serviceValidate";
public static final String TICKET_BEGIN = "ticket=";
private static final String LT_BEGIN = "name="lt" value="";
public static final String CAS_USER_BEGIN = "<cas:user>";
public static final String CAS_USER_END = "</cas:user>";
private HttpClient fClient;
private String fCasUrl;
/**
* Construct a new CasClient.
*
* @param casUrl The base URL of the CAS service to be used.
*/
public CasClient( String casBaseUrl )
{
this( new HttpClient(), casBaseUrl );
}
/**
* Construct a new CasClient which uses the specified HttpClient
* for its HTTP calls.
*
* @param client
* @param casBaseUrl
*/
public CasClient( HttpClient client, String casBaseUrl )
{
fClient = client;
fCasUrl = casBaseUrl;
}
/**
* Authenticate the specified username with the specified password.
* This will not yield any ticket, as no service is authenticated
* against. This wil just set the CAS cookie in this client upon
* successful authentication.
*
* @param username
* @param password
*/
public void authenticate( String username, String password )
{
authenticate( null, username, password );
}
/**
* Validate the specified service ticket against the specified service.
* If the ticket is valid, this will yield the clear text user name
* of the autenticated user.<br>
* Note that each service ticket issued by CAS can be used exactly once
* to validate.
*
* @param serviceUrl
* @param serviceTicket
*
* @return Clear text username of the authenticated user.
*/
public String validate( String serviceUrl, String serviceTicket )
{
String result = null;
PostMethod method = new PostMethod( fCasUrl + SERVICE_VALIDATE_URL_PART );
method.setParameter( "service", serviceUrl );
method.setParameter( "ticket", serviceTicket );
try
{
int statusCode = fClient.executeMethod(method);
if (statusCode != HttpStatus.SC_OK)
{
LOG.error( "Could not validate: " + method.getStatusLine() );
method.releaseConnection();
} else
{
result = extractUser( new String( method.getResponseBody() ) );
}
} catch ( Exception x )
{
LOG.error( "Could not validate: " + x.toString () );
x.printStackTrace();
}
method.releaseConnection();
return result;
}
/**
* Authenticate the specified user with the specified password against the
* specified service.
*
* @param serviceUrl May be null. If a url is specified, the authentication will happen against this service, yielding a service ticket which can be validated.
* @param username
* @param password
* @return A valid service ticket, if and only if the specified service URL is not null.
*/
public String authenticate( String serviceUrl, String username, String password )
{
String lt = getLt( serviceUrl );
if ( lt == null )
{
LOG.error( "Cannot retrieve LT from CAS. Aborting authentication for '" + username + "'" );
return null;
}
String result = null;
PostMethod method = new PostMethod( fCasUrl + LOGIN_URL_PART );
if ( serviceUrl != null ) // optional
method.setParameter( "service", serviceUrl );
method.setParameter( "_eventId", "submit" );
method.setParameter("username", username );
method.setParameter("password", password );
method.setParameter("lt", lt );
method.setParameter( "gateway", "true" );
try
{
fClient.executeMethod(method);
if ( serviceUrl == null )
{
if ( extractLt( new String( method.getResponseBody() ) ) != null ) // if CAS does not return a login page with an LT authentication was successful
{
LOG.error( "Authentication for '" + username + "' unsuccessful" );
if ( LOG.isDebugEnabled() )
LOG.debug( "Authentication for '" + username + "' unsuccessful." );
} else
{
if ( LOG.isDebugEnabled() )
LOG.debug( "Authentication for '" + username + "' unsuccessful." );
}
} else
{
Header h = method.getResponseHeader( "Location" );
if ( h != null )
result = extractServiceTicket( h.getValue() );
if ( result == null )
LOG.error( "Authentication for '" + username + "' unsuccessful." );
}
} catch ( Exception x )
{
LOG.error( "Could not authenticate'" + username + "':" + x.toString () );
}
method.releaseConnection();
return result;
}
/**
* Helper method to extract the user name from a "service validate" call to CAS.
*
* @param data Response data.
* @return The clear text username, if it could be extracted, null otherwise.
*/
protected String extractUser( String data )
{
String user = null;
int start = data.indexOf( CAS_USER_BEGIN );
if ( start >= 0 )
{
start += CAS_USER_BEGIN.length();
int end = data.indexOf( CAS_USER_END );
if ( end > start )
user = data.substring( start, end );
else
LOG.warn( "Could not extract username from CAS validation response. Raw data is: '" + data + "'" );
} else
{
LOG.warn( "Could not extract username from CAS validation response. Raw data is: '" + data + "'" );
}
return user;
}
/**
* Helper method to extract the service ticket from a login call to CAS.
*
* @param data Response data.
* @return The service ticket, if it could be extracted, null otherwise.
*/
protected String extractServiceTicket( String data )
{
String serviceTicket = null;
int start = data.indexOf( TICKET_BEGIN );
if ( start > 0 )
{
start += TICKET_BEGIN.length();
serviceTicket = data.substring( start );
}
return serviceTicket;
}
/**
* Helper method to extract the LT from a login form from CAS.
*
* @param data Response data.
* @return The LT, if it could be extracted, null otherwise.
*/
protected String extractLt( String data )
{
String token = null;
int start = data.indexOf( LT_BEGIN );
if ( start < 0 )
{
LOG.error( "Could not obtain LT token from CAS: LT Token not found in response." );
} else
{
start += LT_BEGIN.length();
int end = data.indexOf( """, start );
token = data.substring( start, end );
}
return token;
}
/**
* This method requests the original login form from CAS.
* This form contains an LT, an initial token that must be
* presented to CAS upon sending it an authentication request
* with credentials.<br>
* If a service URL is provided (which is optional), this method
* will post the URL such that CAS authenticates against the
* specified service when a subsequent authentication request is
* sent.
*
* @param serviceUrl
* @return The LT token if it could be extracted from the CAS response.
*/
protected String getLt( String serviceUrl )
{
String lt = null;
HttpMethod method = null;
if ( serviceUrl == null )
method = new GetMethod( fCasUrl + LOGIN_URL_PART );
else
{
method = new PostMethod( fCasUrl + LOGIN_URL_PART );
( ( PostMethod ) method ).setParameter( "service", serviceUrl );
}
try
{
int statusCode = fClient.executeMethod(method);
if (statusCode != HttpStatus.SC_OK)
{
LOG.error( "Could not obtain LT token from CAS: " + method.getStatusLine() );
method.releaseConnection();
} else
{
Object o = method.getResponseHeaders() ;
return extractLt( new String( method.getResponseBody() ) );
}
} catch ( Exception x )
{
LOG.error( "Could not obtain LT token from CAS: " + x.toString () );
}
method.releaseConnection();
return lt;
}
}
System.getProperty("line.separator")
html
<meta http-equiv="pragma" content="no-cache">
<meta http-equiv="cache-control" content="no-cache">
<meta http-equiv="expires" content="0">
servlet
response.setHeader("pragma","no-cache");
response.setHeader("cache-control","no-cache");
response.setDateHeader("expires", 0);
response.addHeader("P3P","CP=CAO PSA OUR");
原文地址 http://lsong17.spaces.live.com/blog/cns!556C21919D77FB59!603.trak
用vim这么久
了,始终也不知道怎么在vim中使用系统粘贴板,通常要在网上复制一段代码都是先gedit打开文件,中键粘贴后关闭,然后再用vim打开编辑,真的不
爽;上次论坛上有人问到了怎么在vim中使用系统粘贴板,印象里回复很多,有好几页的回复却没有解决问题,今天实在受不了了又在网上找办法,竟意外地找到
了,贴出来分享一下。
如果只是想使用系统粘贴板的话直接在输入模式按Shift+Inset就可以了,下面讲一下vim的粘贴板的基础知识,有兴趣的可以看看,
应该会有所收获的。
vim帮助文档里与粘贴板有关的内容如下:
- vim有12个粘贴板,分别是0、1、2、...、9、a、“、+;用:reg命令可以查看各个粘贴板里的内容。在vim中简单用y只是复制到
“(双引号)粘贴板里,同样用p粘贴的也是这个粘贴板里的内容;
- 要将vim的内容复制到某个粘贴板,需要退出编辑模式,进入正常模式后,选择要复制的内容,然后按"Ny完成复制,其中N为粘
贴板号(注意是按一下双引号然后按粘贴板号最后按y),例如要把内容复制到粘贴板a,选中内容后按"ay就可以了,有两点需要说明一下:
- “号粘贴板(临时粘贴板)比较特殊,直接按y就复制到这个粘贴板中了,直接按p就粘贴这个粘贴板中的内容;
- +号粘贴板是系统粘贴板,用"+y将内容复制到该粘贴板后可以使用Ctrl+V将其粘贴到其他文档(如firefox、gedit)
中,同理,要把在其他地方用Ctrl+C或右键复制的内容复制到vim中,需要在正常模式下按"+p;
- 要将vim某个粘贴板里的内容粘贴进来,需要退出编辑模式,在正常模式按"Np,其中N为粘贴板号,如上所述,可以按"5p将
5号粘贴板里的内容粘贴进来,也可以按"+p将系统全局粘贴板里的内容粘贴进来。
注意:在我这里,只有vim.gtk或vim.gnome才能使用系统全局粘贴板,默认的
vim.basic看不到+号寄存器。
登录LINUX系统后,经常会看到"you have mail",却苦于不知道如何查看,相信菜鸟们都遇到过,偶在网上用“linux
mail"找了很久,但大都是介绍mail服务器的,黄天总算没负有心人,在洪恩在找到一篇介绍基础的文章,不敢独享。
系统提供了用户
之间通信的邮件系统,当用户打开终端注册登录时发现系统给出如下信息:
you have mail.
这时用户可通过键入mail命令读取信件:
$ mail
mail程序将逐个显示用户的信件,并依照时间顺序,显示最新的信件。每显示一段信件,mail都询问用户是否要对该信件作些处理。若用户回答d,则表示
删除信件;若仅按回车键,表示对信件不作任何改动(信件仍旧保存,下次还可读这一信件);若回答p,则要求重复显示信件;s
filename表示要把信件存入所命名的文件;若回答q,表示要从mail退出。
我们在本章的第一个例子中演示了如何写一封信,作为练习,你可送信件给自己,然后键入mail读取自己发的信件,看看会有什么效果。(发信给自己是一种设
置备忘录的方法)。
$mail frank 给自己写信
subject: test
This is a mail test
CRL-d
EOT
$
$mail 查看信件
“/var/spool/mail/frank:”1 message 1 new
>Nfrank@xteam.xteamlinux.comThu
Mar 25 11:00 13/403 “test”
&
Message 1:
From frank Thu Mar 25 11:00:25 1999/3/25
Received: (fromfrank@localhost)
by xteam.xteamlinux.com(8.8.4/8.8.4)
id LAA05170 for frank;Thu 25 Mar 1999 11:00:25 GMT
Date: Thu,25 Mar 1999 11:00:25 GMT
From:RHS Linux User <frank@xteam.xteamlinux.com>
Message-Id:<199903251142.LAA05170@xteam.xteamlinux.com>
To:frank@xteam.xteamlinux.com
Subject:test
Status:R
This is a mail test
&
mail命令还有很多其它用法,例如发送事先准备好的信件,或一次送信给若干人。还可以用其它方法送信件。
Mysql中limit的用法:在我们使用查询语句的时候,经常要返回前几条或者中间某几行数据,这个时候怎么办呢?
不用担心,mysql已经为我们提供了这样一个功能。
SELECT * FROM table LIMIT [offset,] rows | rows OFFSET offset
LIMIT 子句可以被用于强制 SELECT 语句返回指定的记录数。LIMIT 接受一个或两个数字参数。参数必须是一个整数常量。
如果给定两个参数,第一个参数指定第一个返回记录行的偏移量,第二个参数指定返回记录行的最大数目。初始记录行的偏移量是 0(而不是 1):
为了与 PostgreSQL 兼容,MySQL 也支持句法: LIMIT # OFFSET #。
mysql> SELECT * FROM table LIMIT 5,10; // 检索记录行 6-15
//为了检索从某一个偏移量到记录集的结束所有的记录行,可以指定第二个参数为 -1:
mysql> SELECT * FROM table LIMIT 95,-1; // 检索记录行 96-last.
//如果只给定一个参数,它表示返回最大的记录行数目:
mysql> SELECT * FROM table LIMIT 5; //检索前 5 个记录行
//换句话说,LIMIT n 等价于 LIMIT 0,n。
注意limit 10和limit 9,1的不同:
例如:
1.Select * From cyclopedia Where ID>=(
Select Max(ID) From (
Select ID From cyclopedia Order By ID limit 90001
) As tmp
) limit 100;
2.Select * From cyclopedia Where ID>=(
Select Max(ID) From (
Select ID From cyclopedia Order By ID limit 90000,1
) As tmp
) limit 100;
第1句是先取了前90001条记录,取其中最大一个ID值作为起始标识,然后利用它可以快速定位下100条记录
第2句择是仅仅取90000条记录后1条,然后取ID值作起始标识定位下100条记录
第1句执行结果.100 rows in set (0.23) sec
第2句执行结果.100 rows in set (0.19) sec
其实第2句完全可以简化成:
Select * From cyclopedia Where ID>=(
Select ID From cyclopedia limit 90000,1
)limit 100;
直接利用第90000条记录的ID,不用经过Max运算,这样做理论上效率因该高一些,但在实际使用中几乎看不到效果,
因为本身定位ID返回的就是1条记录,Max几乎不用运作就能得到结果,但这样写更清淅明朗,省去了画蛇那一足.
Select Top 100 * From cyclopedia Where ID>=(
Select Top 90001 Max(ID) From (
Select ID From cyclopedia Order By ID
) As tmp
)
但不管是实现方式是存贮过程还是直接代码中,瓶颈始终在于MS-SQL的TOP总是要返回前N个记录,这种情况在数据量不大时感受不深,
但如果成百上千万,效率肯定会低下的.相比之下MySQL的limit就有优势的多,执行:
Select ID From cyclopedia limit 90000
Select ID From cyclopedia limit 90000,1
的结果分别是:
90000 rows in set (0.36) sec
1 row in set (0.06) sec
而MS-SQL只能用Select Top 90000 ID From cyclopedia 执行时间是390ms,执行同样的操作时间也不及MySQL的360ms.
limit的offset(偏移量)用于记录较多的时候,记录较少时,偏移offset较小,直接使用limit较优。offset越大,后者越优。
1、offset比较小的时候。
select * from yanxue8_visit limit 10,10
多次运行,时间保持在0.0004-0.0005之间
Select * From yanxue8_visit Where vid >=(
Select vid From yanxue8_visit Order By vid limit 10,1
) limit 10
多次运行,时间保持在0.0005-0.0006之间,主要是0.0006
结论:偏移offset较小的时候,直接使用limit较优。这个显示是子查询的原因。
2、offset大的时候。
select * from yanxue8_visit limit 10000,10
多次运行,时间保持在0.0187左右
Select * From yanxue8_visit Where vid >=(
Select vid From yanxue8_visit Order By vid limit 10000,1
) limit 10
多次运行,时间保持在0.0061左右,只有前者的1/3。可以预先offset越大,后者越优。
mysql> SELECT * FROM table LIMIT 95,-1; // 检索记录行 96-last.
//如果只给定一个参数,它表示返回最大的记录行数目.
public enum OrderStatus {
A(1), B(2), C(3), D(4), F(5), INCOMPLETE(6);
private final int value;
/**
* Constructor.
*/
private OrderStatus(int value) {
this.value = value;
}
/**
* Get the value.
* @return the value
*/
public int getValue() {
return value;
}
}
<script language="javascript">
try
{
throw new
Error(10,"asdasdasd")
}
catch (e)
{
alert(e.message);
alert(e.description)
alert(e.number)
alert(e.name)
throw new
Error(10,"asdasdasd")
}
</script>
在JavaScript可以使用try...catch来进行异常处理。例如:
try {
foo.bar();
} catch (e) {
alert(e.name + ": " + e.message);
}
目前我们可能得到的系统异常主要包含以下6种:
- EvalError: raised when an error occurs
executing code in eval()
- RangeError: raised when a numeric
variable or parameter is outside of its valid range
- ReferenceError: raised when
de-referencing an invalid reference
- SyntaxError: raised when a syntax
error occurs while parsing code in eval()
- TypeError: raised when a variable or
parameter is not a valid type
- URIError: raised when encodeURI() or
decodeURI() are passed invalid parameters
上面的六种异常对象都继承自Error对象。他们都支持以下两种构造方法:
new Error();
new Error("异常信息");
手工抛出异常的方法如下:
try {
throw new Error("Whoops!");
} catch (e) {
alert(e.name + ": " + e.message);
}
如要判断异常信息的类型,可在catch中进行判断:
try {
foo.bar();
} catch (e) {
if (e instanceof EvalError) {
alert(e.name + ":" + e.message);
}
else if (e instanceof RangeError) {
alert(e.name + ": " + e.message);
}
// etc
}
Error具有下面一些主要属性:
- description: 错误描述 (仅IE可用).
- fileName: 出错的文件名 (仅Mozilla可用).
- lineNumber: 出错的行数 (仅Mozilla可用).
- message: 错误信息 (在IE下同description)
- name: 错误类型.
- number: 错误代码 (仅IE可用).
- stack: 像Java中的Stack Trace一样的错误堆栈信息 (仅Mozilla可用).
因此为了更好的了解错误信息我们可以将catch部分改为如下形式:
try {
foo.bar();
} catch (e) {
if (browserType != BROWSER_IE) {
alert("name: " + e.name +
"message: " + e.message +
"lineNumber: " + e.lineNumber +
"fileName: " + e.fileName +
"stack: " + e.stack);
}
else {
alert("name: " + e.name +
"errorNumber: " + (e.number & 0xFFFF ) +
"message: " + e.message");
}
}
JavaScript中的throw命令事实上可以抛出任何对象,并且我们可以在catch接受到此对象。例
如:
try {
throw new Date(); // 抛出当前时间对象
} catch (e) {
alert(e.toLocaleString()); // 使用本地格式显示当前时间
}
import java.io.*;
public class ObjectCloner
{
// so that nobody can accidentally create an ObjectCloner object
private ObjectCloner(){}
// returns a deep copy of an object
static public Object deepCopy(Object oldObj) throws Exception
{
ObjectOutputStream oos = null;
ObjectInputStream ois = null;
try
{
ByteArrayOutputStream bos = new ByteArrayOutputStream();
oos = new ObjectOutputStream(bos);
// serialize and pass the object
oos.writeObject(oldObj);
oos.flush();
ByteArrayInputStream bin = new ByteArrayInputStream(bos.toByteArray());
ois = new ObjectInputStream(bin);
// return the new object
return ois.readObject();
}
catch(Exception e)
{
System.out.println("Exception in ObjectCloner = " + e);
throw(e);
}
finally
{
oos.close();
ois.close();
}
}
}
select cast(1 as char)
char 不能换成varchar,否则会报错。
If u are on an unknown
server
and keen to know it’s
linux
distribution info, you can check the linux distribution info by just a
single command (eg. version, codename, etc). Just tested this command in
UBuntu and CentOS, both return as what i expected.
To check linux distribution and
version, follow the steps below:-
Run-->External Tools-->External tools configurations
new 一个 program
location 里面填 :C:\WINDOWS\explorer.exe
Arguments 里面填: ${container_loc}
点击 Run
ArrayUtils //简化数组的操作
LocaleUtils
SerializationUtils
StringEscapeUtils
StringUtils
SystemUtils
Validate //输入参数验证
NestableException
NestableRuntimeException
StopWatch //秒表类
xmind是绘制思维导图的工具。
使用之后,发现绘制组织结构图和wbs都很方便。
软件基于eclipse框架开发,反应速度和操作性也都很不错。
更重要的,图形的效果也是专业级的 :)
serialVersionUID作用:
序列化时为了保持版本的兼容性,即在版本升级时反序列化仍保持对象的唯一性。
有两种生成方式:
一个是默认的1L,比如:private static final long serialVersionUID = 1L;
一个是根据类名、接口名、成员方法及属性等来生成一个64位的哈希字段,比如:
private static final long serialVersionUID = xxxxL;
当你一个类实现了Serializable接口,如果没有定义serialVersionUID,Eclipse会提供这个
提示功能告诉你去定义 。在Eclipse中点击类中warning的图标一下,Eclipse就会
自动给定两种生成的方式。如果不想定义它,在Eclipse的设置中也
可以把它关掉的,设置如下:
Window ==> Preferences ==> Java ==> Compiler ==>
Error/Warnings ==>
Potential programming problems
将Serializable class without serialVersionUID的warning改成ignore即可。
如果你没有考虑到兼容性问题时,就把它关掉,不过有这个功能是好的,只要任何类别实现了Serializable这个接口的话,如果没有加入
serialVersionUID,Eclipse都会给你warning提示,这个serialVersionUID为了让该类别
Serializable向后兼容。
如果你的类Serialized存到硬盘上面后,可是后来你却更改了类别的field(增加或减少或改名),当你Deserialize时,就会出现
Exception的,这样就会造成不兼容性的问题。
但当serialVersionUID相同时,它就会将不一样的field以type的预设值Deserialize,可避开不兼容性问题。
ApplicationContext wac = WebApplicationContextUtils .getRequiredWebApplicationContext(config.getServletContext());
环境->虚拟主机->default_host->其它属性(主机别名)->修改端口
服务器->应用程序服务器->server1->端口->WC_defaulthost->修改端口
/* 追加自定义验证方法 */
// 身份证号码验证
jQuery.validator.addMethod("idcardno", function(value, element) {
return this.optional(element) || isIdCardNo(value);
}, "请正确输入身份证号码");
//字母数字
jQuery.validator.addMethod("alnum", function(value, element) {
return this.optional(element) || /^[a-zA-Z0-9]+$/.test(value);
}, "只能包括英文字母和数字");
// 手机号码验证
jQuery.validator.addMethod("cellphone", function(value, element) {
var length = value.length;
return this.optional(element) || (length == 11 && /^(1\d{10})$/.test(value));
}, "请正确填写手机号码");
// 电话号码验证
jQuery.validator.addMethod("telephone", function(value, element) {
var tel = /^(\d{3,4}-?)?\d{7,9}$/g;
return this.optional(element) || (tel.test(value));
}, "请正确填写电话号码");
// 邮政编码验证
jQuery.validator.addMethod("zipcode", function(value, element) {
var tel = /^[0-9]{6}$/;
return this.optional(element) || (tel.test(value));
}, "请正确填写邮政编码");
// 汉字
jQuery.validator.addMethod("chcharacter", function(value, element) {
var tel = /^[\u4e00-\u9fa5]+$/;
return this.optional(element) || (tel.test(value));
}, "请输入汉字");
/**
* 身份证号码验证
*
*/
function isIdCardNo(num) {
var factorArr = new Array(7,9,10,5,8,4,2,1,6,3,7,9,10,5,8,4,2,1);
var parityBit=new Array("1","0","X","9","8","7","6","5","4","3","2");
var varArray = new Array();
var intValue;
var lngProduct = 0;
var intCheckDigit;
var intStrLen = num.length;
var idNumber = num;
// initialize
if ((intStrLen != 15) && (intStrLen != 18)) {
return false;
}
// check and set value
for(i=0;i<intStrLen;i++) {
varArray[i] = idNumber.charAt(i);
if ((varArray[i] < '0' || varArray[i] > '9') && (i != 17)) {
return false;
} else if (i < 17) {
varArray[i] = varArray[i] * factorArr[i];
}
}
if (intStrLen == 18) {
//check date
var date8 = idNumber.substring(6,14);
if (isDate8(date8) == false) {
return false;
}
// calculate the sum of the products
for(i=0;i<17;i++) {
lngProduct = lngProduct + varArray[i];
}
// calculate the check digit
intCheckDigit = parityBit[lngProduct % 11];
// check last digit
if (varArray[17] != intCheckDigit) {
return false;
}
}
else{ //length is 15
//check date
var date6 = idNumber.substring(6,12);
if (isDate6(date6) == false) {
return false;
}
}
return true;
}
/**
* 判断是否为“YYYYMM”式的时期
*
*/
function isDate6(sDate) {
if(!/^[0-9]{6}$/.test(sDate)) {
return false;
}
var year, month, day;
year = sDate.substring(0, 4);
month = sDate.substring(4, 6);
if (year < 1700 || year > 2500) return false
if (month < 1 || month > 12) return false
return true
}
/**
* 判断是否为“YYYYMMDD”式的时期
*
*/
function isDate8(sDate) {
if(!/^[0-9]{8}$/.test(sDate)) {
return false;
}
var year, month, day;
year = sDate.substring(0, 4);
month = sDate.substring(4, 6);
day = sDate.substring(6, 8);
var iaMonthDays = [31,28,31,30,31,30,31,31,30,31,30,31]
if (year < 1700 || year > 2500) return false
if (((year % 4 == 0) && (year % 100 != 0)) || (year % 400 == 0)) iaMonthDays[1]=29;
if (month < 1 || month > 12) return false
if (day < 1 || day > iaMonthDays[month - 1]) return false
return true
}
为了在windows和linux平台公用相同的邮件客户端和邮件内容,主要是有时候切换操作系统又要看以前的邮件。最后找到了
thunderbird(下面简称TB)客户端。这个客户端就是有点慢,倒是能满足我的要求。但是它的默认设置有时候有点不符合我们的使用习惯,我对它的
设置作了下面的一些修改:
1:转发邮件
默认的设置转发把邮件的内容作为附件转发。这样有两个不好的地方:第一,如果邮件有附件,这个附件不能转发;第二,接收方必须要用TB客户端了,否则打不
开。
修改:编辑 -> 首选项 -> 编写 -> 常规:转发消息改成内联
2:其它邮件客户端接收TB发的中文附件是乱码
这个是标准问题,TB使用的是新的标准,但是别的客户端使用的是旧的标准(具体那个标准忘了,google一下就可以了)。这样就会出现乱码了。
修改:编辑 -> 首选项 -> 高级 ->
配置编辑器:mail.strictly_mime.parm_folding 改成0或者1
3:自动打开附件
TB默认的是在打开邮件的时候同时自动打开邮件的附件。这样的话,如果附件大就很头痛。
修改:编辑 -> 首选项
-> 高级 -> 配置编辑器:
mail.inline_attachments 改成faulse
mail.content_disposition.type 改成1
4:回复邮件时回复的邮件内容在下面
TB默认的回复邮件的回复内容是在下面的,这样如果邮件来回几次,回复比较多,看起来很不方便。
修改:编辑
-> 首选项 -> 高级 -> 配置编辑器:Mail.identify.default.reply_on_top值由0改为1
还有一个问题没有解决,就是有时候在TB中打开一个文件夹,它会重新建索引还是什么的,这时候打开一个文件夹比较慢。看网上有说把这个文件夹重命名,再创
建一个同名的文件夹,最后把老的文件夹的内容拷贝到新的里面就好了,这个没有试过。不过这个也不是特别大的问题,就没有继续搞了,什么时候有空再看看,到
时候再贴上来。
格式: tar 选项 文件目录列表
功能: 对文件目录进行打包备份
选项:
-c 建立新的归档文件
-r 向归档文件末尾追加文件
-x 从归档文件中解出文件
-O 将文件解开到标准输出
-v 处理过程中输出相关信息
-f 对普通文件操作
-z 调用gzip来压缩归档文件,与-x联用时调用gzip完成解压缩
-Z 调用compress来压缩归档文件,与-x联用时调用compress完成解压缩
例如:
1.将当前目录下所有.txt文件打包并压缩归档到文件this.tar.gz,我们可以使用
tar czvf this.tar.gz ./*.txt
2.将当前目录下的this.tar.gz中的文件解压到当前目录我们可以使用
tar xzvf this.tar.gz ./
Application Servers > server1 > Process Definition > Java
Virtual Machine > Custom Properties
虚拟机参数在命令行的形式为 -Dproperty=value,在程序中可以用System.getProperty("property")取值。
利用这个特性可以对程序运行进行控制,避免代码的修改。
地址栏输入about:support,在打开的页面有打开配置文件夹的按钮;
扩展在Extensions文件夹下,插件在安装文件夹下的plugin和其他目录。
import java.net.URL;
import org.codehaus.xfire.client.Client;
public class XfireClient
{
public static void main(String[] args)
{
DyClient();
}
/**
* You get a DynamicClient when you create a Client with the URL of a WSDL
*/
public static void DyClient()
{
try
{
Client client = new Client(
new URL(
"http://www.webxml.com.cn/webservices/qqOnlineWebService.asmx?wsdl"));
Object[] results = client.invoke("qqCheckOnline",
new Object[] { "31506173" });
System.out.println((String) results[0]);
}
catch ( Exception e)
{
e.printStackTrace();
}
}
}
必须jar:
commons-codec-1.3.jar
commons-httpclient-3.0.jar
commons-logging-1.0.4.jar
jdom-1.0.jar
wsdl4j-1.6.1.jar
xfire-all-1.2.6.jar
XmlSchema-1.1.jar
import java.io.UnsupportedEncodingException;
public class UTF {
public static void main(String[] args) {
String s = "非常好";
try {
byte[] b = s.getBytes("UTF-8");
for(int i=0; i< b.length; i++){
System.out.println(Integer.toHexString(b[i]).substring(6));
}
System.out.println(new String(b, "UTF-8"));
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
}
}
输出:
e9
9d
9e
e5
b8
b8
e5
a5
bd
非常好
是否需要在State类或Strategy类中访问Context.
状态模式通常需要调用Context中的方法,以改变Context的状态。
Strategy通常不需要。
记号 |
含义 |
举例 |
匹配 |
. |
任何字符 |
a.. |
a后两个字符 |
^ |
行首 |
^wood |
位于行首的wood |
$ |
行尾 |
x$
^INSERT$
^$ |
位于行尾的x
只包含字符串INSERT的行
不包含任何字符的行 |
* |
前导的正则表达式重复0或若干次 |
x*
xx*
.*
w.*s |
0或若干次连续的x
1或多个连续的x
0或若干个字符
以w开始,s结尾的任何字符串 |
[字符表] |
字符表中的任一 |
[tT]
[a-z]
[a-zA-Z] |
小写或大写的t
小写字母
字母(大写或小写) |
[^字符表] |
任一不在字符表中的字符 |
[^0-9]
[^a-zA-Z] |
任何数字
非字母 |
\{min,max\} |
前导的正则表达式重复至少min次,最多max次 |
X\{1,5\}
[0-9]\{3,9\}
[0-9]\{3\}
[0-9]\{3,\} |
最少1个,最多5个x
3到9个数字
正好3个数字
至少3个数字 |
\(…\) |
将小括号中匹配的字符串存储到下一个寄存器中(1-9) |
^\(.\)
^\(.\)\1 |
行中第1个字符存到1号寄存器
行首两个字符,且它们相同 |
如下命令含有正则表达式:
cut
paste
sed
tr
grep
sort
uniq
spring
svn checkout https://src.springframework.org/svn/spring-framework/trunk spring-framework
ant resolve
struts2
svn checkout http://svn.apache.org/repos/asf/struts/struts2/trunk struts2
svn checkout http://svn.apache.org/repos/asf/struts/xwork/trunk/ xwork
mvn install
mvn eclipse:eclipse
tomcat6
svn checkout http://svn.apache.org/repos/asf/tomcat/tc6.0.x/trunk tc6.0.x
1.下载ant 1.6.x
2. 命令行下执行 ant download,下载依赖jar.
在JavaEye论坛上回答网友joyjiang的疑问:“REST的优势到底是什么?开发效率?文档的管理?url的直观?还是其它的什么优势呢?”
REST的主要优势在我看来其实在于它是一种对于服务器的更加有效的抽象方式。
对于基于网络的应用来说,你怎么样看待服务器,就会产生什么样的架构风格,随之产生与该架构风格相关的交互模式。
RPC架构风格将服务器看作是由一些过程组成,客户端调用这些过程来执行特定的任务。SOAP就是RPC风格的一种架构。过程是动词性的(做某件事),因此RPC建模是以动词为中心的。
分布式对象架构风格认
为服务器是由一些对象和对象上的方法组成,客户端通过调用这些对象上的方法来执行特定的任务。并且客户端调用这些对象上的方法应该就像是调用本地对象上的
方法一样,这样开发就可以完全按照统一的面向对象方法来做。但是很可惜,这样的抽象并不是很有效,因为分布式对象与本地对象存在着巨大的本质差别,想要掩
盖这些差别很多时候甚至是有害无益的。
REST架构风格并
没有试图掩盖这些差别,而是将服务器抽象为一组离散资源的集合。资源是一个抽象的概念,而不是代表某个具体的东西。注意:要真正理解REST,就一定要增
强自己的抽象思维能力,充分理解到资源是抽象的。如果完全不具有抽象思维的能力,一定要将资源与数据库中的一张表或服务器端的一个文件(HTML、
Servlet、JSP、etc.)一一挂起钩来,就无法真正理解REST了。资源是名词性的,因此REST建模是以名词为中心的。
上述
是目前基于网络的应用的主要的三种抽象方式。这三种不同的抽象方式会严重影响客户端与服务器的交互模式,而不同交互模式的交互效率差别相当大。分布式对象
的交互模式很多时候效率很低,因为掩盖了分布式对象与本地对象的差别,很多时候都会导致细粒度的API(需要一再强调才能让一些不明就里的架构初哥按照正
确的方式来做设计)。实践已经证明,与RPC和分布式对象相比,REST是一种对于服务器更加有效的抽象方式,将会带来粒度更大和更有效率的交互模式。这
样的效果与Fielding设计REST的初衷是吻合的,REST就是专门为交互的性能和可伸缩性进行过优化的一种架构风格。而SOAP在设计的时候优先
考虑的从来不是性能和可伸缩性,而是互操作性。除非出现奇迹,否则你种什么,就应该长出来什么。你种的是瓜,长出来的就是瓜;你种的是豆,长出来的就是
豆。
Fielding写到:“
REST提供了一组架构约束,当作为一个整体来应用时,强调组件交互的可伸缩性、接口的通用性、组件的独立部署、以及用来减少交互延迟、增强安全性、封装遗留系统的中间组件。”
有
人认为REST不是面向对象的,其实REST虽然没有分布式对象那么面向对象,在我看来至少比RPC更加面向对象。按照《企业应用架构模式》,以动词为中
心建模是什么?是不是就是事务脚本?以名词为中心建模是什么?是不是就是领域模型?这就扯远了,网络通信是否一定需要实现为面向对象的形式,我认为是不需
要的。
“REST的主要优势在我看来其实在于它是一种对于服务器的更加有效的抽象方式。”
这句话等于是,我先把一个骨架放在这里,还没有用血肉来充实它,也就是还没有举出具体的实例来。具体的实例以后我们还需要来详细讨论。REST是非常简练的,同时又是一种非常强大的抽象方式,在我看来就是从根本上简化Web开发的一味良药。
select index_name from dba_indexes where table_name
in (select table_name from user_tables)
1.位图索引用于数据仓库,不能用于普通系统
2.使用组合索引. 当大量字段同同时作为过滤条件时,使用组合索引会大大提高性能。
建立组合索引时,注意小基数字段在前,大基数字段在后。
3.同一字段出现在不同表要保持类型一致(确有需要,可使用函数索引)
4.使用count(*)
5.使用返回单个结果的查询改写外连接能取得较好的性能
Oracle优化器会自动选择以下三种方式的一种运行表连接,但在数据环境上配合强化选择合适的方式或强制使用某种方式是SQL优化的需要:
NESTED LOOP
对于被连接的数据子集较小的情况,nested loop连接是个较好的选择。nested loop就是扫描一个表,每读到一条记录,就根据索引去另一个表里面查找,没有索引一般就不会是 nested loops。
一般在nested loop中, 驱动表满足条件结果集不大,被驱动表的连接字段要有索引,这样就走nested loop。如果驱动表返回记录太多,就不适合nested loops了。如果连接字段没有索引,则适合走hash join,因为不需要索引。
可用ordered提示来改变优化器默认的驱动表,可用USE_NL(table_name1 table_name2)提示来强制使用nested loop。
HASH JOIN
hash join是优化器做大数据集连接时的常用方式。优化器扫描小表(或数据源),利用连接键(也就是根据连接字段计算hash 值)在内存中建立hash表,然后扫描大表,每读到一条记录就来探测hash表一次,找出与hash表匹配的行。
当
小表可以全部放入内存中,其成本接近全表扫描两个表的成本之和。如果表很大不能完全放入内存,这时优化器会将它分割成若干不同的分区,不能放入内存的部分
就把该分区写入磁盘的临时段,此时要有较大的临时段从而尽量提高I/O 的性能。临时段中的分区都需要换进内存做hash
join。这时候成本接近于全表扫描小表+分区数*全表扫描大表的代价和。
至于两个表都进行分区,其好处是可以使用parallel query,就是多个进程同时对不同的分区进行join,然后再合并。但是复杂。
使用hash join时,HASH_AREA_SIZE初始化参数必须足够的大,如果是9i,Oracle建议使用SQL工作区自动管理,设置WORKAREA_SIZE_POLICY 为AUTO,然后调整PGA_AGGREGATE_TARGET即可。
以下条件下hash join可能有优势:
两个巨大的表之间的连接。
在一个巨大的表和一个小表之间的连接。
可用ordered提示来改变优化默认的驱动表,可用USE_HASH(table_name1 table_name2)提示来强制使用hash join。
SORT MERGE JOIN
sort merge join的操作通常分三步:对连接的每个表做table access full;对table access full的结果进行排序;进行merge join对排序结果进行合并。sort merge join性能开销几乎都在前两步。一般是在没有索引的情况下,9i开始已经很少出现了,因为其排序成本高,大多为hash join替代了。
通常情况下hash join的效果都比sort merge join要好,然而如果行源已经被排过序,在执行sort merge join时不需要再排序了,这时sort merge join的性能会优于hash join。
在全表扫描比索引范围扫描再通过rowid进行表访问更可取的情况下,sort merge join会比nested loops性能更佳。
可用USE_MERGE(table_name1 table_name2)提示强制使用sort merge join。
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
public class ProcessTest {
public static void main(String[] args) {
ProcessBuilder pb = new ProcessBuilder ( "tasklist");
try {
Process process = pb.start();
InputStream fis = process.getInputStream();
BufferedReader br = new BufferedReader(new InputStreamReader(fis));
String line = null;
StringBuffer cmdout = new StringBuffer();
while ((line = br.readLine()) != null) {
cmdout.append(line).append("\n");
}
System.out.println(cmdout.toString().trim());
} catch (IOException e) {
e.printStackTrace();
}
}
}
输出如下:
图像名 PID 会话名 会话# 内存使用
========================= ====== ================ ======== ============
System Idle Process 0 Console 0 28 K
System 4 Console 0 324 K
smss.exe 1076 Console 0 812 K
csrss.exe 1152 Console 0 3,296 K
winlogon.exe 1176 Console 0 31,580 K
services.exe 1220 Console 0 4,684 K
lsass.exe 1232 Console 0 1,672 K
svchost.exe 1408 Console 0 6,236 K
svchost.exe 1496 Console 0 5,036 K
svchost.exe 1656 Console 0 38,656 K
spoolsv.exe 1872 Console 0 8,000 K
explorer.exe 332 Console 0 18,888 K
avp.exe 376 Console 0 24,960 K
db2dasrrm.exe 460 Console 0 34,652 K
TSVNCache.exe 672 Console 0 12,476 K
igfxtray.exe 1380 Console 0 7,344 K
hkcmd.exe 1236 Console 0 4,056 K
igfxpers.exe 1428 Console 0 3,468 K
db2mgmtsvc.exe 1444 Console 0 10,072 K
RTHDCPL.exe 1460 Console 0 32,480 K
igfxsrvc.exe 1572 Console 0 3,772 K
avp.exe 1680 Console 0 6,008 K
db2systray.exe 1700 Console 0 32,512 K
ctfmon.exe 1780 Console 0 4,892 K
picpick.exe 1984 Console 0 3,600 K
QQ.exe 2024 Console 0 34,648 K
dsNcService.exe 264 Console 0 3,880 K
365日历.EXE 1952 Console 0 43,788 K
CLCL.exe 1028 Console 0 8,252 K
klnagent.exe 1052 Console 0 3,196 K
thunderbird.exe 352 Console 0 38,692 K
rtxc.exe 472 Console 0 29,968 K
db2rcmd.exe 1836 Console 0 11,832 K
TXPlatform.exe 2488 Console 0 3,808 K
firefox.exe 2724 Console 0 195,912 K
cmd.exe 2716 Console 0 52 K
sh.exe 3936 Console 0 152 K
conime.exe 2752 Console 0 3,424 K
eclipse.exe 3060 Console 0 2,592 K
JAVAW.EXE 2984 Console 0 446,692 K
EXCEL.EXE 3232 Console 0 1,936 K
wmiprvse.exe 4084 Console 0 6,368 K
JAVAW.EXE 320 Console 0 6,860 K
tasklist.exe 2936 Console 0 4,812 K
当运行其他命令 ,如dir等时,用如下写法:
ProcessBuilder pb = new ProcessBuilder ( "cmd", "/c", "dir");
Ubuntu下上网解析DNS慢有很大程度上是和IPV6有关,而目前国内大部分地方都还没有IPV6网络,所以一般用户应该需要关闭IPV6
网上流传着很多IPV6的关闭方法,但是经过测试大部分都是针对老版本的,而且效果不好。
这里提供一种方法作为参考
/proc/sys/net/ipv6/conf/lo/disable_ipv6
这个档案,用cat指令可以看到 0 这个数字,将他设定为1就可以了。
因为已经将ipv6编入kernel,因此在proc里面就可以看得到相关的设定。
设定的方式有很多种,有的是用echo 1 >> [路径]/档桉名称
的方式,这种方式是每次开机以后就得要打一次。
所以延伸出第二个方法,在rcS.d里面设定一个连结,去执行这个指令的script。
第3个方法,就是设定sysctl.conf档桉,也是最正统的做法。
位置在
/etc/sysctl.conf
这个档桉可以设定很多,包括要当成NAT时的封包转发等等。
要设定
/proc/sys/net/ipv6/conf/lo/disable_ipv6
这个档桉,就是在sysctl.conf里面加上
net.ipv6.conf.lo.disable_ipv6 = 1
In git, you can’t update a tag directly, but you can branch the code to create a new tag. Here’s how you would do that:
First, you need to checkout the tag:
git checkout <tag_name>
Then create a branch:
git branch -b <branch_name>
After you make your changes, commit them (there are a few ways to do this, keeping it simple):
git commit -am 'my descriptive comment on this commit'
You can create a new tag:
git tag <new_tag_name>
Then you can push the tag:
git push --tags
I just learned that if you have some changes in your working tree that you want to get rid of, you don't type 'git revert' like you might guess. No, that's what cvs, subversion, mercurial, and bazaar (to name a few) use revert to mean, but not git. With git, revert is used to undo actual commits. Thankfully, you can undo your revert with another 'git revert', I just learned. So let me repeat to be clear, if you have changes to your working files that you want to abandon, DO NOT do this:
git revert HEAD
That will undo your last commit. Do this instead:
git reset --hard HEAD
I'm glad I have that straightened out now. I'm wondering if /etc was really a good place for me to start out playing with git.
UPDATE: Nearly two years later and I'm still getting comments on this. I'm glad I've been able to help people out this way. The discussion in the comments is good, and one thing I'd like to point out is that I now always use and recommend:
git checkout filename
(as recommended by Anonymous and others below) instead of git reset. I think the git stash trick from Nicolas is pretty cool too.
内核版本指的是在Linus领导下的开发小组开发出的系统内核的版本号。第一数字叫主版本号,第二个叫次版本号,第三个叫修订版本号。一般说来次版本号还
有特定的意义,以序号的第二位为偶数的版本表明这是一个可以使用的稳定版本,如2.0.35,而序号的第二位为奇数的版本一般有一些新的东西加入,是不一
定很稳定的测试版本,如2.1.88。这样稳定版本来源于上一个测试版升级版本号,而一个稳定版本发展到完全成熟后就不再发展。
总结一下:
第一个数字表示大版本,相当于大升级了.
第二个数字有两个含义:大版本的小版本;偶数表示生产版(奇数表示测试版);
第三个数字表示指定小版本的补丁包;如2.6.10 Linux内核的推出,表明一年中已经发布了10个版本.
不过,2.6.x系列以前版本没有带有次小数点的版本号,即没有四个数字组成的版本号,而在2.6.x系列中,从2.6.8.1内核开始,一直持续到2.6.11,较小的内核隐患和安全补丁被赋予了次小数点版本号(例如2.6.11.1)。
但
值得注意的是.readhat
linux内核的版本稍有不同如2.4.20-10,可以发现多了一组数字(10),该数字是建立(build)号。每个建立可以增加少量新的驱动程序或
缺陷修复。一些readhat 内核还带“pp”,就是“pre-patch”的意思,是个内核的测试版本。
PV(pageview),即页面浏览量,或点击量;通常是衡量一个网络新闻频道或网站甚至一条网络新闻液没涤的主要指标;当然,有时还会同时考察另外一个指标,即uv(unique
visitor),指访问某个站点或点击某条新闻的不同IP地址的人数。
PV之于网站,就像收视率之于电视,从某种程度上已成为投资者衡量商业网站表现的最重要尺度。从长远看,很多网站也意识到,PV的追求需要和品牌的打造结合起来;但现代商业行为在投资者急功近利的评判压力下,往往无奈为了使PV提升而不择手段。
一条新闻发布以后,其PV便可以加以跟踪,通常是每5分钟统计一次。不同网站的不
同频道,对其所发布新闻的PV表现有一个大致的评判尺度。新闻发布后,一般PV值总有一个上升的过程。可以从不同时段来计算PV的单位时间变化幅度,有经
验的网络编辑,经过几个5分钟的数据积累,便能大致预料到这条新闻的PV峰值水平。如果这个水平不能令人满意,则编辑就要采取一些手段,如“优化”标题、
增加图片、挪动位置等。一般来说,通过这样的“处理”,一条新闻的PV表现能有所改善,达到新的高峰。也就是说,网络新闻的编辑手段影响着PV值。
还有哪些因素对PV有影响呢?至少还有这些因素:新闻发布的时间。不同的时间段,上网的人数不同,访问该站点的人数也不同,因此,有时PV的涨落,其主要贡献,在于不同时段上网人数的自然波动。同样一条新闻,在不同的时段发布,PV就会有差别。
不同时段上网的人,其人口特征(性别、年龄、教育程度、阅读旨趣等)不同,所以,同样是一万个上网的人,甚至同样是对某个网站的一万次访问,不同时段,这一万次访问在不同频道/内容上的分布是有差别的。所以PV的变化,与这些因素导致的变化有关。
访问的周期。对于一些常浏览的网站,我们可能一天之中会访问几次,这中间有一定的
时间间隔。这个间隔,很多时候和人们的现实工作节奏有关系。比如,不少人一上班会抽空浏览一下新闻,第二次再来看看又有什么新闻的时候,往往是上午中间休
息时,甚至是午饭后的休息时间。因此,即使其他因素不变,由于人们回访网站的周期性,也会对新闻或网站的PV带来影响。当然,由于不同的人回访的周期长短
不一、时段不一,这个影响因素未必会导致明显的波动,而可能分散在不同时段的PV表现中,但可以肯定的是,任何一个PV数据,也有这种回访周期的因素所起
的作用。
搭便车因素。比如一些突发事件,会导致人们对某一网站的访问增加,但这些访问的
初衷,本只是突发事件相关新闻。然而由于人们的新闻消费,往往具有不可预期性,所以常见的现象是,人们在看完想看的新闻后,还会顺带看看其他的。这一因
素,也可能对某条新闻(与突发事件无关)的PV有所贡献。
最后,偶然因素也对PV有一定影响,比如天气因素,比如非典期间等等。
由此看来,一个简单的PV数据,其实是多种因素综合贡献的结果,所以有时的PV涨
落,实在不是完全可以通过编辑手段来加以引导和影响的。知道这一点很重要,因为这告诉我们,盲目地、不加具体分析地以PV来衡量成败好坏,是不合理的。在
社会科学研究中,这种区分不同因素对某一个现象的贡献,就是所谓的详析模式。很多看似不变的东西,其实内部构成比例上发生了很大的变化。而有些看似变化的
东西,其相对关系其实没有什么变化,只是一种单纯的数量上的涨落。
这使我想到那本《统计陷阱》,一本由一个记者所写的通俗读物。这本书的英文名称直译,叫“如何用统计数据说谎”。
Sun微系统的JavaHelp系统是一种应用程序帮助技术,它可以在所有支持Java的平台和浏览器中运行只要它是一个纯粹的Java产品。通过
applet和Java应用程序,在其他环境下,比如在互联网或企业内部互联网中,它是理想的使用对象。在本文中,我将回顾JavaHelp的主要作用,
并阐明它的简单使用方法。
优点
JavaHelp通过使用Java基础类(JFC) 软件组件来实现,这使得开发户自定义界面和功能变得灵活轻松。您可以轻而易举地将应用程序的帮助信息嵌入到应用程序之中,或者嵌入到另一个独立的窗口里。JFC 还具有通过自定义导航控件拓展帮助功能的能力。
JavaHelp使动态更新帮助数据或将其存储在一个便利的位置变得简单易行,不管是在客户端还是服务器端。内容窗口使用HTML 3.2格式显示话题,使内容设计变得简单。标准导航提供内容表单(TOC)、索引和全文搜索。(TOC和索引都遵循W3C标准。)
JavaHelp Data
Sheet提供以下信息:TOC提供层叠式和扩展式话题展示,无限量等级层次和多重TOC的合并。TOC与内容阅读器同步显示,也就是说显示的话题在
TOC中被突出。索引支持多重索引的合并。全文搜索被设计得很灵活,并且可以配置。搜索引擎也可置于用户或服务器中的任一方。
帮助内容和搜索引擎被压缩成一个使用标准JAR格式的单个文件。尽管为了使存储和访问更具灵活性,推荐将所有文件压缩成一个单独的文件,但是您仍然可以将
所有文件以扩展的格式保存在目录中。您可以在自定义编写的导航器、搜索引擎、标准JavaHelp阅读器或在任何HTML显示中,包括标准Web浏览器
中,查看和导航内容。
JavaHelp API提供一个上下文敏感帮助和ID映射的机制。您可以将多重组件的帮助信息用JavaHelp API合并,这样用户就看到一个单独的完整文档。合并机制可以应用于TOC、索引和搜索数据库。
特性
JavaHelp有两个主要版本:1.1.3和2.0版。最早支持JavaHelp 2.0的Java版本是J2SE
1.2.2,因此您无需担心自己的代码是否支持JavaHelp。JavaHelp系统由一个特性完整的、可扩展的规范、API以及一个参考执行组成。
标准JavaHelp系统的主窗口:有三个显示窗口,在您退出窗口时并未被关闭,依然可配置。在默认情况下,一个主要窗口有以下三个窗格:工具栏、导航窗口和内容窗口。见图表1。
用户可以应用多种方式通过内部应用程序调用在线帮助,例如,当一个用户从帮助菜单中选择一项,或在应用程序GUI中点击帮助按钮,JavaHelp系统提
供一个简便的界面,通过这个界面,应用程序要求话题的ID 得以显示。然后JavaHelp系统将话题ID与相应的URL相连接并将其显示出来。
ID被映射到一个被称作映射文件的JavaHelp系统元数据文件中的URL上。例如,当为文件选择对话框编码时,开发人员要求话题ID
fc_help在当对话框底部的帮助按钮被点击时显示。在映射文件中,ID fc_help被定义为名为
FileChooser.html的文件,使用以下XML句法:
将文件名(或URL)的规范从程序代码分离为内容的作者提供了控制与话题ID相关的信息的自由。
工具提示是一个显示给用户的简短信息,当光标停留在一个按钮上的时间间隔长于阈值时它就会出现。您还可以将工具提示信息纳入JavaHelp系统数据。
JavaHelp系统数据具有调用在线帮助的能力,能够在应用程序GUI中描述图形组件。用户操纵激活语境敏感帮助,然后将组件以问题形式特定化。与组件相联系的ID被显示。您可以从TOC、索引导航或主要窗口的内容窗格中显示帮助话题。
轻型组件
轻型组件能够向帮助话题添加功能。这些组件与Java的applet相似,但它们的加载和执行更为迅速。一个帮助的创作者能够使用已经在JavaHelp
中执行的轻型组件;这个组件执行弹出窗口和次级窗口。在HTML
话题文件中使用轻型组件,您可以使用HTML<object>标记,正如列表A中的例子所显示的。
在例子中,弹出的对象包括文件../topicB/glossary_def.html,用户点击的对象(viewerActivator)是个链接,窗
口类型属于弹出型,窗口大小为300x400,用户在链接中看到的内容是“Click here(点击这里)”。
Java开发商还可以创造新的轻型组件。例如,这样的组件可能为帮助话题添加动画和多媒体功能。
要求抽象查看的轻型组件必须执行javax.javahelp.impl.ViewAwareComponen。这些组件执行setViewData()
方法。这个组件能够根据抽象查看中的信息决定它所执行的环境。(注意:要访问抽象查看,您应该使用JavaHelp
API。)在列表B中,文档对象下面的编码片断来自于抽象查看。
需要更多关于创造Java轻型组件的信息,请参阅相关文件。
JavaHelp编写工具
有很多通过商业途径可以得到的编写工具能够为您的应用程序编写和维护一个完美的帮助系统。最有名的三个是Software7
Gmbh的Helen
1.5、Solutionsoft的HelpBreeze和Pivotonic的JawaHelpAuthor。所有这些第三方产品都支持Java 2
和Java 1.1标准。需要更多关于编写工具的信息,请参阅Sun的帮助编写工具页面。
试试看吧
如果JavaHelp听起来像是一个您认为您的用户和伙伴开发商都喜欢的系统的话,那么从Sun Microsystem公司的网站上立刻下载吧。这个地址提供了安装过程所需要的所有信息。
本文源地址: http://easwy.com/blog/archives/enable-ip-forward-on-linux/
Linux系统缺省并没有打开IP转发功能,要确认IP转发功能的状态,可以查看/proc文件系统,使用下面命令:
cat /proc/sys/net/ipv4/ip_forward
如果上述文件中的值为0,说明禁止进行IP转发;如果是1,则说明IP转发功能已经打开。
要想打开IP转发功能,可以直接修改上述文件:
echo 1 > /proc/sys/net/ipv4/ip_forward
把文件的内容由0修改为1。禁用IP转发则把1改为0。
上面的命令并没有保存对IP转发配置的更改,下次系统启动时仍会使用原来的值,要想永久修改IP转发,需要修改/etc/sysctl.conf文件,修改下面一行的值:
net.ipv4.ip_forward = 1
修改后可以重启系统来使修改生效,也可以执行下面的命令来使修改生效:
sysctl -p /etc/sysctl.conf
进行了上面的配置后,IP转发功能就永久使能了
Linux下的库文件分为共享库和静态库两大类,它们两者的差别仅在程序执行时所需的代码是在运行时动态加载的,
还是在编译时静态加载的。区分库类型最好的方法是看它们的文件后缀,通常共享库以.so(Shared Object的缩写)结尾,
静态链接库通常以.a结尾(Archive的缩写)。在终端缺省情况下,共享库通常为绿色,而静态库为黑色。
库操作命令
Linux库操作可以使用命令完成,目前常用的命令是ldd和ldconfig。
1.ldd
ldd是Library Dependency Display缩写,它的作用是显示一个可执行程序必须使用的共享库。
$ ldd /usr/bin/mesg
libc.so.6 => /lib/tls/i686/cmov/libc.so.6 (0xb7eaf000)
/lib/ld-linux.so.2 => /lib/ld-linux.so.2 (0xb7feb000)
2.ldconfig
库安装到系统以后,为了让动态链接库为系统所认识及共享,就需要运行ldconfig。ldconfig命令的用途,
主要是在默认搜寻目录(/lib和/usr/lib)以及动态库配置文件/etc/ld.so.conf内所列的目录下,搜索出可共享的动态链接库(格式如lib*.so*),
进而创建出动态装入程序(ld.so)所需的连接和缓存文件。缓存文件默认为/etc/ld.so.cache,此文件保存已排好序的动态链接库名字列表,
ldconfig通常在系统启动时运行,而当用户安装了一个新的动态链接库时,就需要手工运行这个命令。
(1)命令格式
ldconfig [选项] [libs]
(2)主要选项
-v或–verbose ldconfig将显示正在扫描的目录、搜索到的动态链接库,以及它所创建的连接的名字。
-f CONF 指定动态链接库的配置文件为CONF,系统默认为/etc/ld.so.conf。
-C CACHE 指定生成的缓存文件为CACHE,系统默认的是/etc/ld.so.cache,文件存放已排好序的可共享的动态链接库的列表。
-p或–print-cache 让ldconfig打印出当前缓存文件所保存的所有共享库的名字。
-r ROOT 改变应用程序的根目录为ROOT。
-n ldconfig仅扫描命令行指定的目录,不扫描默认目录(/lib、/usr/lib),也不扫描配置文件/etc/ld.so.conf所列的目录。
运行没有选项的ldconfig命令时,用于更新高速缓冲文件。这个命令主要用于高速缓冲DNS服务器(Caching DNS Server)。
高速缓冲DNS服务器的原理是提供查询的历史记录,并且利用这些记录来提高查询的效率。
当某个查询是第一次被发送到高速缓冲DNS服务器时,高速缓冲DNS服务器就将此查询的整个过程记录下来,
在一定的时期内用它来回答所有相同的查询,从而减少整个DNS系统的负担并且提高查询速度。
库的升级
Linux系统软件更新很快,新的核心几乎每几个星期就公布一次,其它软件的更新也是非常频繁。多数情况下,
盲目跟随潮流的升级并不必要,如果确实需要新版本的特性时再升级。换句话说,不要为了升级而升级。
Linux系统中多数软件都是用共享库来编译的,其中包含了在不同程序之间共享的公用子例程。
在运行某个程序时,如果看到如下信息:“Incompatible library version.”则表明需要将该库升级到程序所需要的版本。
库是向下兼容的,也就是说,用老版本库编译的程序可以在新安装的版本库上运行,反之则不行。
Linux库函数的升级是一项重要的工作,往往与其它软件包的升级有一定关联作用,所以操作前一定要备份文件。
下面看一下如何把Glibc 2.2.4.13升级至2.3.2版本,其过程如下:
1.下载.gz压缩文件并解压
在GUN C网站下载的四个.gz压缩文件,解压至一临时目录中:
cd /usr/caolinux
tar xzvf glibc-2.3.2.tar.gz
cd glibc-2.3.2
tar xzvf ../glibc-linuxthreads-2.3.2.tar.gz
tar xzvf ../glibc-crypt-2.3.2.tar.gz
tar xzvf ../glibc-localedata-2.3.2.tar.gz
2.建立库函数的安装目录
mkdir /usr/higlibc
cd /usr/higlibc
3.建立编译目录
mkdir cao
cd cao
./configure –enable-add-ons=linuxthreads,crypt,localedata -prefix=/usr/higlibc
4.编译与安装
make
make check
make install
5.改变数据库的链接
ln -s /usr/higlibc/lib/ld-linux.so.2 /lib/ld-linux.so.2
然后,修改/etc/ld.so.conf,加入一行/usr/higlibc/lib,执行下面代码:
ldconfig -v
更新/etc/ld.so.cache的内容,列出每个库的版本号,扫描目录和所要创建及更新的链接。
6.更改GCC设置
cd /usr/lib/gcc-lib
cp -r i386-redhat-linux higlibc
7.更新符号链接
cd /usr/higlibc/include
ln -s /usr/src/linux/include/linux
ln -s /usr/src/linux/include/asm
ln -s /usr/X11R6/include/X11
8.测试并完成
在Windows下,大家应该知道系统里面有很多服务,掌控着系统中各个设备和功能的启动,停止,暂停等操作。
在Ubuntu下,自然也是一样,只不过Linux下对服务的管理,要比windows高级N多,也细致很多。
因此linux的服务这部分,一直让许多新人们望而却步。
这次的教程,我将推荐给大家一个Ubuntu下的图形化的,管理系统服务的软件。名字叫:Boot-Up Manager,简称:BUM。
一. 基础知识补充
在Linux系统下,各个服务都是运行在一定的”运行级别“下的。我们先来认识下运行级别,在Linux系统下,系统有6个级别,分别是:
*运行等级 S:开机进程中的第一个运行等级。
*运行等级 1:(single-user)单用户模式。为单用户模式,就像Win9x 下的安全模式类似。
*运行等级 2,3,4,5: multi-user多用户环境,细分的话,是:
(2) 为多用户模式,但是没有NFS 支持。
(3) 为完整的多用户模式,是标准的运行级。
(4) 一般不用,在一些特殊情况下可以用它来做一些事情。
例如在笔记本电脑的电池用尽时,可以切换到这个模式来做一些设置。
(5) 就是 X11 ,进到 X Window 系统了。
*运行等级 0:(halt)关闭计算机
*运行等级 6:(reboot)重起计算机
我们以Ubuntu Linux为例,大家可以在 /etc/目录下找到类似: rcS.d,rc1.d,rc2.d等等这样的文件夹,应该是有 7 个。
这些文件夹当中,就是用来存储在相应(数字)等级下要运行(或者不运行)的服务的”链接“。大家可以随便着一个进去看看,
它里面的文件应该都是在左上角带一个”箭头“的。这些文件具体的链接,都是统一的,指向: /etc/init.d/ 这个文件夹下的文件。
这7个rc*.d文件下的文件,有个共性,就是:若文件夹下的链接文件开头子母是大写的,那么它链接的在 /etc/init.d 下的对应脚本将被调用并开启,
该进程将被执行。 相反,如果是小写字母,则代表该服务不会被运行。
此外,链接文件的开头S(start)或K(kill)后紧接的数字是指运行的顺序,数字小的运行在前。
二.对系统服务的“控制”
由上面的内容,我们应该知道,其实想开启,关闭一个服务,只需要将运行级的链接文件名的第一个字母由大写改为小写 (S–>s,K–>k)。
或者,我们先说我这次的教程推荐的“图形化”控制方式。
1. BUM 的安装: sudo apt-get install bum ,回车就是了,这个没什么问题的
2. BUM 的运行: 这个软件安装后,“应用程序”里面没有快捷方式的。可以从终端里面输入:sudo bum 来运行
3. BUM 的使用: 我们先来看看他的界面吧,每次运行,它都会先扫描系统的服务项目,扫描完成就是这个样子。
然后,我们勾选:Advanced,切换到“高级”模式,然后选择”Services“,就能看到我们当前系统的”服务“了。
这个软件使用很简单,如果我们要禁用某个服务,在列表中找到它,然后去掉前面的“勾”,点击“应用”就行了。
够简单吧?这个软件,除了可以控制服务的开启和关闭,还能控制系统的“开关机脚本(Startup and shutdown scripts),
操作方法一样的,有需要的兄弟们可以看下。
4. 用”命令“的方法来控制服务。
刚才我们说过了,其实禁用某个服务,就是把 rc*.d下的对应文件,开头字母改成小写。因此我们可以这样做,比如我要禁用”mdadm-raid”这个服务,
它是管理系统的 Raid(磁盘阵列)的,如果你没有阵列,自然不需要了。
sudo mv /etc/rcS.d/S04mdadm-raid /etc/rcS.d/s04mdadm-raid
估计喜欢玩“命令”的人,肯定都会选择这样做吧,呵呵,命令控们~
5. 系统服务的常见”操作“。在linux系统里面,如果你需要暂时性的对某个服务的运行状态进行操作,一般都可以这样操作:
(1)运行服务: sudo /etc/init.d/服务名 start
(2)停止服务: sudo /etc/init.d/服务名 stop
(3)重启服务: sudo /etc/init.d/服务名 restart
比如:sudo /etc/init.d/gdm restart
Debug
启动 Debug,它是可用于测试和调试 MS-DOS 可执行文件的程序。
Debug [[drive:][path] filename [parameters]]
参数
[drive:][path] filename
指定要测试的可执行文件的位置和名称。
parameters
指定要测试的可执行文件所需要的任何命令行信息。
++
说明
使用 Debug 命令但不指定要测试的文件
如果使用没有位置和文件名的 Debug 命令,然后键入所有的 Debug 命令以响应 Debug 提示符,连字符 (-)。
Debug 命令
以下是 Debug 命令列表:
? 显示 Debug 命令列表。
a 汇编 8086/8087/8088 记忆码。
c 比较内存的两个部分。
d 显示部分内存的内容。
e 从指定地址开始,将数据输入到内存。
f 使用指定值填充一段内存。
g 运行在内存中的可执行文件。
h 执行十六进制运算。
i 显示来自特定端口的 1 字节值。
l 将文件或磁盘扇区内容加载到内存。
m 复制内存块中的内容
/n 为 l 或 w 命令指定文件,或者指定正在测试的文件的参数。
o 向输出端口发送 1 个字节的值。
p 执行循环、重复的字符串指令、软件中断或子例程。
q 停止 Debug 会话。
r 显示或改变一个或多个寄存器。
s 在部分内存中搜索一个或多个字节值的模式。
t 执行一条指令,然后显示所有寄存器的内容、所有标志的状态和 Debug 下一步要执行的指令的解码形式。
u 反汇编字节并显示相应的原语句。
w 将被测试文件写入磁盘。
xa 分配扩展内存。
xd 释放扩展内存。
xm 映射扩展内存页。
xs 显示扩展内存的状态。
分隔命令参数
所有 Debug 命令都接受参数,除了 q 命令之外。可以用逗号或空格分隔参数,但是只有在两个十六进制值之间才需要这些分隔符。因此,以下命令等价:
dcs:100 110
d cs:100 110
d,cs:100,110
指定有效地址项
Debug 命令中的 address 参数指定内存位置。Address
是一个包含字母段记录的二位名称或一个四位字段地址加上一个偏移量。可以忽略段寄存器或段地址。a,g,l,t,u 和 w 命令的默认段是
CS。所有其他命令的默认段是 DS。所有数值均为十六进制格式。
有效地址如下:
CS:0100
04BA:0100
在段名和偏移量之间要有冒号。
指定有效范围项
Debug 命令中的 range 参数指定了内存的范围。可以为 range 选择两种格式:起始地址和结束地址,或者起始地址和长度范围(由 l 表示)。
例如,下面的两个语法都可以指定从 CS:100 开始的 16 字节范围:
cs:100 10f
cs:100 l 10
++
Debug 子命令
选择 Debug 命令以获得详细信息。
Debug:A(汇编)
Debug:C(比较)
Debug(转储)
Debug:E(键入)
Debug:F(填充)
Debug:G(转向)
Debug:H(十六进制)
Debug:I(输入)
Debug:L(加载)
Debug:M(移动)
Debug:N(名称)
Debug:O(输出)
Debug:P(执行)
Debug:Q(退出)
Debug:r(寄存器)
Debug:s(搜索)
Debug:T(跟踪)
Debug:U(反汇编)
Debug:W(写入)
Debug:XA(分配扩展内存)
Debug:XD(取消分配扩展内存)
Debug:XM(映射扩展内存页)
Debug:XS(显示扩展内存状态)
***********************Debug子命令******************************
Debug:A(汇编)
直接将 8086/8087/8088 记忆码合并到内存。
该命令从汇编语言语句创建可执行的机器码。所有数值都是十六进制格式,必须按一到四个字符输入这些数值。在引用的操作代码(操作码)前指定前缀记忆码。
a [address]
参数
address
指定键入汇编语言指令的位置。对 address 使用十六进制值,并键入不以“h”字符结尾的每个值。如果不指定地址,a 将在它上次停止处开始汇编。
有关将数据输入到指定字节中的信息,请单击“相关主题”列表中的 Debug E(键入)。
有关反汇编字节的信息,请单击“相关主题”列表中的 Debug U(反汇编)。
范例
a 命令支持所有形式的间接注册命令,如下例所示:
add bx,34[bp+2].[si-1]
pop [bp+di]
push [si] )
还支持所有操作码同义词,如下例所示:
loopz 100
loope 100
ja 200
jnbe 200
对于 8087 操作码,必须指定 wait 或 fwait 前缀,如下例所示:
fwait fadd st,st(3) ; this line assembles
; an fwait prefix
说明
使用记忆码
段的替代记忆码为 cs:、ds:、es: 和 ss:。远程返回的记忆码是
retf。字符串处理的记忆码必须明确声明字符串大小。例如,使用 movsw 可以移动 16 位的字串,使用
mov***(文字因故被系统屏蔽)***(文字因故被系统屏蔽) 可以移动 8 位字节串。
汇编跳转和调用
汇编程序根据字节替换自动将短、近和远的跳转及调用汇编到目标地址。通过使用 near 或 far 前缀可以替代这样的跳转或调用,如下例所示:
-a0100:0500
0100:0500 jmp 502 ; a 2-byte short jump
0100:0502 jmp near 505 ; a 3-byte near jump
0100:0505 jmp far 50a ; a 5-byte far jump
可以将 near 前缀缩写为 ne。
区分字和字节内存位置
当某个操作数可以引用某个字内存位置或者字节内存位置时,必须用前缀 word ptr 或者前缀 byte ptr 指定数据类型。可接受的缩写分别是 wo 和 by。以下范例显示两种格式:
dec wo [si]
neg byte ptr [128]
指定操作数
Debug 使用包括在中括号 ([ ]) 的操作数引用内存地址的习惯用法。这是因为另一方面 Debug 不能区分立即操作数和内存地址的操作数。以下范例显示两种格式:
mov ax,21 ; load AX with 21h
mov ax,[21] ; load AX with the
; contents of
; memory location 21h
使用伪指令
使用 a 命令提供两个常用的伪指令:db 操作码,将字节值直接汇编到内存,dw 操作码,将字值直接汇编到内存。以下是两个伪指令的范例:
db 1,2,3,4,\"THIS IS AN EXAMPLE\"
db THIS IS A QUOTATION MARK:\"
db \"THIS IS A QUOTATION MARK:\"
dw 1000,2000,3000,\"BACH\"
Debug:C(比较)
比较内存的两个部分。
c range address
参数
range
指定要比较的内存第一个区域的起始和结束地址,或起始地址和长度。有关有效的 range 值的信息,请单击“相关主题”列表中的“Debug 说明”。
address
指定要比较的第二个内存区域的起始地址。有关有效 address 值的信息,请单击“相关主题”列表中的“Debug 说明”。
++
范例
以下命令具有相同效果:
c100,10f 300
c100l10 300
每个命令都对 100h 到 10Fh 的内存数据块与 300h 到 30Fh 的内存数据块进行比较。
Debug 响应前面的命令并显示如下信息(假定 DS = 197F):
197F:0100 4D E4 197F:0300
197F:0101 67 99 197F:0301
197F:0102 A3 27 197F:0302
197F:0103 35 F3 197F:0303
197F:0104 97 BD 197F:0304
197F:0105 04 35 197F:0305
197F:0107 76 71 197F:0307
197F:0108 E6 11 197F:0308
197F:0109 19 2C 197F:0309
197F:010A 80 0A 197F:030A
197F:010B 36 7F 197F:030B
197F:010C BE 22 197F:030C
197F:010D 83 93 197F:030D
197F:010E 49 77 197F:030E
197F:010F 4F 8A 197F:030F
注意列表中缺少地址 197F:0106 和 197F:0306。这表明那些地址中的值是相同的。
++
说明
如果 range 和 address 内存区域相同,Debug 将不显示任何内容而直接返回到 Debug 提示符。如果有差异,Debug 将按如下格式显示:
address1 byte1 byte2 addess2
++++
Debug(转储)
显示一定范围内存地址的内容。
d [range]
参数
range
指定要显示其内容的内存区域的起始和结束地址,或起始地址和长度。有关有效的 range
值的信息,请单击“相关主题”列表中的“Debug 说明”。如果不指定 range,Debug 程序将从以前 d
命令中所指定的地址范围的末尾开始显示 128 个字节的内容。
有关显示寄存器内容的信息,请单击“相关主题”列表中的 Debug R(寄存器)。
++
范例
假定键入以下命令:
dcs:100 10f
Debug 按以下格式显示范围中的内容:
04BA:0100 54 4F 4D 00 53 41 57 59-45 52 00 00 00 00 00 00 TOM.SAWYER......
如果在没有参数的情况下键入 d 命令,Debug 按以前范例中所描述的内容来编排显示格式。显示的每行以比前一行的地址大 16 个字节(如果是显示 40 列的屏幕,则为 8 个字节)的地址开头。
对于后面键入的每个不带参数的 d 命令,Debug 将紧接在最后显示的命令后立即显示字节内容。
如果键入以下命令,Debug 将从 CS:100 开始显示 20h 个字节的内容:
dcs:100 l 20
如果键入以下命令,Debug 将显示范围从 CS 段的 100h 到 115h 中所有字节的内容:
dcs:100 115
++
说明
当使用 d 命令时,Debug 以两个部分显示内存内容:十六进制部分(每个字节的值都用十六进制格式表示)和
ASCII 码部分(每个字节的值都用 ASCII 码字符表示)。每个非打印字符在显示的 ASCII 部分由句号 (.) 表示。每个显示行显示
16 字节的内容,第 8 字节和第 9 字节之间有一个连字符。每个显示行从 16 字节的边界上开始。
++
Debug:E(键入)
将数据输入到内存中指定的地址。
可以按十六进制或 ASCII 格式键入数据。以前存储在指定位置的任何数据全部丢失。
e address
参数
address
指定输入数据的第一个内存位置。
list
指定要输入到内存的连续字节中的数据。
有关集成记忆码的信息,请单击“相关主题”列表中的 Debug A(汇编)。
有关显示内存部分内容的信息,请单击“相关主题”列表中的 Debug D (转储)。
++
范例
假定键入以下命令:
ecs:100
Debug 按下面的格式显示第一个字节的内容:
04BA:0100 EB.
要将该值更改为 41,请在插入点键入 41,如下所示:
04BA:0100 EB.41_
可以用一个 e 命令键入连续的字节值。在键入新值后按 SPACEBAR(空格键),而不是按 ENTER 键。Debug 显示下一个值。在此范例中,如果按三次 SPACEBAR(空格键),Debug 将显示下面的值:
04BA:0100 EB.41 10. 00. BC._
要将十六进制值 BC 更改为 42,请在插入点键入 42,如下所示:
04BA:0100 EB.41 10. 00. BC.42_
假定决定值 10 应该是 6F。要纠正该值,请按 HYPHEN 键两次以返回到地址 0101(值 10)。Debug 显示以下内容:
04BA:0100 EB.41 10. 00. BC.42-
04BA:0102 00.-
04BA:0101 10._
在插入点键入 6f 更改值,如下所示:
04BA:0101 10.6f_
按 ENTER 停止 e 命令并返回到 Debug 提示符下。
以下是字符串项的范例:
eds:100 \"This is the text example\"
该字符串将从 DS:100 开始填充 24 个字节。
++
说明
使用 address 参数
如果在没有指定可选的 list 参数的值情况下指定 address 的值,Debug 将显示地址和内容,在下一行重复地址,并等待您的输入。此时,您可以执行下列操作之一:
· 替换字节值。为此,请在当前值后键入新值。如果您键入的值不是有效的十六进制值,或该值包含两个以上的数字,则 Debug 不会回显无效或额外的字符。
· 进入下一个字节。为此,请按 SPACEBAR(空格键)。要更改该字节中的值,请在当前值后键入新值。如果按 SPACEBAR(空格键)时,移动超过了 8 位界限,Debug 程序将显示新的一行并在行首显示新地址。
· 返回到前一个字节。为此,请按 HYPHEN 键 (-)。可以反复按 HYPHEN 键 (-) 向后移动超过多个字节。在按 HYPHEN 时,Debug 开始新行并显示当前地址和字节值。
· 停止执行 e 命令。为此,请按 ENTER 键。在任何字节位置都可以按 ENTER。
使用 list 参数
如果指定 list 参数的值,随后的 e 命令将使用列表中的值替换现有的字节值。如果发生错误,将不更改任何字节值。
List 值可以是十六进制字节或字符串。使用空格、逗号或制表符来分隔值。必须将字符串包括在单或双引号中。
++++
Debug:F(填充)
使用指定的值填充指定内存区域中的地址。
可以指定十六进制或 ASCII 格式表示的数据。任何以前存储在指定位置的数据将会丢失。
f range list
参数
range
指定要填充内存区域的起始和结束地址,或起始地址和长度。关于有效的 range 值的信息,请单击“相关主题”列表中的“Debug 说明”。
list
指定要输入的数据。List 可以由十六进制数或引号包括起来的字符串组成。
++
范例
假定键入以下命令:
f04ba:100l100 42 45 52 54 41
作为响应,Debug 使用指定的值填充从 04BA:100 到 04BA:1FF 的内存位置。Debug 重复这五个值直到 100h 个字节全部填满为止。
++
说明
使用 range 参数
如果 range 包含的字节数比 list 中的数值大,Debug 将在 list 中反复指派值,直到 range 中的所有字节全部填充。
如果在 range 中的任何内存损坏或不存在,Debug 将显示错误消息并停止 f 命令。
使用 list 参数
如果 list 包含的数值多于 range 中的字节数,Debug 将忽略 list 中额外的值。
Debug:G(转向)
运行当前在内存中的程序。
g [=address] [breakpoints]
参数
=address
指定当前在内存中要开始执行的程序地址。如果不指定 address,Windows 2000 将从 CS:IP 寄存器中的当前地址开始执行程序。
breakpoints
指定可以设置为 g 命令的部分的 1 到 10 个临时断点。
有关执行循环、重复的字符串指令、软件中断或子程序的信息,请单击“相关主题”列表中的 Debug P(执行)。
有关执行指令的信息,请单击“相关主题”列表中的 Debug T(跟踪)。
范例
假定键入以下命令:
gcs:7550
Windows 2000 运行当前内存中的程序,直到执行到 CS 段中的断点地址 7550 为止。Debug 将显示寄存器的内容和标志的状态并结束 g 命令。
以下命令设置两个断点:
gcs:7550, cs:8000
如果在 Debug 遇到断点之后再次键入 g 命令,将从在断点之后的指令开始执行,而不是在通常的开始地址执行。
++
说明
使用 address 参数
必须在 address 参数之前使用等号 (=) 以区分开始地址 (address) 和断点地址 (breakpoints)。
指定断点
程序在它遇到的第一个断点处停止,而不论您在 breakpoint 列表的什么位置键入断点。Debug 在每个断点处用中断代码代替原始指令。
当程序到达断点时,Debug 将所有断点地址恢复到它们的最初指令并显示所有寄存器的内容、所有标记的状态以及最后执行指令的解码形式。Debug 显示的信息与使用 Debug r(寄存器)命令并指定断点时所显示的信息相同。
如果不在断点处停止程序,Debug 程序将不使用原始指令替换中断代码。
设置断点的限制
可以只在包含 8086 操作代码(操作码)的第一个字节的地址上设置断点。如果设置了 10 个以上的断点,Debug 将显示以下信息:
bp error
对用户堆栈指针的要求
用户堆栈指针必须有效且必须有 6 个字节可用于 g 命令。该命令使用 iret 指令跳转到正在被测试的程序。Debug
设置用户堆栈指针并将用户标志、代码段寄存器和指令指针压入用户堆栈。(如果用户堆栈无效或太小,操作系统可能会失败。)Debug
在指定的断点处设置中断代码 (0CCh)。
重新启动程序
不要在 Windows 2000 显示以下消息后尝试重新启动程序;
Program terminated normally
要正确地运行程序,必须通过使用 Debug n(名称)和 l(加载)命令重新加载该程序。
++++
Debug:H(十六进制)
对指定的两个参数执行十六进制运算。
h value1 value2
参数
value1
代表从 0 到 FFFFh 范围内的任何十六进制数字。
value2
代表从 0 到 FFFFh 范围内第二个十六进制数字。
++
范例
假定键入以下命令:
h19f 10a
Debug 执行运算并显示以下结果。
02A9 0095
++
说明
Debug 首先将指定的两个参数相加,然后从第一个参数中减去第二个参数。这些计算的结果显示在一行中:先计算和,然后计算差。
++++
Debug:I(输入)
从指定的端口读取并显示一个字节值。
i port
参数
port
按地址指定输入端口。地址可以是 16 位的值。
有关将字节值发送到输出端口的信息,请单击“相关主题”列表中的 Debug O(输出)。
++
范例
假定键入以下命令:
i2f8
同时假定端口的字节值是 42h。Debug 读取该字节,并将其值显示如下:
42
++
Debug:L(加载)
将某个文件或特定磁盘扇区的内容加载到内存。
要从磁盘文件加载 BX:CX 寄存器中指定的字节数内容,请使用以下语法:
l [address]
要略过 Windows 2000 文件系统并直接加载特定的扇区,请使用以下语法:
l address drive start number
参数
address
指定要在其中加载文件或扇区内容的内存位置。如果不指定 address,Debug 将使用 CS 寄存器中的当前地址。
drive
指定包含读取指定扇区的磁盘的驱动器。该值是数值型:0 = A, 1 = B, 2 = C 等。
start
指定要加载其内容的第一个扇区的十六进制数。
number
指定要加载其内容的连续扇区的十六进制数。只有要加载特定扇区的内容而不是加载 debug 命令行或最近的 Debug n(名称)命令中指定的文件时,才能使用 drive、start 和 number 参数。
有关指定用于 l 命令的文件的信息,请单击“相关主题”列表中的 Debug n(名称)。
有关写入调试到磁盘的文件的信息,请单击“相关主题”列表中的 Debug w(写入)。
++
范例
假定启动 Debug 并键入以下命令:
nfile.com
现在可以键入 l 命令以加载 File.com。Debug 将加载文件并显示 Debug 提示符。
假定需要从驱动器 C 将起始逻辑扇区为 15 (0Fh) 的 109 (6Dh) 个扇区的内容加载到起始地址为 04BA:0100 的内存中。为此,请键入以下命令:
l04ba:100 2 0f 6d
++
注意
使用不带参数的 l 命令
当使用不带参数的 l 命令时,在 debug
命令行上指定的文件将加载到内存中,从地址 CS:100 开始。Debug 同时将 BX 和 CX 寄存器设置为加载的字节数。如果不在
debug 命令行指定文件,所装入的文件将是最近使用 n 命令经常指定的文件。
使用具有 address 参数的 1 命令
如果使用带 address 参数的 l 命令,Debug 将从内存位置 address 开始加载文件或指定扇区的内容。
使用带全部参数的 l 命令
如果使用带所有参数的 l 命令,Debug 将加载指定磁盘扇区的内容而不是加载文件。
加载特定扇区的内容
指定范围内的每个扇区均从 drive 读取。Debug 从 start 开始加载,直到在 number 中指定的扇区数中的内容全部被加载。
加载 .exe 文件
Debug 忽略 .exe 文件的地址 address 参数。如果指定 .exe 文件,Debug
将文件重新定位到 .exe 文件的标题中指定的加载地址。在 .exe 文件被加载到内存前,标题自身从 .exe 文件脱离,因此磁盘上的
.exe 文件大小与内存中的不同。如果要检查整个 .exe 文件,请使用不同的扩展名重命名文件。
打开十六进制文件
Debug 将具有 .hex 扩展名的文件认为十六进制格式文件。键入不带参数的 l
命令,可以加载从十六进制文件中指定的地址处开始的十六进制文件。如果键入的 l 命令包含 address 参数,Debug
将把指定的地址加到在十六进制文件中找到的地址上,以确定起始地址。
Debug:M(移动)
将一个内存块中的内容复制到另一个内存块中。
m range address
参数
range
指定要复制内容的内存区域的起始和结束地址,或起始地址和长度。
address
指定要将 range 内容复制到该位置的起始地址。
++
范例
假定键入以下命令:
mcs:100 110 cs:500
Debug 首先将 CS:110
地址中的内容复制到地址 CS:510 中,然后将 CS:10F 地址中的内容复制到 CS:50F 中,如此操作直至将 CS:100
地址中的内容复制到地址 CS:500 中。要查看结果,请使用 Debug d(转储)命令,并使用 m 命令指定目标地址。
++
说明
复制操作对现有数据的影响
如果新数据没有写入正在被复制的数据块中的地址,则源数据将保持不变。但是,如果目标块已经包含数据(就象它在覆盖副本操作中一样),则将改写该数据。(覆盖复制操作是指那些目标数据块部分内容覆盖原数据块部分内容的操作。)
执行覆盖复制操作
m
命令执行目标地址的覆盖复制操作,而不丢失数据。将改写的地址内容首先复制。因此,如果将较高位地址的数据复制到较低位地址,则复制操作从原块的最低位地
址开始并向最高位地址进行。反之,如果要将数据从低地址复制到高地址,复制操作从原块的最高地址开始,向最低地址进行。
++++
Debug:N(名称)
指定 Debug l(加载)或 w(写入)命令的可执行文件的名称,或者指定正在调试的可执行文件的参数。
n [drive:][path] filename
要指定测试的可执行文件的参数,请使用以下语法:
n file-parameters
参数
如果在没有参数的情况下使用,则 n 命令清除当前规范。
[drive:][path] filename
指定要测试的可执行文件的位置和名称。
file-parameters
为正在测试的可执行文件指定参数和开关。
有关将文件或指定磁盘扇区的内容加载到内存中的信息,请单击“相关主题”列表中的 Debug L(加载)。
有关写入调试到磁盘的文件的信息,请单击“相关主题”列表中的 Debug W(写入)。
++
范例
假定已经启动 Debug,并加载了正在调试的程序 Prog.com。接着您决定为 Prog.com 指定两个参数并运行此程序。以下是此范例的命令序列:
debug prog.com
nparam1 param2
g
在这种情况下,Debug g(转向)命令会运行该程序,就好像您已在 Windows 2000 命令提示符后键入了如下命令:
prog param1 param2
所以,测试和调试反映 Prog.com 通常的运行时间环境。
在下面的命令序列中,第一个 n 命令将 File1.exe 指定为后接的 l(加载)命令的文件,该命令将 File1.exe
加载到内存。第二个 n 命令指定 File1.exe 将使用的参数。最后,g 命令将运行 File1.exe 文件,就好像您在 Windows
2000 命令行中键入了 File1 File2.dat File2.dat 一样。
nfile1.exe
l
nfile2.dat file3.dat
g
注意
· 不要在 n 命令的第二种形式后使用 l 命令。还要注意,如果现在使用 w(写入)命令,Windows 2000
将使用名称 File2.dat 保存正在调试的文件 File1.exe。为避免出现此结果,应该总是在 l 或 w 命令之前立即使用 n
命令的第一种形式。
++
说明
n 命令的两个用途
可以按两种方式使用 n 命令。首先,您可以使用它以指定后面的 l(加载)或
w(写入)命令所使用的文件。如果在没有命名所调试文件的情况下启动 Debug,必须在使用 l 命令加载文件之前使用命令 nfilename。在
CS:5C 为文件控制块 (FCB) 正确编排文件名的格式。其次,可以使用 n 命令指定被调试文件的命令行参数和开关。
内存区域
以下四个内存区域都会受到 n 命令的影响:
内存位置 内容
CS:5C 文件 1 的文件控制数据块 (FCB)
CS:6C 文件 2 的文件控制数据块 (FCB)
CS:80 n 命令行的长度(以字符表示)
CS:81 n 命令行字符的开头
为 n 命令指定的第一个文件名被放在 CS:5C 的 FCB 中。如果指定第二个文件名,此名称将放置到 CS:6C 的 FCB 中。n
命令行上键入的字符数(除第一个字符之外,n)存储在位置 CS:80。n 命令行上的实际字符(再次,除了字母 n 之外)存储在以 CS:81
开头的位置。注意这些字符可以是在 Windows 2000 命令提示符下键入的命令中有效的任何开关和分隔符。
++++
Debug:O(输出)
将字节值发送到输出端口。
o port byte-value
参数
port
通过地址指定输出端口。端口地址可以是 16 位值。
byte-value
指定要指向 port 的字节值。
有关从输入端口读取字节值的信息,请单击“相关主题”列表中的 Debug I(输入)。
++
范例
要将字节值 4Fh 发送到地址为 2F8h 的输出端口,请键入以下命令:
o2f8 4f
++++
Debug:P(执行)
执行循环、重复的字符串指令、软件中断或子例程;或通过任何其他指令跟踪。
p [= address] [number]
参数
=address
指定第一个要执行指令的位置。如果不指定地址,则默认地址是在 CS:IP 寄存器中指定的当前地址。
number
指定在将控制返回给 Debug 之前要执行的指令数。默认值为 1。
有关运行当前在内存中程序的信息,请单击“相关主题”列表中的 Debug G(转向)。
有关执行指令的信息,请单击“相关主题”列表中的 Debug T(跟踪)。
++
范例
假定正在测试的程序在地址 CS:143F 处包含一个 call 指令。要运行 call 目标位置的子程序然后将控制返回到 Debug,请键入以下命令:
p=143f
Debug 按以下格式显示结果:
AX=0000 BX=0000 CX=0000 DX=0000 SP=FFEE BP=0000 SI=0000 DI=0000
DS=2246 ES=2246 SS=2246 CS=2246 IP=1443 NV UP EI PL NZ AC PO NC
2246:1442 7505 JNZ 144A
++
说明
将控制传送到要测试的程序
当 p 命令将控制从 Debug 传送到要测试的程序时,该程序不间断运行,直到循环、重复字符串指令、软件中断或者完成了指定地址的子例程为止,或者直到执行了指定数量的机器指令为止。控制返回到 Debug。
地址参数的限制
如果 address 参数没有指定段,Debug 将使用被测试程序的 CS 寄存器。如果省略
address,程序将从 CS:IP 寄存器所指定的地址开始执行。必须在 address 参数之前使用等号 (=) 以便将它与 number
参数区分。如果在指定地址处的指令不是循环、重复的字符串指令、软件中断或子例程,则 p 命令与 Debug t(跟踪)命令的作用相同。
使用 p 命令显示的邮件
当 p 执行完一段说明后,Debug 显示出程序的寄存器内容、标志的状态以及下一段将要被执行的指令的解码形式。
警告
· 不能使用 p 命令跟踪只读内存 (ROM)。
Debug:Q(退出)
停止 Debug 会话,不保存当前测试的文件。
当您键入 q 以后,控制返回到 Windows 2000 的命令提示符。
q
参数
该命令不带参数。
有关保存文件的信息,请单击“相关主题”列表中的 Debug W(写入)。
++++
Debug:R(寄存器)
显示或改变一个或多个 CPU 寄存器的内容。
r [register-name]
参数
无
如果在没有参数的情况下使用,则 r 命令显示所有寄存器的内容以及寄存器存储区域中的标志。
register-name
指定要显示其内容的寄存器名。
有关显示内存部分内容的信息,请单击“相关主题”列表中的 Debug D(转储)。
有关反汇编字节的信息,请单击“相关主题”列表中的 Debug U(反汇编)。
++
范例
要查看所有寄存器的内容、所有标记的状态和当前位置的指令解码表,请键入以下命令:
r
如果当前位置是 CS:11A,显示外观将类似于以下内容:
AX=0E00 BX=00FF CX=0007 DX=01FF SP=039D BP=0000 SI=005C DI=0000
DS=04BA ES=04BA SS=04BA CS=O4BA IP=011A NV UP DI NG NZ AC PE NC
04BA:011A CD21 INT 21
要只查看标志的状态,请键入以下命令:
rf
Debug 按以下格式显示信息:
NV UP DI NG NZ AC PE NC - _
现在,您可以按任意顺序键入一个或多个有效的标志值,其中可以有或没有空格,如下所示:
nv up di ng nz ac pe nc - pleicy
Debug 结束 r 命令并显示 Debug 提示符。要查看更改,请键入 r 或 rf 命令。Debug 将显示以下内容:
NV UP EI PL NZ AC PE CY - _
按 ENTER 返回到 Debug 提示符。
++
说明
使用 r 命令
如果指定了寄存器名称,Windows 2000 将显示以十六进制标记表示的寄存器的 16 位值,并将冒号显示为提示符。如果要更改包含在寄存器中的值,除非键入新值并按 ENTER 键;否则,请按 ENTER 键返回 Debug 提示符。
有效寄存器名
以下是 register-name 的有效值:ax、bx、cx、dx、sp、bp、si、di、ds、es、ss、cs、ip、pc 及 f。ip 和 pc 都引用指令指针。
如果指定寄存器名称,而不是从前面的列表中指定,Windows 2000 将显示以下消息:
br error
使用 f 字符而不是寄存器名
如果键入 f 字符代替寄存器名,Debug 将每个标记的当前设置显示为两字母代码,然后显示 Debug 提示符。要更改标志的设置,请从下表中键入适当的两字母代码:
标志名 设置 清除
溢出 ov nv
方向 dn(减) up(增)
中断 ei(启用) di(禁用)
正负 ng(负) pl(正)
零 zr nz
辅助进位 ac na
奇偶校验 pe(偶校验) po(奇校验)
进位 cy nc
可以按任何顺序键入新的标志值。不需要在这些值之间留出空格。要停止 r 命令,请按 ENTER 键。任何没有指定新值的标志保持不变。
用 r 命令显示的邮件
如果为标记指定了多个值,Debug 将显示以下消息:
df error
如果指定没有在前面的表中列出的标志代码,Debug 将显示以下消息:
bf error
在这两种情况下,Debug 将忽略所有在无效项目之后指定的设置。
Debug 的默认设置
在启动 Debug 时,会将段寄存器设置到空闲内存的低端,指令指针设置为 0100h,清除所有标志,并且将其余寄存器设置为零,除了被设置为 FFEEh 的 sp 之外。
++++
Debug:S(搜索)
在某个地址范围搜索一个或多个字节值的模式。
s range list
参数
range
指定要搜索范围的开始和结束地址。有关 range 参数有效值的信息,请单击“相关主题”列表中的 Debug。
list
指定一个或多个字节值的模式,或要搜索的字符串。用空格或逗号分隔每个字节值和下一个字节值。将字符串值包括在引号中。
++
范例
假定需要查找包含值 41 并且范围从 CS:100 到 CS:110 的所有地址。为此,请键入以下命令:
scs:100 110 41
Debug 按以下格式显示结果:
04BA:0104
04BA:010D
-
以下命令在 CS:100 到 CS:1A0 的范围内搜索字符串“Ph”。
scs:100 1a0 \"Ph\"
++
说明
如果 list 参数包含多个字节值,Debug 将只显示出现字节值的第一个地址。如果 list 只包含一个字节值,Debug 将显示指定范围内出现该值的所有地址。
++++
Debug:T(跟踪)
执行一条指令,并显示所有注册的内容、所有标志的状态和所执行指令的解码形式。
t [=address] [number]
参数
=address
指定 Debug 启动跟踪指令的地址。如果省略 address 参数,跟踪将从程序的 CS:IP 寄存器所指定的地址开始。有关 address 参数有效值的信息,请单击“相关主题”列表中的 Debug。
number
指定要跟踪的指令数。该值必须是十六进制数。默认值为 1。
有关执行循环、重复的字符串指令、软件中断或子例程的信息,请单击“相关主题”列表中的 Debug P(执行)。
有关执行当前内存中程序的信息,请单击“相关主题”列表中的 Debug G(转向)。
++
范例
要执行一个指令(CS:IP 指向的指令),然后显示寄存器的内容、标志的状态以及指令的解码形式,请键入以下命令:
t
如果程序中的指令位于 04BA:011A,Debug 可能显示下列信息:
AX=0E00 BX=00FF CX=0007 DX=01FF SP=039D BP=0000 SI=005C DI=0000
DS=04BA ES=04BA SS=04BA CS=O4BA IP=011A NV UP DI NG NZ AC PE NC
04BA:011A CD21 INT 21
++
说明
跟踪只读内存中的指令
t 命令使用 8086 或 8088 微处理器的硬件跟踪模式。因此,也可以跟踪存储在只读内存 (ROM) 中的指令。
使用地址参数
必须在 address 参数之前使用等号 (=) 以便将它与 number 参数区分。
Debug:U(反汇编)
反汇编字节并显示相应的原语句,其中包括地址和字节值。反汇编代码看起来象已汇编文件的列表。
u [range]
参数
无
如果在没有参数的情况下使用,则 u 命令分解 20h 字节(默认值),从前面 u 命令所显示地址后的第一个地址开始。
range
指定要反汇编代码的起始地址和结束地址,或起始地址和长度。有关 range 参数有效值的信息,请单击“相关主题”列表中的 Debug。
有关集成记忆码的信息,请单击“相关主题”列表中的 Debug A(汇编)。
有关显示内存部分内容的信息,请单击“相关主题”列表中的 Debug D(转储)。
++
范例
要反汇编 16 (10h) 字节,从地址 04BA:0100 开始,请键入以下命令:
u04ba:100l10
Debug 按以下格式显示结果:
04BA:0100 206472 AND [SI+72],AH
04BA:0103 69 DB 69
04BA:0104 7665 JBE 016B
04BA:0106 207370 AND [BP+DI+70],DH
04BA:0109 65 DB 65
04BA:010A 63 DB 63
04BA:010B 69 DB 69
04BA:010C 66 DB 66
04BA:010D 69 DB 69
04BA:010E 63 DB 63
04BA:010F 61 DB 61
如果只显示从 04BA:0100 到 04BA:0108 特定地址的信息,请键入以下命令:
u04ba:0100 0108
Debug 显示以下内容:
04BA:0100 206472 AND [SI+72],AH
04BA:0103 69 DB 69
04BA:0104 7665 JBE 016B
04BA:0106 207370 AND [BP+DI+70],DH
++++
Debug:W(写入)
将文件或特定分区写入磁盘。
要将在 BX:CX 寄存器中指定字节数的内容写入磁盘文件,请使用以下语法:
w [address]
要略过 Windows 2000 文件系统并直接写入特定的扇区,请使用以下语法:
w address drive start number
参数
address
指定要写到磁盘文件的文件或部分文件的起始内存地址。如果不指定 address,Debug 程序将从 CS:100 开始。关于 address 参数有效值的信息,请在“相关主题”列表中单击 Debug。
drive
指定包含目标盘的驱动器。该值是数值型:0 = A, 1 = B, 2 = C,等等。
start
指定要写入第一个扇区的十六进制数。
number
指定要写入的扇区数。
有关指定用于 w 命令的文件的信息,请单击“相关主题”列表中的 Debug N(名称)。
有关将文件或文件扇区内容加载到内存中的信息,请单击“相关主题”列表中的 Debug L(加载)。
范例
假定要将起始地址为 CS:100 的内存内容写入到驱动器 B 的磁盘中。需要将数据从磁盘的逻辑扇区号 37h 开始并持续 2Bh 个扇区。为此,键入以下命令:
wcs:100 1 37 2b
当写操作完成时,Debug 再次显示 Debug 提示符。
++
说明
必须在启动 Debug 时或者在最近的 Debug n(名称)命令中指定磁盘文件的名字。这两种方法都可以将地址 CS:5C 处文件控制块的文件名正确地编排格式。
在使用不带参数的 w 命令之前重新设置 BX:CX
如果使用了 Debug g(转向)、t(跟踪)、p(执行)或 r(寄存器)命令,必须在使用无参数的 w 命令之前,将 BX:CX 寄存器复位。
将修改后的文件写入磁盘
如果修改文件但不更改文件名、长度或起始地址,Debug 仍然可以正确地将文件写入源磁盘位置。
w 命令的限制
不能用该命令写入 .exe 或 .hex 文件。
警告
· 因为略过 Windows 2000 文件句柄,所以写入特定的分区非常危险。如果键入错误的值,则磁盘文件结构很容易被损坏。
++++
Debug:XA(分配扩展内存)
分配扩展内存的指定页面数。
要使用扩展内存,必须安装符合 4.0 版的 Lotus/Intel/Microsoft 扩展内存规范 (LIM EMS) 的扩展内存设备驱动程序。
xa [count]
参数
count
指定要分配的扩展内存的 16KB 页数。
有关使用扩展内存的其他 Debug 命令的信息,请单击“相关主题”列表中的 XD(释放扩展内存)、XM(映射扩展内存页)或 XS(显示扩展内存状态)。
++
范例
要分配扩展内存的 8 个页面,请键入以下命令:
xa8
如果命令成功,Debug 将显示类似的以下消息:
Handle created=0003
++
说明
如果指定的页面数可用,则 Debug 将显示消息,此消息表明所创建的句柄的十六进制数;否则,Debug 将显示错误消息。
Debug:XD(释放扩展内存)
释放指向扩展内存的句柄。
要使用扩展内存,必须安装符合 4.0 版的 Lotus/Intel/Microsoft 扩展内存规范 (LIM EMS) 的扩展内存设备驱动程序。
xd [handle]
参数
handle
指定要释放的句柄。
有关使用扩展内存的其他 Debug 命令的信息,请单击“相关主题”列表中 XA(分配扩展内存)、XM(映射扩展内存页) 或 XS(显示扩展内存状态)。
++
范例
要释放句柄 0003,请键入以下命令:
xd 0003
如果命令成功,Debug 将显示下列消息:
Handle 0003 deallocated
++++
Debug:XM(映射扩展内存页)
将属于指定句柄的扩展内存逻辑页映射到扩展内存的物理页。
要使用扩展内存,必须安装符合 4.0 版的 Lotus/Intel/Microsoft 扩展内存规范 (LIM EMS) 的扩展内存设备驱动程序。
xm [lpage] [ppage] [handle]
参数
lpage
指定要映射到物理页 ppage 的扩展内存的逻辑页面号。
ppage
指定将 lpage 映射到的物理页面号。
handle
指定句柄。
有关使用扩展内存的其他 Debug 命令的信息,请单击“相关主题”列表中的 XA(分配扩展内存)、XD(释放扩展内存)或 XS(显示扩展内存)。
++
范例
要将句柄 0003 的逻辑页 5 映射到物理页 2,请键入以下命令:
xm 5 2 0003
如果命令成功,Debug 将显示下列消息:
Logical page 05 mapped to physical page 02
++++
Debug:XS(显示扩展内存状态)
显示有关扩展内存状态的信息。
要使用扩展内存,必须安装符合 4.0 版的 Lotus/Intel/Microsoft 扩展内存规范 (LIM EMS) 的扩展内存设备驱动程序。
xs
参数
该命令不带参数。
有关使用扩展内存的其他 Debug 命令的信息,请单击“相关主题”列表中的 XA(分配扩展内存)、XD(释放扩展内存)或 XM(映射扩展内存页)。
++
范例
要显示扩展内存信息,请键入以下命令:
xs
Debug 显示与以下类似的信息:
Handle 0000 has 0000 pages allocated
Handle 0001 has 0002 pages allocated
Physical page 00 = Frame segment C000
Physical page 01 = Frame segment C400
Physical page 02 = Frame segment C800
Physical page 03 = Frame segment CC00
2 of a total 80 EMS pages have been allocated
2 of a total FF EMS handles have been allocated
++
说明
Debug 显示的信息有如下格式:
Handle xx has xx pages allocated
Physical page xx = Frame segment xx
xx of a total xx EMS pages have been allocated
xx of a total xx EMS handles have been allocated
让ubuntu的图形界面更紧凑
ubuntu自带的theme似乎都比较不好用,个个按钮都贼大,浪费了不少屏幕空间,这点恐怕吓跑了不少windows用户。
但是linux的可配置能力还是毋庸置疑的,任何东西都可以自己动手丰衣足食,甚至是小康,哈哈
1.配置文件 ~/.gtkrc-2.0
作用:gtk2.0程序的设置文件,如果不存在,可手工创建。配置GTK2.0程序字体的配置如下:
style "gtkcompact" {
font_name="Sans 8"
GtkButton::default_border={0,0,0,0}
GtkButton::default_outside_border={0,0,0,0}
GtkButtonBox::child_min_width=0
GtkButtonBox::child_min_heigth=0
GtkButtonBox::child_internal_pad_x=0
GtkButtonBox::child_internal_pad_y=0
GtkMenu::vertical-padding=1
GtkMenuBar::internal_padding=0
GtkMenuItem::horizontal_padding=4
GtkOptionMenu::indicator_size=0
GtkOptionMenu::indicator_spacing=0
GtkPaned::handle_size=4
GtkRange::trough_border=0
GtkRange::stepper_spacing=0
GtkScale::value_spacing=0
GtkScrolledWindow::scrollbar_spacing=0
GtkExpander::expander_size=10
GtkExpander::expander_spacing=0
GtkTreeView::vertical-separator=0
GtkTreeView::horizontal-separator=0
GtkTreeView::expander-size=8
GtkTreeView::fixed-height-mode=TRUE
GtkWidget::focus_padding=0
}
class "GtkWidget" style "gtkcompact"
该文件也有一个全局配置文件/etc/gtk-2.0/gtkrc,注意是gtkrc,而不是gtkrc-2.0,默认该文件也是没有的,需手工创建。
一旦存在~/.gtkrc-2.0或/etc/gtk-2.0/gtkrc文件,则该文件的配置优先级是最高的,即使用gnome- font-properties字体配置程序也不能改变。
例如你在~/.gtkrc-2.0里设置了字体是SimSun 10号字,则你不能用gnome-font-properties字体配置程序更改该设置。
2.也有很多别人做好的紧凑型theme可用
http://jaket.is-a-geek.com/blog/programming/a-compact-gtk-theme-for-small-screens
最近玩awesome(平铺式窗口管理器),感觉不错。美中不足的是启动的程序界面太难看。
awesome本身的theme没法控制这个,只能控制它自己的状态栏,很可能以后会加入这种控制。
kde gnome xfce都有这样的控制。
google之后发现,只要在主目录建立个文件.gtkrc-2.0就可以控制gtk程序的外观了,当然配置还是有点复杂,
但是可以直接引用已经存在的配置,这样写:
include "/usr/share/themes/Clearlooks/gtk-2.0/gtkrc"
这样配置以后,使用awesome就比较顺畅了
使用set命令可以设置各种shell选项或者列出shell变量.单个选项设置常用的特性.
在某些选项之后-o参数将特殊特性打开.在某些选项之后使用+o参数将关闭某些特性,
不带任何参数的set命令将显示shell的全部变量.除非遇到非法的选项,否则set总是
返回ture.
当BASH shell被调用时,可以列出全部的选项.当前的选项集列在$-中.在option参数
被处理后,其他的参数被赋值到位置参数中($1,$2,...,$n)
set [--abefhkmnptuvxldCHP] [-o option name] [arguments ...]
源码:
选项 说明
-a 自动向已经修改的变量或为导出后序命令的变量作出标志
-b 不是在原提示符之前,而是立即引发终止后台任务的状态表表
-e 如果命令带非零值返回,立即退出
-f 禁止带扩展名的路径
-h 定义函数时,定位和存储函数命令,当函数被执行时,通常查询
函数命令
-k 所有的关键词参数,而不只是那些命令名前的关键词参数,被放
在环境命令中
-m 监视器模式,启动任务控制.此选项默认支持系统shell交互.后
台进程以单独的进程组运行,在每次完成任务时显示包含退出的
状态行
-n 读取命令但不执行命令.通常监查shell脚本的句法错误.交互
shell被忽略
-o option-name 选项名可以是下列之一:
选项 说明
allexport 同-a选项
braceexpand shell执行花括号扩展,在默认情况下起作用
emacs 使用emacs风格命令行编辑接口.除非shell以-noline-editing
选项启动,否则当shell交互时,通过默认启动该选项
errexit 同-e选项
histexpand 同-H选项
ignoreeof 其结果是好像shell命令IGNOREEOF=10被执行
interactive 允许单词前带#号,以使得在交互shell中忽略命令行的全部字符
-commands
monitor 同-m选项
noclobber 同-C选项
noexec 同-n选项
noglob 同-f选项
nohash 同-d选项
notify 同-b选项
nounset 同-u选项
physical 同-p选项
posix 改变BASH属性以匹配标准,默认操作不同于POSIX1003.2标准
verbose 同-v选项
vi 使用vi风格的命令行编辑器
XTRACE 同-x选项,如果没有给出选项名,显示当前选项值
-p 打开特权模式(在此模式,$ENV文件被处理,不能从环境中继承
shell函数.如果是有效用户ID而不是实用户组则自动启动.关闭
此选项将使得有效用户和组IDs设置实用户和组IDs)
-t 在读取命令并执行之后退出
-u 当执行参数括展时,把非设置变量作为错误处理(如果扩展企图
出现在非设置变量中,shell显示错误信息.如果不是交互式,则
带非凌值退出)
-v 输入行被读取时,显示shell输入行
-x 在每个简单命令被扩展之后,显示PS4扩展值,之后是要执行的命令
-l 保存和恢复绑定在命令中的名称
-d 禁止执行查找散列命令(通常,命令被保存在散列表中,一旦被找到
就不再继续查找)
-C 效果好像是执行了noclobber=shell命令
-H 使用!风格的历史替代(当shell交互时,在默认情况下,此选项有效)
-P 如果设置此参数,当执行改变目录命令cd时,不遵循符号链接,而是
使用实际的目录
-- 如果在选项后没有参数,不设置位置参数.否则,即使一些参数以a
选项开始,也要把位置参数设置为argument
- 结束选项的信号,将引发其余的参数被赋值到位置参数中(-x和-v
选项被关闭.如果没有argument,位置参数将保留未改变的参数)
在oracle安装完成后,默认会启动安装时建立的实例
如果我们需要创建新的实例,该如何操作呢?那么如何启动呢?如何进行访问呢?
下面就简单总结下:
1、 以oracle用户登录后,通过dbca命令启动图形化创建实例的界面
输入global name和sid,一路next,最后输入访问这个库的密码
2、 修改当前oracle用户的环境变量,修改oracle_sid为新创建实例的名字eastpay
然后source .bash_profile使其生效
3、 然后以sys用户登录sqlplus / as sysdba
4、 新建实例的用户,即给当前实例eastpay创建用户
Create user east identified by east
授权角色:
Grant all privileges to east(这里简单的把所有权限都赋予了east)
5、 以dba身份登录sqlplus / as sysdba,然后启动当前实例
Startup (注意默认是启动当前的oracle_sid,而不是启动所有的实例)
6、 然后测试新建用户连接新建的实例
Sqlplus east/east@eastpay
这里区分几个概念:
1、 系统用户和实例用户
a) 系统的用户包括:sys,system,sysman,dbsnmp
系统用户在安装oracle数据库时就存在了,而且以后再新建的实例中也存在
所以系统用户的密码是有多个的,即:在oracle数据库中有一个,在每个实例中也可以有一个
b) 实例用户
在创建实例后,为了使得以后连接、导入导出数据的方便,给每个实例创建一个用户(也可多个),然后就可以使用实例用户。实例用户只存在于该实例下
2、 启动实例和停止实例
启动实例即startup命令,只是启动当前oracle_sid所标识的实例名,而不是所有的实例,这里startup命令后也不需要带实例的名称
同样停止实例也是如此
在我使用Eclipse时,经常会发现需要调用Vim来进行一些编辑,例如整篇文章的自动缩进什么的。Eclipse能够很好的格式化Java代码,但是Vim能自动缩进更多的文件类型。我不需要太高级的功能,因此vim plugin什么的就派不上用场了。Eclipse的External Tool能完成我想要的一切。在某个视图中选中需要编辑的文件(或文件包含的类,方法,等)之后,只要单击设置好的vim外部工具,Vim就会被启动并打开相应的文件。
在Eclipse中建立这个外部工具的过程非常简单,如下简单的配置即可:
open external tool dialog下创建program:
name : vim
location : vim路径
working directory: ${project_loc}
arguments: -p --remote-tab-silent ${resource_loc}
p.s. -p --remote-tab-silent ${resource_loc} 这是打开一个新的标签页,而不是一个新的vim进程。
为了让Eclipse在Vim编辑过程结束之后自动更新资源的状态,我们需要到"Refresh"页面进行一下设置:
选择 the selected resource 选项
1.龙书(Dragon book)
书名是Compilers: Principles,Techniques,and Tools
作者是:Alfred V.Aho,Ravi Sethi,Jeffrey D.Ullman
国内所有的编译原理教材都是抄的它的,而且只是抄了最简单的前端的一些内容。
2.鲸书(Whale book)
书名是:Advanced Compiler Design and Implementation
作者是:Steven S.Muchnick
也就是高级编译原理,象什么陈意云之类的估计也就是这个水平不错了
3.虎书(Tiger book)
书名是:Modern Compiler Implementation in Java/C++/ML,Second Edition
作者是:Andrew W.Appel,with Jens Palsberg
这本书是3本书中最薄的一本,也是最最牛的一本!
lkd3 Linux Kernel Development 3
ldd3 Linux Device Driver 3
ulk3
Understanding the Linux Kernel 3
源代码情景分析
http://localhost:9080/daydayup/to.jsp
POST /daydayup/to.jsp HTTP/1.1
Host: localhost:9080
User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; zh-CN; rv:1.9.1.5) Gecko/20091102 Firefox/3.5.5
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: zh-cn,zh;q=0.5
Accept-Encoding: gzip,deflate
Accept-Charset: GB2312,utf-8;q=0.7,*;q=0.7
Keep-Alive: 300
Connection: keep-alive
Referer: http://localhost:9080/daydayup/from.jsp
Cookie: JSESSIONID=0000-Ubag-0HtbyayXaZZqC5H0-:-1
Content-Type: application/x-www-form-urlencoded
Content-Length: 18
username=454545454
HTTP/1.x 200 OK
Content-Type: text/html; charset=ISO-8859-1
Content-Language: zh-CN
Content-Length: 1099
Date: Wed, 25 Nov 2009 01:50:31 GMT
Server: WebSphere Application Server/6.1
----------------------------------------------------------
http://localhost:9080/daydayup/to.jsp?username=454545454
GET /daydayup/to.jsp?username=454545454 HTTP/1.1
Host: localhost:9080
User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; zh-CN; rv:1.9.1.5) Gecko/20091102 Firefox/3.5.5
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: zh-cn,zh;q=0.5
Accept-Encoding: gzip,deflate
Accept-Charset: GB2312,utf-8;q=0.7,*;q=0.7
Keep-Alive: 300
Connection: keep-alive
Referer: http://localhost:9080/daydayup/from.jsp
Cookie: JSESSIONID=0000-Ubag-0HtbyayXaZZqC5H0-:-1
HTTP/1.x 200 OK
Content-Type: text/html; charset=ISO-8859-1
Content-Language: zh-CN
Content-Length: 978
Date: Wed, 25 Nov 2009 01:51:35 GMT
Server: WebSphere Application Server/6.1
----------------------------------------------------------
使用alter table 加上 ACTIVATE NOT LOGGED INITIALLY 选项,
可以关闭日志记录,具体语法可以去看alter table命令的帮助。
该命令只对当前事务有效,事务结束后日志记录会自动恢复
神奇的国度,在internet上不翻墙简直是寸步难行,准备一个VPN是非常必要的,所以今天我就介绍一个
免费的高速美国VPN给大家。
首先登陆www.itshidden.com,然后点击上方的“Free Signup”,接着点击“Free”进入注册页面,也可以直接点击这里进入注册页面。在注册页面填上你的用户名,邮箱和密码,然后选上“I accept the terms and condition.”再点击“Register”提交。
www.itshidden.com的文档里有关于ubuntu下的设置介绍,但是由于在墙外(Blogspot上),我就贴到这里了:
To use the VPN service provided by ItsHidden.com, complete the following steps (that were partially found in this Ubuntu forum thread):
- Install the PPTP plug-in for Network Manager
$ sudo apt-get install network-manager-pptp
- Restart Network Manager
$ sudo killall NetworkManager
$ sudo NetworkManager &
- Create the VPN connection
- In the “Advanced…” settings, enable “Use Point-to-Point encryption (MPPE)”
Linux编辑器vim中删除行尾的^M
有时候,在 Linux 中使用打开曾在 Windows 中编辑过的文件时,会在行尾看到 ^M 字符。看起来总是感觉很别扭。
删除方法如下:
在 Vim 的命令模式中输入 :%s/^M$//g 后,回车即会自动删除该文件中的所有 ^M 字符。
注意: ^M 要用 Ctrl + v, Ctrl + m 来输入,M 后面的 $ 代表匹配行尾的内容,最后的 g 则表示每行中匹配到的内容都要置换。
一个程序经过编译连接后形成的地址空间是一个虚拟地址空间,而Linux在内存寻址时简化了分段
机制,使得虚拟地址与线性地址是一致的,比如程序test_wait.c代码如下:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main(int argc, char **argv)
{
int i;
unsigned char *buff;
buff = (char *)malloc(sizeof(char)*1024);
printf("pid is :%d\n", getpid());
for (i = 0; i < 60; i++) {
sleep(60);
}
return 0;
}
经过编译后形成的文件是test_wait,然后用命令objdump反汇编后如下(只取部分代码):
$ objdump -d test_wait
test_wait: file format elf32-i386
Disassembly of section .init:
08048304 <_init>:
8048304: 55 push %ebp
8048305: 89 e5 mov %esp,%ebp
8048307: 53 push %ebx
8048308: 83 ec 04 sub $0x4,%esp
804830b: e8 00 00 00 00 call 8048310 <_init+0xc>
8048310: 5b pop %ebx
8048311: 81 c3 e4 1c 00 00 add $0x1ce4,%ebx
8048317: 8b 93 fc ff ff ff mov -0x4(%ebx),%edx
804831d: 85 d2 test %edx,%edx
8048301: e8 2e 00 00 00 call 8048334 <__gmon_start__@plt>
8048306: e8 15 01 00 00 call 8048420
可以看到,其中的地址就是虚拟地址,整个虚拟地址空间大小为3GB,再加上可以通过系统调用进入
内核的1GB空间,于是每个进程可以拥有4GB的虚拟地址空间(也叫虚拟内存)。某个进程的虚拟地
址空间可以通过/proc文件系统看到:
$ ./test_wait
pid is :9840
重新开一个终端:
cat /proc/9840/maps
08048000-08049000 r-xp 00000000 08:01 212891 /home/chen/mem/test_wait
08049000-0804a000 r--p 00000000 08:01 212891 /home/chen/mem/test_wait
0804a000-0804b000 rw-p 00001000 08:01 212891 /home/chen/mem/test_wait
096d5000-096f6000 rw-p 096d5000 00:00 0 [heap]
b7dee000-b7def000 rw-p b7dee000 00:00 0
b7def000-b7f47000 r-xp 00000000 08:01 409724 /lib/tls/i686/cmov/libc-2.8.90.so
b7f47000-b7f49000 r--p 00158000 08:01 409724 /lib/tls/i686/cmov/libc-2.8.90.so
b7f49000-b7f4a000 rw-p 0015a000 08:01 409724 /lib/tls/i686/cmov/libc-2.8.90.so
b7f4a000-b7f4d000 rw-p b7f4a000 00:00 0
b7f59000-b7f5c000 rw-p b7f59000 00:00 0
b7f5c000-b7f76000 r-xp 00000000 08:01 392460 /lib/ld-2.8.90.so
b7f76000-b7f77000 r-xp b7f76000 00:00 0 [vdso]
b7f77000-b7f78000 r--p 0001a000 08:01 392460 /lib/ld-2.8.90.so
b7f78000-b7f79000 rw-p 0001b000 08:01 392460 /lib/ld-2.8.90.so
bf964000-bf979000 rw-p bffeb000 00:00 0 [stack]
关于此文件的详细信息可以参看:
http://www.kerneltravel.net/?p=287
由上面的信息可以看到
08048000-08049000地址段的标志是r-xp(读,执行)是代码段,
08049000-0804a000的标志是rw-p(读写)是数据段
096d5000-096f6000是堆也叫空洞,只有当程序中调用malloc()申请空间时才有堆段。
bf964000-bf979000 是堆栈段
这样我们可以看到进程的用户空间的分配了。如下图:
可以看出代码段在最低地址依次往上是数据段,空洞、堆栈段在最高地址,栈指针向下移动。
进程的虚拟地址在保存在内核中的task_struct(PCB)结构中,定义如下:
struct task_struct { //进程结构体
//……
struct mm_struct *mm;//描述进程的整个用户空间
}
而stuct mm_struct 结构中包含了虚拟空间的结构体字段
mmap(struct vm_area_struct * mmap),所以可以通过模块编程来查看进程的虚拟地址空间。
关于模块编程可以看这里:
http://www.kerneltravel.net/?p=80,程序清单如下:
#include <linux/module.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/sched.h>
static int pid;
module_param(pid,int,0644);
static int __init memtest_init(void)
{
struct task_struct *p;
struct vm_area_struct *temp;
printk("My module worked!\n");
p = find_task_by_vpid(pid);
temp = p->mm->mmap;
while(temp) {
printk("start:%p\tend:%p\n", (unsigned long *)temp->vm_start,
(unsigned long *)temp->vm_end);
temp = temp->vm_next;
}
return 0;
}
static void __exit memtest_exit(void)
{
printk("Unloading my module.\n");
return;
}
module_init(memtest_init);
module_exit(memtest_exit);
MODULE_LICENSE("GPL");
编译模块,运行刚才的程序test_wait,然后带参数插入模块,如下:
$ ./test_wait &
pid is :9413
$ sudo insmod mem.ko pid=9413
[ 2690.715913] My module worked!
[ 2690.715992] start:08048000 end:08049000
[ 2690.716005] start:08049000 end:0804a000
[ 2690.717029] start:0804a000 end:0804b000
[ 2690.717065] start:096d5000 end:096f6000
[ 2690.717096] start:b7dee000 end:b7def000
[ 2690.717126] start:b7def000 end:b7f47000
[ 2690.717157] start:b7f47000 end:b7f49000
[ 2690.717187] start:b7f49000 end:b7f4a000
[ 2690.717217] start:b7f4a000 end:b7f4d000
[ 2690.717248] start:b7f59000 end:b7f5c000
[ 2690.717304] start:b7f5c000 end:b7f76000
[ 2690.717334] start:b7f76000 end:b7f77000
[ 2690.717364] start:b7f77000 end:b7f78000
[ 2690.717395] start:b7f78000 end:b7f79000
[ 2690.717425] start:bf964000 end:bf979000
可以看出和刚才/proc文件系统中的地址是一样的。
在任意一个时刻,一个CPU只有一个进程在运行,所以虽然有时候很多进程的虚拟地址值有相同的
,但是由于每次只有一个进程运行,在当某个进程运行时cpu就将其虚拟地址也切换进来,这样就保
证了每个进程都拥有4GB的地址空间。
----------------------------------------------------------------------------------------------------------------------------
大
多数 Linux 发布版都定义了适当的缓冲区和其他 Transmission Control
Protocol(TCP)参数。可以修改这些参数来分配更多的内存,从而改进网络性能。设置内核参数的方法是通过 proc 接口,也就是通过读写
/proc 中的值。幸运的是,sysctl 可以读取 /etc/sysctl.conf 中的值并根据需要填充
/proc,这样就能够更轻松地管理这些参数。清单 2 展示在互联网服务器上应用于 Internet 服务器的一些比较激进的网络设置。
# Use TCP syncookies when needed
net.ipv4.tcp_syncookies = 1
# Enable TCP window scaling
net.ipv4.tcp_window_scaling: = 1
# Increase TCP max buffer size
net.core.rmem_max = 16777216
net.core.wmem_max = 16777216
# Increase Linux autotuning TCP buffer limits
net.ipv4.tcp_rmem = 4096 87380 16777216
net.ipv4.tcp_wmem = 4096 65536 16777216
# Increase number of ports available
net.ipv4.ip_local_port_range = 1024 65000
将这些设置添加到 /etc/sysctl.conf 的现有内容中。第一个设置启用 TCP SYN cookie。当从客户机发来新的
TCP 连接时,数据包设置了 SYN 位,服务器就为这个半开的连接创建一个条目,并用一个 SYN-ACK
数据包进行响应。在正常操作中,远程客户机用一个 ACK 数据包进行响应,这会使半开的连接转换为全开的。有一种称为 SYN 泛滥(SYN
flood) 的网络攻击,它使 ACK 数据包无法返回,导致服务器用光内存空间,无法处理到来的连接。SYN cookie
特性可以识别出这种情况,并使用一种优雅的方法保留队列中的空间(细节参见 参考资料
一节)。大多数系统都默认启用这个特性,但是确保配置这个特性更可靠。
启用 TCP 窗口伸缩使客户机能够以更高的速度下载数据。TCP 允许在未从远程端收到确认的情况下发送多个数据包,默认设置是最多 64 KB,在与延迟比较大的远程客户机进行通信时这个设置可能不够。窗口伸缩会在头中启用更多的位,从而增加窗口大小。
后面四个配置项增加 TCP 发送和接收缓冲区。这使应用程序可以更快地丢掉它的数据,从而为另一个请求服务。还可以强化远程客户机在服务器繁忙时发送数据的能力。
最后一个配置项增加可用的本地端口数量,这样就增加了可以同时服务的最大连接数量。
在下一次引导系统时,或者下一次运行 sysctl -p /etc/sysctl.conf 时,这些设置就会生效。
----------------------------------------------------------------------------------------------------------------------------
磁盘子系统的调优
磁盘在 LAMP 架构中扮演着重要的角色。静态文件、模板和代码都来自磁盘,组成数据库的数据表和索引也来自磁盘。对磁盘的许多调优(尤其是对于数据库)集中于避免磁盘访问,因为磁盘访问的延迟相当高。因此,花一些时间对磁盘硬件进行优化是有意义的。
首先要做的是,确保在文件系统上禁用 atime 日志记录特性。atime
是最近访问文件的时间,每当访问文件时,底层文件系统必须记录这个时间戳。因为系统管理员很少使用
atime,禁用它可以减少磁盘访问时间。禁用这个特性的方法是,在 /etc/fstab 的第四列中添加 noatime 选项。
演示如何启用 noatime 的 fstab 示例
/dev/VolGroup00/LogVol00 / ext3 defaults,noatime 1 1
LABEL=/boot /boot ext3 defaults,noatime 1 2
devpts /dev/pts devpts gid=5,mode=620 0 0
tmpfs /dev/shm tmpfs defaults 0 0
proc /proc proc defaults 0 0
sysfs /sys sysfs defaults 0 0
LABEL=SWAP-hdb2 swap swap defaults 0 0
LABEL=SWAP-hda3 swap swap defaults 0 0
有多种磁盘硬件组合,而且 Linux 不一定能够探测出访问磁盘的最佳方式。可以使用 hdparm 命令查明和设置用来访问
IDE 磁盘的方法。hdparm -t /path/to/device
执行速度测试,可以将这个测试结果作为性能基准。为了使结果尽可能准确,在运行这个命令时系统应该是空闲的。
在 /dev/hd 上执行的速度测试
# hdparm -t /dev/hda
/dev/hda:
Timing buffered disk reads: 182 MB in 3.02 seconds = 60.31 MB/sec
hdparm 的常用选项
选项 描述
-vi 向磁盘查询它支持的设置以及它正在使用的设置。
-c 查询/启用 (E)IDE 32 位 I/O 支持。hdparm -c 1 /dev/hda 启用这个设置。
-m 查询/设置每中断多扇区模式。如果设置大于零,设置值就是每个中断可以传输的最大扇区数量。
-d 1 -X 启用直接内存访问(DMA)传输并设置 IDE 传输模式。hdparm 手册页详细说明了在 -X 后面可以设置的数字。只有在 -vi 说明目前并未使用最快速的模式的情况下,才需要进行这个设置。
不幸的是,对于 Fiber Channel and Small Computer Systems Interface(SCSI)系统,调优依赖于具体的驱动器。
必须将有帮助的设置添加到启动脚本中,比如 rc.local。
----------------------------------------------------------------------------------------------------------------------------
TCP/IP子系统的调优
所有的TCP/IP调优参数都位于/proc/sys/net/目录. 例如, 下面是最重要的一些调优参数, 后面是它们的含义:
1. /proc/sys/net/core/rmem_max — 最大的TCP数据接收缓冲
2. /proc/sys/net/core/wmem_max — 最大的TCP数据发送缓冲
3. /proc/sys/net/ipv4/tcp_timestamps — 时间戳在(请参考RFC 1323)TCP的包头增加12个字节
4. /proc/sys/net/ipv4/tcp_sack — 有选择的应答
5. /proc/sys/net/ipv4/tcp_window_scaling — 支持更大的TCP窗口. 如果TCP窗口最大超过65535(64K), 必须设置该数值为1
6. rmem_default — 默认的接收窗口大小
7. rmem_max — 接收窗口的最大大小
8. wmem_default — 默认的发送窗口大小
9. wmem_max — 发送窗口的最大大小
/proc目录下的所有内容都是临时性的, 所以重启动系统后任何修改都会丢失.
建议在系统启动时自动修改TCP/IP参数:
把下面代码增加到/etc/rc.local文件, 然后保存文件, 系统重新引导的时候会自动修改下面的TCP/IP参数:
echo 256960 > /proc/sys/net/core/rmem_default
echo 256960 > /proc/sys/net/core/rmem_max
echo 256960 > /proc/sys/net/core/wmem_default
echo 256960 > /proc/sys/net/core/wmem_max
echo 0 > /proc/sys/net/ipv4/tcp_timestamps
echo 1 > /proc/sys/net/ipv4/tcp_sack
echo 1 > /proc/sys/net/ipv4/tcp_window_scaling
TCP/IP参数都是自解释的, TCP窗口大小设置为256960, 禁止TCP的时间戳(取消在每个数据包的头中增加12字节), 支持更大的TCP窗口和TCP有选择的应答.
上面数值的设定是根据互连网连接和最大带宽/延迟率来决定.
注: 上面实例中的数值可以实际应用, 但它只包含了一部分参数.
另外一个方法: 使用 /etc/sysctl.conf 在系统启动时将参数配置成您所设置的值:
net.core.rmem_default = 256960
net.core.rmem_max = 256960
net.core.wmem_default = 256960
net.core.wmem_max = 256960
net.ipv4.tcp_timestamps = 0
net.ipv4.tcp_sack =1
net.ipv4.tcp_window_scaling = 1
----------------------------------------------------------------------------------------------------------------------------
文件子系统的调优
ulimit -a 用来显示当前的各种用户进程限制。
Linux对于每个用户,系统限制其最大进程数。为提高性能,可以根据设备资源情况,
设置各linux 用户的最大进程数,下面我把某linux用户的最大进程数设为10000个:
ulimit -u 10000
对于需要做许多 socket 连接并使它们处于打开状态的 Java 应用程序而言,
最好通过使用 ulimit -n xx 修改每个进程可打开的文件数,缺省值是 1024。
ulimit -n 4096 将每个进程可以打开的文件数目加大到4096,缺省为1024
其他建议设置成无限制(unlimited)的一些重要设置是:
数据段长度:ulimit -d unlimited
最大内存大小:ulimit -m unlimited
堆栈大小:ulimit -s unlimited
CPU 时间:ulimit -t unlimited
虚拟内存:ulimit -v unlimited
暂时地,适用于通过 ulimit 命令登录 shell 会话期间。
永久地,通过将一个相应的 ulimit 语句添加到由登录 shell 读取的文件中, 即特定于 shell 的用户资源文件,如:
1)、解除 Linux 系统的最大进程数和最大文件打开数限制:
vi /etc/security/limits.conf
# 添加如下的行
* soft noproc 11000
* hard noproc 11000
* soft nofile 4100
* hard nofile 4100
说明:* 代表针对所有用户
noproc 是代表最大进程数
nofile 是代表最大文件打开数
2)、让 SSH 接受 Login 程式的登入,方便在 ssh 客户端查看 ulimit -a 资源限制:
a、vi /etc/ssh/sshd_config
把 UserLogin 的值改为 yes,并把 # 注释去掉
b、重启 sshd 服务:
/etc/init.d/sshd restart
3)、修改所有 linux 用户的环境变量文件:
vi /etc/profile
ulimit -u 10000
ulimit -n 4096
ulimit -d unlimited
ulimit -m unlimited
ulimit -s unlimited
ulimit -t unlimited
ulimit -v unlimited
/**************************************
有时候在程序里面需要打开多个文件,进行分析,系统一般默认数量是1024,(用ulimit -a可以看到)对于正常使用是够了,但是对于程序来讲,就太少了。
修改2个文件。
1./etc/security/limits.conf
vi /etc/security/limits.conf
加上:
* soft nofile 8192
* hard nofile 20480
2./etc/pam.d/login
session required /lib/security/pam_limits.so
**********
另外确保/etc/pam.d/system-auth文件有下面内容
session required /lib/security/$ISA/pam_limits.so
这一行确保系统会执行这个限制。
***********
3.一般用户的.bash_profile
#ulimit -n 1024
重新登陆ok
----------------------------------------------------------------------------------------------------------------------------
内存子系统的调优
内存子系统的调优不是很容易,需要不停地监测来保证内存的改变不会对服务器的其他子系统造成负面影响。如果要改变虚拟内存参数(在/proc/sys/vm),建议您每次只改变一个参数然后监测效果。对与虚拟内存的调整包括以下几个项目:
配置Linux内核如何更新dirty
buffers到磁盘。磁盘缓冲区用于暂存磁盘的数据。相对于内存来讲,磁盘缓冲区的速度很慢。因此,如果服务器使用这类内存,性能会成问题。当缓冲区内
的数据完全dirty,使用:sysctl -w vm.bdflush="30 500 0 0 500 3000 60 20 0"
vm.bdflush有9个参数,但是建议您只改变其中的3个:
1 nfract, 为排队写入磁盘前,bdflush daemon允许的缓冲区最大百分比
2 ndirty, 为bdflush即刻写的最大缓冲区的值。如果这个值很大,bdflush需要更多的时间完成磁盘的数据更新。
7 nfract_sync, 发生同步前,缓冲区变dirty的最大百分比
配置kswapd daemon,指定Linux的内存页数量
sysctl -w vm.kswapd="1024 32 64"
三个参数的描述如下:
– tries_base 相当于内核每次所的“页”的数量的四倍。对于有很多交换信息的系统,增加这个值可以改进性能。
– tries_min 是每次kswapd swaps出去的pages的最小数量。
– swap_cluster 是kswapd 即刻写如的pages数量。数值小,会提高磁盘I/O的性能;数值大可能也会对请求队列产生负面影响。
如果要对这些参数进行改动,请使用工具vmstat检查对性能的影响。其它可以改进性能的虚拟内存参数为:
_ buffermem
_ freepages
_ overcommit_memory
_ page-cluster
_ pagecache
_ pagetable_cache
----------------------------------------------------------------------------------------------------------------------------
网络子系统的调优
操作系统安装完毕,就要对网络子系统进行调优。对其它子系统的影响:影响CPU利用率,尤其在有大量TCP连接、块尺寸又非常小时,内存的使用会明显增加。
如何预防性能下降
如下的sysctl命令用于改变安全设置,但是它也可以防止网络性能的下降。这些命令被设置为缺省值。
◆关闭如下参数可以防止黑客对服务器IP地址的攻击
sysctl -w net.ipv4.conf.eth0.accept_source_route=0
sysctl -w net.ipv4.conf.lo.accept_source_route=0
sysctl -w net.ipv4.conf.default.accept_source_route=0
sysctl -w net.ipv4.conf.all.accept_source_route=0
◆开启TCP SYN cookies,保护服务器避免受syn-flood攻击,包括服务取决denial-of-service
(DoS) 或者分布式服务拒绝distributed denial-of-service (DDoS) (仅适用Red Hat
Enterprise Linux AS)
sysctl -w net.ipv4.tcp_syncookies=1
◆以下命令使服务器忽略来自被列入网关的服务器的重定向。因重定向可以被用来进行攻击,所以我们只接受有可靠来源的重定向。
sysctl -w net.ipv4.conf.eth0.secure_redirects=1
sysctl -w net.ipv4.conf.lo.secure_redirects=1
sysctl -w net.ipv4.conf.default.secure_redirects=1
sysctl -w net.ipv4.conf.all.secure_redirects=1
另外,你可以配置接受或拒绝任何ICMP重定向。ICMP重定向是器传输信息的机制。比如,当网关接收到来自所接网络主机的
Internet数据报时,网关可以发送重定向信息到一台主机。网关检查路由表获得下一个网关的地址,第二个网关将数据报路由到目标网络.关闭这些重定向
得命令如下:
sysctl -w net.ipv4.conf.eth0.accept_redirects=0
sysctl -w net.ipv4.conf.lo.accept_redirects=0
sysctl -w net.ipv4.conf.default.accept_redirects=0
sysctl -w net.ipv4.conf.all.accept_redirects=0
◆如果这个服务器不是一台路由器,那么它不会发送重定向,所以可以关闭该功能:
sysctl -w net.ipv4.conf.eth0.send_redirects=0
sysctl -w net.ipv4.conf.lo.send_redirects=0
sysctl -w net.ipv4.conf.default.send_redirects=0
sysctl -w net.ipv4.conf.all.send_redirects=0
◆配置服务器拒绝接受广播风暴或者smurf 攻击attacks:
sysctl -w net.ipv4.icmp_echo_ignore_broadcasts=1
◆忽略所有icmp包或者pings:
sysctl -w net.ipv4.icmp_echo_ignore_all=1
◆有些路由器针对广播祯发送无效的回应,每个都产生警告并在内核产生日志.这些回应可以被忽略:
sysctl -w net.ipv4.icmp_ignore_bogus_error_responses=1
针对TCP和UDP的调优
下边的命令用来对连接数量非常大的服务器进行调优.
◆对于同时支持很多连接的服务器,新的连接可以重新使用TIME-WAIT套接字. 这对于Web服务器非常有效:
sysctl -w net.ipv4.tcp_tw_reuse=1
如果你使用该命令,还要启动TIME-WAIT 套接字状态的快速循环功能:
sysctl -w net.ipv4.tcp_tw_recycle=1
图Figure 10-7显示出将这些功能启用,连接数量明显降低.因为每个TCP传输都包含远程客户端的信息缓存,所以有利于提高性能.缓存中存放round-trip时间、最大segment大小、拥塞窗口的信息。
◆
参数tcp_fin_timeout 是套接字关闭时,保持FIN-WAIT-2状态的时间。一个TCP连接以three-segment
SYN序列开始, 以three-segment FIN序列结束.均不保留数据.通过改变tcp_fin_timeout的值,
从FIN序列到内存可以空闲出来处理新连接的时间缩短了,使性能得到改进.改变这个值的前要经过认真的监测,避免因为死套接字造成内存溢出.
sysctl -w net.ipv4.tcp_fin_timeout=30
◆服务器的一个问题是,同一时刻的大量TCP连接里有很多的连接被打开但是没有使用. TCP的keepalive功能检测到这些连接,缺省情况下,在2小时之后丢掉. 2个小时的可能导致内存过度使用,降低性能.因此改成1800秒(30分钟)是个更好的选择:
sysctl -w net.ipv4.tcp_keepalive_time=1800
◆对于所有的队列,设置最大系统发送缓存(wmem) 和接收缓存(rmem)到8MB
sysctl -w net.ipv4.core.wmem_max=8388608
sysctl -w net.ipv4.core.rmem_max=8388608
这些设置指定了创建TCP套接字时为其分配的内存容量. 另外,使用如下命令发送和接收缓存.该命令设定了三个值:最小值、初始值和最大值:
sysctl -w net.ipv4.tcp_rmem="4096 87380 8388608"
sysclt -w net.ipv4.tcp.wmem="4096 87380 8388608"
第三个值必须小于或等于wmem_max和rmem_max。
◆(SUSE LINUX Enterprise Server适用) 通过保留路径验证来源数据包。缺省情况下,路由器转发所有的数据包,即便是明显的异常网络流量。通过启动和是的过滤功能,丢掉这些数据包:
sysctl -w net.ipv4.conf.eth0.rp_filter=1
sysctl -w net.ipv4.conf.lo.rp_filter=1
sysctl -w net.ipv4.conf.default.rp_filter=1
sysctl -w net.ipv4.conf.all.rp_filter=1
◆当服务器负载繁重或者是有很多客户端都是超长延时的连接故障,可能会导致half-open连接数量的增加。这对于Web服务器很来讲很平
常,尤其有很多拨号客户时.这些half-open连接保存在 backlog connections 队列中.将这个值最少设置为4096
(缺省为1024). 即便是服务器不接收这类连接,设置这个值还能防止受到denial-of-service (syn-flood)的攻击.
sysctl -w net.ipv4.tcp_max_syn_backlog=4096
◆设置ipfrag参数,尤其是NFS和Samba服务器。这里,我们可以设置用于重新组合IP碎片的最大、最小内存。当ipfrag_high_thresh值被指派,碎片会被丢弃直到达到ipfrag_low_thres值。
当TCP数据包传输发生错误时,开始碎片整理。有效的数据包保留在内存,同时损坏的数据包被转发。例如,设置可用内存范围从256 MB到384 MB
sysctl -w net.ipv4.ipfrag_low_thresh=262144
sysctl -w net.ipv4.ipfrag_high_thresh=393216
----------------------------------------------------------------------------------------------------------------------------
网络安全设置:
TCP SYN Flood 攻擊
TCP SYN Flood是一種常見,而且有效的遠端(遠程)拒絕服務(Denial of
Service)攻擊方式,它透過一定的操作破壞TCP三次握手建立正常連接,佔用並耗費系統資源,使得提供TCP服務的主機系統無法正常工作。
由於TCP SYN
Flood是透過網路底層對服務器Server進行攻擊的,它可以在任意改變自己的網路IP地址的同時,不被網路上的其他設備所識別,這樣就給防範網路犯
罪部門追查犯罪來源造成很大的困難。
系統檢查
一般情況下,可以一些簡單步驟進行檢查,來判斷系統是否正在遭受TCP SYN Flood攻擊。
1、 服務端無法提供正常的TCP服務。連接請求被拒絕或超時。
2、透過 netstat -an 命令檢查系統,發現有大量的SYN_RECV連接狀態。
3. iptables的设置,引用自CU
防止同步包洪水(Sync Flood)
# iptables -A FORWARD -p tcp --syn -m limit --limit 1/s -j ACCEPT
也有人写作
#iptables -A INPUT -p tcp --syn -m limit --limit 1/s -j ACCEPT
--limit 1/s 限制syn并发数每秒1次,可以根据自己的需要修改
防止各种端口扫描
# iptables -A FORWARD -p tcp --tcp-flags SYN,ACK,FIN,RST RST -m limit --limit 1/s -j ACCEPT
Ping洪水攻击(Ping of Death)
# iptables -A FORWARD -p icmp --icmp-type echo-request -m limit --limit 1/s -j ACCEPT
----------------------------------------------------------------------------------------------------------------------------
暂定步骤:
修改/etc/profile文件,加入:
ulimit -u 10240
ulimit -n 4096
ulimit -d unlimited
ulimit -m unlimited
ulimit -s unlimited
ulimit -t unlimited
ulimit -v unlimited
修改/etc/rc.d/rc.local,加入:
echo ‘131072′ > /proc/sys/fs/file-max
echo ‘131072′ > /proc/sys/fs/inode-max
(1G内存值修改成:65535 2G内存值修改成:131072 4G内存值修改成:262144)
修改/etc/sysctl.conf文件,加入:
net.core.rmem_default = 8388608
net.core.rmem_max = 8388608
net.core.wmem_default = 8388608
net.core.wmem_max = 8388608
net.ipv4.tcp_timestamps = 0
net.ipv4.tcp_sack =1
net.ipv4.tcp_window_scaling = 1
net.core.netdev_max_backlog=3000
#Modify i-node
sys.fs.file-max= 65535
sys.fs.inode-max= 65535
#Set System Memory
vm.bdflush="30 500 0 0 500 3000 60 20 0"
vm.kswapd="1024 32 64"
#Disable HackAttack!
net.ipv4.conf.eth0.accept_source_route=0
net.ipv4.conf.lo.accept_source_route=0
net.ipv4.conf.default.accept_source_route=0
net.ipv4.conf.all.accept_source_route=0
net.ipv4.conf.lo.accept_redirects=0
net.ipv4.conf.all.accept_redirects=0
net.ipv4.conf.eth0.accept_redirects=0
net.ipv4.conf.default.accept_redirects=0
net.ipv4.conf.lo.secure_redirects=0
net.ipv4.conf.all.secure_redirects=0
net.ipv4.conf.eth0.secure_redirects=0
net.ipv4.conf.default.secure_redirects=0
net.ipv4.conf.eth0.send_redirects=0
net.ipv4.conf.lo.send_redirects=0
net.ipv4.conf.default.send_redirects=0
net.ipv4.conf.all.send_redirects=0
net.ipv4.tcp_syncookies=1
net.ipv4.icmp_echo_ignore_broadcasts=1
net.ipv4.icmp_ignore_bogus_error_responses=1
#Web Servers
net.ipv4.tcp_tw_reuse=1
net.ipv4.tcp_tw_recycle=1
net.ipv4.tcp_fin_timeout=30
net.ipv4.tcp_keepalive_time=1800
net.ipv4.core.wmem_max=16777216
net.ipv4.core.rmem_max=16777216
net.ipv4.tcp_rmem="4096 87380 8388608"
net.ipv4.tcp.wmem="4096 87380 8388608"
net.ipv4.tcp_max_syn_backlog=8192
防火墙安全:
iptables -A FORWARD -p tcp --syn -m limit --limit 1/s -j ACCEPT
也有人写作
iptables -A INPUT -p tcp --syn -m limit --limit 1/s -j ACCEPT
--limit 1/s 限制syn并发数每秒1次,可以根据自己的需要修改
防止各种端口扫描
iptables -A FORWARD -p tcp --tcp-flags SYN,ACK,FIN,RST RST -m limit --limit 1/s -j ACCEPT
Ping洪水攻击(Ping of Death)
iptables -A FORWARD -p icmp --icmp-type echo-request -m limit --limit 1/s -j ACCEPT
备注说明:(相对比较激进的网络参数调整)
# Use TCP syncookies when needed
net.ipv4.tcp_syncookies = 1
# Enable TCP window scaling
net.ipv4.tcp_window_scaling: = 1
# Increase TCP max buffer size
net.core.rmem_max = 16777216
net.core.wmem_max = 16777216
# Increase Linux autotuning TCP buffer limits
net.ipv4.tcp_rmem = 4096 87380 8388608
net.ipv4.tcp_wmem = 4096 65536 8388608
# Increase number of ports available
net.ipv4.ip_local_port_range = 1024 65000
----------------------------------------------------------------------------------------------------------------------------
LINUX安全设置步骤:
删除所有那些不能在你系统上使用的默认用户和组账户: lp,sync,shutdown,halt, news, uucp, operator, games, gopher
ROOT自动从shell注销
编辑你的配置文件”vi /etc/profile”,在某个地方加入如下行,
“HISTFILESIZE=”
TMOUT=3600
我们为变量”TMOUT=”输入的这个值使用秒表示的、代表一个小时(60*60=3600秒)。如果你将此行加入你的 “/etc/profile” 文件,那么在一小时的非活动状态之后将要系统里的所有用户自动注销。你可以在用户私人的”.bashrc”文件里面
设置这个变量,可以在一个确定的时间以后自动注销他们。
禁止并且卸载所有没有用的服务
你必须禁止别切卸载所有你不用的的服务,那样的话,你就能少担心一些。看看你的”/etc/inetd.conf”文件, 用注释的方法禁用(在一行的开始加个#),然后给inetd进程发送一个SIGHUP命令去更新到当前的”inetd.conf”文件。这样做:
第一步把”/etc/inetd.conf”更改许可权限成600,那样的话,就只有root可以读和写。
[Root@kapil /]# chmod 600 /etc/inetd.conf
第二步确保”/etc/inetd.conf”的所有者是root。
第
三步编辑inetd.conf文件(vi /etc/inetd.conf),并且禁止一些服务,就像:ftp, telnet, shell,
login, exec, talk, ntalk, imap, pop-2, pop-3, finger,
auth等等,除非你打算用它。关闭这些服务就降低一些风险。
第四步给你的inetd进程发送一个HUP信号[root@kapil /]# killall -HUP inetd
第五步设置”/etc/inetd.conf”文件为不可更改,使用 chattr 命令,这样的话,没人能修改那个文件。*简单的设置文件为不可更改,执行如下命令:
[root@kapil /]# chattr +i /etc/inetd.conf
这将防止对”inetd.conf”文件的任何更改(意外的更改或者其他更改)。只有超级用户root能设置或者清除这个文件属性。修改inetd.conf *简单的取消不可更改的设置,执行如下命令:
[root@kapil /]# chattr -i /etc/inetd.conf
免疫”/etc/services”文件
你必须免疫 “/etc/services” 文件,防止未经授权的删除、增加服务。
免疫 “/etc/services” 文件,使用命令:
[root@kapil /]# chattr +i /etc/services
禁止Control-Alt-Deletc键盘关机命令
在你的”/etc/inittab”文件里面注释掉如下的行(用一个”#”)。
这样做,编辑inittab文件(vi /etc/inittab),更换:
ca::ctrlaltdel:/sbin/shutdown -t3 -r now
读入:
#ca::ctrlaltdel:/sbin/shutdown -t3 -r now
现在,按照如下提示输入,让更改生效:
[root@kapil /]# /sbin/init q
为脚本文件整理”/etc/rc.d/init.d”下的权限
整理脚本文件的许可权限,可靠的开始和结束所有你需要在引导时运行的常态进程,这样做:
[root@kapil/]# chmod -R 700 /etc/rc.d/init.d/*
这意味着只有root可以被允许读,写,和执行目录里面的脚本文件。
隐藏你的系统信息
默认状态下,当你登录到linux机器时,他告诉你Linux分布商的名字,版本,核心版本和服务器名字。这对一个骇客来说从你的服务器得到这些信息足够了以必须立刻用一个”Login:”提示符提示用户。
第一步
这样做,编辑”/etc/rc.d/rc.local”文件,放置”#”在如下行的前面。
# This will overwrite /etc/issue at every boot. So, make any changes you
# want to make to /etc/issue here or you will lose them when you reboot.
#echo “” >; /etc/issue
#echo “$R” >;>; /etc/issue
#echo “Kernel $(uname -r) on $a $(uname -m)” >;>; /etc/issue
#
#cp -f /etc/issue /etc/issue.net
#echo >;>; /etc/issue
第二步
然后,删除如下文件:在”/etc/”目录下的”issue.net” 和 “issue”:
[root@kapil /]# rm -f /etc/issue
[root@kapil /]# rm -f /etc/issue.net
禁止未用的 SUID/SGID 程序
一个常规用户如果设置为SUID root,将能够作为root运行程序。一个系统管理员必须最小化使用这些 SUID/GUID程序, 而且禁止那些不需要的程序。
第1步
从root拥有的程序里发现所有有`s’ 位的程序,用此命令:
[root@kapil]# find / -type f \( -perm -04000 -o -perm -02000 \) \-exec ls -lg {} \;
* 在被选中的程序上禁止suid 位,键入如下命令:
[root@kapil /]# chmod a-s [program]
关闭ipv6
vi /etc/modprobe.conf,在文件中添加以下两行
alias net-pf-10 off
alias ipv6 off
----------------------------------------------------------------------------------------------------------------------------
首先要弄清楚,在Linux系统中,内核为每一个新创建的文件分配一个Inode(索引结点),每个文件都有一个惟一的inode号。文件属性保存在索引结点里,在访问文件时,索引结点被复制到内存在,从而实现文件的快速访问。
链接是一种在共享文件和访问它的用户的若干目录项之间建立联系的一种方法。Linux中包括两种链接:硬链接(Hard Link)和软链接(Soft Link),软链接又称为符号链接(Symbolic link)。
一、硬链接
硬链接说白了是一个指针,指向文件索引节点,系统并不为它重新分配inode。可以用:ln命令来建立硬链接。语法:
ln [options] existingfile newfile
ln[options] existingfile-list directory
|
用
法:
第一种:为”existingfile”创建硬链接,文件名为”newfile”。第二种:在”directory”目录中,
为”existingfile-list”中包含的所有文件创建一个同名的硬链接。常用可选[options] –f
无论”newfile”存在与否,都创建链接。-n 如果”newfile”已存在,就不创建链接。
下面举一些例子:
$ ls –il
13058 -rwx - - - - - - 1 longcheng longcheng 48 8月 5 16:38 file1
13059 -rwx - - - - - - 1 longcheng longcheng 57 8月 5 16:40 file2
$ ln file2 file2hard
$ ls –il
13058 -rwx - - - - - - 1 longcheng longcheng 48 8月 5 16:38 file1
13059 -rwx - - - - - - 2 longcheng longcheng 57 8月 5 16:40 file2
13059 -rwx - - - - - - 2 longcheng longcheng 57 8月 5 16:40 file2hard
|
注
意在创建链接前,file1 显示的链接数目为1,创建链接后(1)file1和file1hard的链接数目都变为2;(2)
file1和file1hard在inode号是一样的(3)
file1和file1hard显示的文件大小也是一样。可见进行了ln命令的操作结果:file1和file1hard是同一个文件的两个名字,它们具
有同样的索引节点号和文件属性,建立文件file1的硬链接,就是为file1的文件索引节点在当前目录上建立一个新指针。如下图,你可以删除其中任何一
个,如rm file2 ,每次只会删除一个指针,
链接数同时减一,只有将所有指向文件内容的指针,也即链接数减为0时,内核才会把文件内容从磁盘上删除。当前目录逻辑结构:(不好意思图没有显示出来)。
还可以在不同目录,但同一文件系统中建立文件的硬链接。设file1、file2在目录/home/longcheng/dir1中,下面的命令,在/home/longcheng中建立file2的硬链接。
ln file2 /home/longcheng/file2hard
|
下面的程序,是将dir1目录中所有文件,在目录dir2中建立硬链接
$mkdir dir2
$ln /home/longcheng/dir1/* /home/longcheng/dir2
|
如果使用了 ln –f existingfile newfile,如果newfile已经存在,则无论原来newfile是什么文件,只用当前用户对它有写权限,newfile就成为exisitngfile的硬链接文件。
尽
管硬链接节省空间,也是Linux系统整合文件系统的传统方式,但是存在一下不足之处:(1)不可以在不同文件系统的文件间建立链接(2)只有超级用户才
可以为目录创建硬链接。虽然很多树上说root用户可以创建,但是笔者在学习过程中发现即使是root用户也不能创建,我的系统是Redhat,内核
2.4、2.6都试过,在其他系统中不知道是不是可以。
二、软链接(符号链接)
软链接克服了硬链接的不足,没有任何文件系统的限制,任何用户可以创建指向目录的符号链接。因而现在更为广泛使用,它具有更大的灵活性,甚至可以跨越不同机器、不同网络对文件进行链接。
建立软链接,只要在ln后面加上选项 –s,下面举个例子
$ ls -il
13058 -rwx - - - - - - 1 longcheng longcheng 48 8月 5 16:38 file1
13059 -rwx - - - - - - 2 longcheng longcheng 57 8月 5 16:40 file2
13059 -rwx - - - - - - 2 longcheng longcheng 57 8月 5 16:40 file2hard
$ln –s file1 file1soft
$ls -il
13058 -rwx - - - - - - 1 longcheng longcheng 48 8月 5 16:38 file1
13059 -rwx - - - - - - 2 longcheng longcheng 57 8月 5 16:40 file2
13059 -rwx - - - - - - 2 longcheng longcheng 57 8月 5 16:40 file2hard
13061 lrwxrwxrwx 1 longcheng longcheng 5 8月 5 16:58 file1soft->file1
|
从
上面链接后的结果可以看出来软链接与硬链接,区别不仅仅是在概念上,在实现上也是不同的。区别:硬链接原文件&链接文件公用一个inode号,说明他们是
同一个文件,而软链接原文件&链接文件拥有不同的inode号,表明他们是两个不同的文件;在文件属性上软链接明确写出了是链接文件,而硬链接没有写出
来,因为在本质上硬链接文件和原文件是完全平等关系;链接数目是不一样的,软链接的链接数目不会增加;文件大小是不一样的,硬链接文件显示的大小是跟原文
件是一样的,这用强调,因为是等同的嘛,而这里软链接显示的大小与原文件就不同了,file1大小是48B,而file1soft是5B,这里面的5实际
上就是“file1”的大小。
总之,建立软链接就是建立了一个新文件。当访问链接文件时,系统就会发现他是个链接文件,它读取链接文件找到真正要访问的文件。
在不同系统之间建立软链接、对目录建立链接,这里就不举例了,读者可以自己去尝试,我也是在不断实践中学习的。
当
然软链接也有硬链接没有的缺点,因为链接文件包含有原文件的路径信息,所以当原文件从一个目录下移到其他目录中,再访问链接文件,系统就找不到了,而硬链
接就没有这个缺陷,你想怎么移就怎么移;还有它要系统分配额外的空间用于建立新的索引节点和保存原文件的路径。补充一下:可以通过symlink来查看链
接文件,可以用 man symlink来学习。
a-killer-collection-of-global-css-reset-styles
杀手级全局css重置方法汇总
由于各个浏览器对页面元素的默认边距等设置不同,在开发跨浏览器的大型网站时,需要对这些默认设置进行重置。
这篇文章就是对各种重置方法的汇总。
这个世界无奇不有,就连做网页都要拼个高低长短,当你上网百般无聊之际,不妨看一看网络中的世界之最吧!
1.最宽网页
你可曾想过,网站就是一道空白的墙壁,然后你可以自由的在墙壁上涂鸦,发挥你的想象力。
号称世界最宽的网站“bluesfear”,是由众多的艺术工作者提供一副又一副的创意作品,拉着滚动条好像在坐火车那般,沿途欣赏风景。
bluesfear目前的宽度还在不断增长中,它还邀请全世界各路设计师,在“墙上”作画,让该网站一直延伸下去,喜欢创作的你也可以设计一张能够跟最后一张连续的图片提交上去。
●网址:
http://worm.bluesfear.com/index2.html
2.最高网页
有了最宽网页,就来看看最高的网页吧!这个目前高1万8千939公里的网页“worlds-highest-website”,浏览者可以手动滚动网页,或使用电梯。如果你向使用滑鼠滚轮看网页最下面的图片,应该是不可能,除非你非常有耐力。
●网址:
http://worlds-highest-website.com/
az
3.最贵网页
英国nottingham大学生亚力,因筹集自己的大学学费而又不想跟银行借贷款,突发奇想的用了10分钟就建立了一个名叫百万首页的网站,然后将这个网站的首页平均分成1万份,每一份只是一个小小的格子。
他宣称每个格子卖100美元,买家可以在自己购买的格子中随意放任何东西,包括商标、名字、或者特意设计的图片链接等等。于是一个几乎是零成本的网页在经过Alex的创意后,总价值就变成了一个高达百万美元的网页。
中国站.长.站
●网址:
http://www.milliondollarhomepage.com
4.最小网页
由阿兰·奥登设计的世界最小的网站“guimp”,它的首页的大小只相当于一个图标,此外什么也没有。虽然设计小,但五脏俱全,里面还有游戏、博客、相册等,不过这网站非常考你的视力,也许想看也看不到。
●网址:
http://www.guimp.com
MySQL管理员应该知道如何设置MySQL用户账号,指出哪个用户可以连接服务器,从哪里连接,连接后能做什么。MySQL
3.22.11开始引入两条语句使得这项工作更容易做:GRANT语句创建MySQL用户并指定其权限,而REVOKE语句删除权限。两条语句扮演了
mysql数据库的前端角色,并提供与直接操作这些表的内容不同的另一种方法。CREATE和REVOKE语句影响4个表:授权表
内容
user 能连接服务器的用户以及他们拥有的任何全局权限
db 数据库级权限
tables_priv 表级权限
columns_priv 列级权限
还有第5个授权表(host),但它不受GRANT和REVOKE的影响。
当你对一个用户发出一条GRANT语句时,在user表中为该用户创建一条记录。如果语句指定任何全局权限(管理权限或适用于所有数据库的权限),这些
也记录在user表中。如果你指定数据库、表和列级权限,他们被分别记录在db、tables_priv和columns_priv表中。
用GRANT和REVOKE比直接修改授权表更容易些,然而,建议你阅读一下《MySQL安全性指南》。这些表异常重要,而且作为一名管理员,你应该理解它们如何超越GRANT和REVOKE语句的功能水平。
在下面的章节中,我们将介绍如何设置MySQL用户账号并授权。我们也涉及如何撤权和从授权表中删除用户。
你可能也想考虑使用mysqlaccess和mysql_setpermission脚本,它是MySQL分发的一部分,它们是Perl脚本,提供GRANT语句的另一种选择设置用户账号。mysql_setpermission需要安装DBI支持。
1 创建用户并授权
GRANT语句的语法看上去像这样:GRANT privileges (columns) ON what TO user IDENTIFIED BY "password" WITH GRANT OPTION
要使用该语句,你需要填写下列部分:
privileges
授予用户的权限,下表列出可用于GRANT语句的权限指定符:
权限指定符 |
权限允许的操作 |
ALTER |
修改表和索引 |
CREATE |
创建数据库和表 |
DELETE |
删除表中已有的记录 |
DROP |
抛弃(删除)数据库和表 |
INDEX |
创建或抛弃索引 |
INSERT |
向表中插入新行 |
REFERENCE |
未用 |
SELECT |
检索表中的记录 |
UPDATE |
修改现存表记录 |
FILE |
读或写服务器上的文件 |
PROCESS |
查看服务器中执行的线程信息或杀死线程 |
RELOAD |
重载授权表或清空日志、主机缓存或表缓存。 |
SHUTDOWN |
关闭服务器 |
ALL |
所有;ALL PRIVILEGES同义词 |
USAGE |
特殊的“无权限”权限 |
上表显示在第一组的权限指定符适用于数据库、表和列,第二组数管理权限。一般,这些被相对严格地授权,因为它们允许用户影响服务器的操作。第三组权限特殊,ALL意味着“所有权限”,UASGE意味着无权限,即创建用户,但不授予权限。
columns
权限运用的列,它是可选的,并且你只能设置列特定的权限。如果命令有多于一个列,应该用逗号分开它们。
what
权限运用的级别。权限可以是全局的(适用于所有数据库和所有表)、特定数据库(适用于一个数据库中的所有表)或特定表的。可以通过指定一个columns字句是权限是列特定的。
user
权限授予的用户,它由一个用户名和主机名组成。在MySQL中,你不仅指定谁能连接,还有从哪里连接。这允许你让两个同名用户从不同地方连接。MySQL让你区分他们,并彼此独立地赋予权限。
MySQL中的一个用户名就是你连接服务器时指定的用户名,该名字不必与你的Unix登录名或Windows名联系起来。缺省地,如果你不明确指定一个
名字,客户程序将使用你的登录名作为MySQL用户名。这只是一个约定。你可以在授权表中将该名字改为nobody,然后以nobody连接执行需要超级
用户权限的操作。
password
赋予用户的口令,它是可选的。如果你对新用户没有指定
IDENTIFIED
BY子句,该用户不赋给口令(不安全)。对现有用户,任何你指定的口令将代替老口令。如果你不指定口令,老口令保持不变,当你用IDENTIFIED
BY时,口令字符串用改用口令的字面含义,GRANT将为你编码口令,不要象你用SET PASSWORD 那样使用password()函数。
WITH GRANT OPTION子句是可选的。如果你包含它,用户可以授予权限通过GRANT语句授权给其它用户。你可以用该子句给与其它用户授权的能力。
用户名、口令、数据库和表名在授权表记录中是大小写敏感的,主机名和列名不是。
一般地,你可以通过询问几个简单的问题来识别GRANT语句的种类:
谁能连接,从那儿连接?
用户应该有什么级别的权限,他们适用于什么?
用户应该允许管理权限吗?
下面就讨论一些例子。
1.1 谁能连接,从那儿连接?
你可以允许一个用户从特定的或一系列主机连接。有一个极端,如果你知道降职从一个主机连接,你可以将权限局限于单个主机
:GRANT
ALL ON samp_db.* TO boris@localhost IDENTIFIED BY "ruby"GRANT ALL ON
samp_db.* TO fred@res.mars.com IDENTIFIED BY "quartz"
(samp_db.*意思是“samp_db数据库的所有表)
另一个极端是,你可能有一个经常旅行并需要能从世界各地的主机连接的用户max。在这种情况下,你可以允许他无论从哪里连接:
GRANT ALL ON samp_db.* TO max@% IDENTIFIED BY "diamond"
“%”字符起通配符作用,与LIKE模式匹配的含义相同。在上述语句中,它意味着“任何主机”。所以max和max@%等价。这是建立用户最简单的方法,但也是最不安全的。
取其中,你可以允许一个用户从一个受限的主机集合访问。例如,要允许mary从snake.net域的任何主机连接,用一个%.snake.net主机指定符:
GRANT ALL ON samp_db.* TO mary@.snake.net IDENTIFIED BY "quartz";
如果你喜欢,用户标识符的主机部分可以用IP地址而不是一个主机名来给定。你可以指定一个IP地址或一个包含模式字符的地址,而且,从MySQL 3.23,你还可以指定具有指出用于网络号的位数的网络掩码的IP号:
GRANT
ALL ON samp_db.* TO boris@192.168.128.3 IDENTIFIED BY "ruby" GRANT ALL
ON samp_db.* TO fred@192.168.128.% IDENTIFIED BY "quartz" GRANT ALL ON
samp_db.* TO rex@192.168.128.0/17 IDENTIFIED BY "ruby"
第一个例子指出用户能从其连接的特定主机,第二个指定对于C类子网192.168.128的IP模式,而第三条语句中,192.168.128.0/17指定一个17位网络号并匹配具有192.168.128头17位的IP地址。
如果MySQL抱怨你指定的用户值,你可能需要使用引号(只将用户名和主机名部分分开加引号)。
GRANT ALL ON samp_db.president TO "my friend"@"boa.snake.net"
1.2 用户应该有什么级别的权限和它们应该适用于什么?
你可以授权不同级别的权限,全局权限是最强大的,因为它们适用于任何数据库。要使ethel成为可做任何事情的超级用户,包括能授权给其它用户,发出下列语句:
GRANT ALL ON *.* TO ethel@localhost IDENTIFIED BY "coffee" WITH GRANT OPTION
ON子句中的*.*意味着“所有数据库、所有表”。从安全考虑,我们指定ethel只能从本地连接。限制一个超级用户可以连接的主机通常是明智的,因为它限制了试图破解口令的主机。
有
些权限(FILE、PROCESS、RELOAD和SHUTDOWN)是管理权限并且只能用"ON
*.*"全局权限指定符授权。如果你愿意,你可以授权这些权限,而不授权数据库权限。例如,下列语句设置一个flush用户,他只能发出flush语句。
这可能在你需要执行诸如清空日志等的管理脚本中会有用:
GRANT RELOAD ON *.* TO flushl@localhost IDENTIFIED BY "flushpass"
一般地,你想授权管理权限,吝啬点,因为拥有它们的用户可以影响你的服务器的操作。
数据库级权限适用于一个特定数据库中的所有表,它们可通过使用ON db_name.*子句授予:
GRANT ALL ON samp_db TO bill@racer.snake.net INDETIFIED BY "rock" GRANT SELECT ON samp_db TO ro_user@% INDETIFIED BY "rock"
第一条语句向bill授权samp_db数据库中所有表的权限,第二条创建一个严格限制访问的用户ro_user(只读用户),只能访问samp_db数据库中的所有表,但只有读取,即用户只能发出SELECT语句。
你可以列出一系列同时授予的各个权限。例如,如果你想让用户能读取并能修改现有数据库的内容,但不能创建新表或删除表,如下授予这些权限:
GRANT SELECT,INSERT,DELETE,UPDATE ON samp_db TO bill@snake.net INDETIFIED BY "rock"
对于更精致的访问控制,你可以在各个表上授权,或甚至在表的每个列上。当你想向用户隐藏一个表的部分时,或你想让一个用户只能修改特定的列时,列特定权限非常有用。如:
GRANT
SELECT ON samp_db.member TO bill@localhost INDETIFIED BY "rock"GRANT
UPDATE (expiration) ON samp_db. member TO bill@localhost
第一条语句授予对整个member表的读权限并设置了一个口令,第二条语句增加了UPDATE权限,当只对expiration列。没必要再指定口令,因为第一条语句已经指定了。
如果你想对多个列授予权限,指定一个用逗号分开的列表。例如,对assistant用户增加member表的地址字段的UPDATE权限,使用如下语句,新权限将加到用户已有的权限中:
GRANT UPDATE (street,city,state,zip) ON samp_db TO assistant@localhost
通
常,你不想授予任何比用户确实需要的权限宽的权限。然而,当你想让用户能创建一个临时表以保存中间结果,但你又不想让他们在一个包含他们不应修改内容的数
据库中这样做时,发生了要授予在一个数据库上的相对宽松的权限。你可以通过建立一个分开的数据库(如tmp)并授予开数据库上的所有权限来进行。例如,如
果你想让来自mars.net域中主机的任何用户使用tmp数据库,你可以发出这样的GRANT语句:
GRANT ALL ON tmp.* TO ""@mars.net
在你做完之后,用户可以创建并用tmp.tbl_name形式引用tmp中的表(在用户指定符中的""创建一个匿名用户,任何用户均匹配空白用户名)。
1.3 用户应该被允许管理权限吗?
你
可以允许一个数据库的拥有者通过授予数据库上的所有拥有者权限来控制数据库的访问,在授权时,指定WITH GRANT
OPTION。例如:如果你想让alicia能从big.corp.com域的任何主机连接并具有sales数据库中所有表的管理员权限,你可以用如下
GRANT语句:
GRANT ALL ON sales.* TO alicia@%.big.corp.com INDETIFIED BY "applejuice" WITH GRANT OPTION
在
效果上WITH GRANT
OPTION子句允许你把访问授权的权利授予另一个用户。要注意,拥有GRANT权限的两个用户可以彼此授权。如果你只给予了第一个用户SELECT权
限,而另一个用户有GRANT加上SELECT权限,那么第二个用户可以是第一个用户更“强大”。
2 撤权并删除用户
要取消一个用户的权限,使用REVOKE语句。REVOKE的语法非常类似于GRANT语句,除了TO用FROM取代并且没有INDETIFED BY和WITH GRANT OPTION子句:
REVOKE privileges (columns) ON what FROM user
user部分必须匹配原来GRANT语句的你想撤权的用户的user部分。privileges部分不需匹配,你可以用GRANT语句授权,然后用REVOKE语句只撤销部分权限。
REVOKE语句只删除权限,而不删除用户。即使你撤销了所有权限,在user表中的用户记录依然保留,这意味着用户仍然可以连接服务器。要完全删除一个用户,你必须用一条DELETE语句明确从user表中删除用户记录:
%mysql -u root mysqlmysql>DELETE FROM user ->WHERE User="user_name" and Host="host_name";mysql>FLUSH PRIVILEGES;
DELETE语句删除用户记录,而FLUSH语句告诉服务器重载授权表。(当你使用GRANT和REVOKE语句时,表自动重载,而你直接修改授权表时不是。)
Number对象的属性 |
属性 |
说明 |
MAX_VALUE |
最大有效数字 |
MIN_VALUE |
最小有效数字 |
NaN |
非数字 |
POSITIVE_INFINITY |
正无穷大 |
NEGATIVE_INFINITY |
负无穷大 |
Number对象的方法 |
方法 |
说明 |
toExponential() |
转化为科学计数法,参数为小数点后保留的位数 |
toFixed() |
设定保留的小数位数,参数为小数闰数,为空则无小数部分,采用四舍五入方法进行 |
toLocaleString() |
转化为本地表示方法,涉及到小数点,千分号的表示 |
toString() |
转化为字符串,参数为转化时参照的进制,默认为十进制 |
toPrecision() |
设定数字的精确度,参数为精度要求 |
valueOf() |
返回数字的数值(由Number转化为简单数值类型) |
scrollHeight: 获取对象的滚动高度。
scrollLeft:设置或获取位于对象左边界和窗口中目前可见内容的最左端之间的距离
scrollTop:设置或获取位于对象最顶端和窗口中可见内容的最顶端之间的距离
scrollWidth:获取对象的滚动宽度
offsetHeight:获取对象相对于版面或由父坐标 offsetParent 属性指定的父坐标的高度
offsetLeft:获取对象相对于版面或由 offsetParent 属性指定的父坐标的计算左侧位置
offsetTop:获取对象相对于版面或由 offsetTop 属性指定的父坐标的计算顶端位置
event.clientX 相对文档的水平座标
event.clientY 相对文档的垂直座标
event.offsetX 相对容器的水平坐标
event.offsetY 相对容器的垂直坐标
document.documentElement.scrollTop 垂直方向滚动的值
event.clientX+document.documentElement.scrollTop 相对文档的水平座标+垂直方向滚动的量
以上主要指IE之中,FireFox差异如下:
IE6.0、FF1.06+:
clientWidth = width + padding
clientHeight = height + padding
offsetWidth = width + padding + border
offsetHeight = height + padding + border
IE5.0/5.5:
clientWidth = width - border
clientHeight = height - border
offsetWidth = width
offsetHeight = height
(需要提一下:CSS中的margin属性,与clientWidth、offsetWidth、clientHeight、offsetHeight均无关)
测试代码:
测试代码:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0
Transitional//EN"
"[url=http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd]http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd[/url]">
<html xmlns="[url=http://www.w3.org/1999/xhtml]http://www.w3.org/1999/xhtml[/url]" lang="gb2312">
<head>
<head>
<title> 代码实例:关于clientWidth、offsetWidth、clientHeight、offsetHeight的测试比较 </title>
<meta http-equiv="content-type" content="text/html; charset=gb2312" />
<meta name="author" content="枫岩,CnLei.y.l@gmail.com">
<meta name="copyright" content="[url=http://www.cnlei.com]http://www.cnlei.com[/url]" />
<meta name="description" content="关于clientWidth、offsetWidth、clientHeight、offsetHeight的测试比较" />
<style type="text/css" media="all">
body {font-size:14px;}
a,a:visited {color:#00f;}
#Div_CnLei {
width:300px;
height:200px;
padding:10px;
border:10px solid #ccc;
background:#eee;
font-size:12px;
}
#Div_CnLei p {margin:0;padding:10px;background:#fff;}
</style>
<script type="text/javascript">
function Obj(s){
return document.getElementById(s)?document.getElementById(s):s;
}
function GetClientWidth(o){
return Obj(o).clientWidth;
}
function GetClientHeight(o){
return Obj(o).clientHeight;
}
function GetOffsetWidth(o){
return Obj(o).offsetWidth;
}
function GetOffsetHeight(o){
return Obj(o).offsetHeight;
}
</script>
</head>
<body>
<p>点击下面的链接:</p>
<div id="Div_CnLei">
<p><a
href="javascript:alert(GetClientWidth('Div_CnLei'));">GetClientWidth();</a> <a
href="javascript:alert(GetClientHeight('Div_CnLei'));">GetClientHeight();</a></p>
<p><a
href="javascript:alert(GetOffsetWidth('Div_CnLei'));">GetOffsetWidth();</a> <a
href="javascript:alert(GetOffsetHeight('Div_CnLei'));">GetOffsetHeight();</a></p>
</div>
<div id="Description">
<p><strong>IE6.0、FF1.06+:</strong><br />
clientWidth = width + padding = 300+10×2 = 320<br />
clientHeight = height + padding = 200+10×2 = 220<br />
offsetWidth = width + padding + border = 300+10×2+10×2= 340<br />
offsetHeight = height + padding + border = 200+10×2+10×2 = 240</p>
<p><strong>IE5.0/5.5:</strong><br />
clientWidth = width - border = 300-10×2 = 280<br />
clientHeight = height - border = 200-10×2 = 180<br />
offsetWidth = width = 300<br />
offsetHeight = height = 200</p>
</div>
</body>
</html>
event.srcElement从字面上可以看出来有以下关键字:事件,源 他的意思就是:当前事件的源,
我们可以调用他的各种属性 就像:document.getElementById(”")这样的功能,
经常有人问 firefox 下的 event.srcElement 怎么用,在此详细说明:
IE下,event对象有srcElement属性,但是没有target属性;Firefox下,event对象有target属性,但是没有srcElement属性.但他们的作用是相当的,即:
firefox 下的 event.target = IE 下的 event.srcElement
解决方法:使用obj(obj = event.srcElement ? event.srcElement : event.target;)来代替IE下的event.srcElement或者Firefox下的event.target.
说明:如果你准备把本文中的命令放到配置文件(比如 .vimrc)中而不是在命令行执行,那么请去掉开头的冒号。
★ 键映射
:maptype key command
其中,key 是要映射的键(序列),command 是所要映射的命令,maptype 包括如下几种:
map 命令,可视和命令追加模式下的键映射
vmap 可视模式下的键映射
nmap 命令模式下的键映射
omap 命令追加模式下的键映射
map! 插入和命令行模式下的键映射
imap 插入模式下的键映射
cmap 命令行模式下的键映射
说明:命令追加模式指的是命令输入中的状态,也就是在输入一个需要多个按键的命令时,已经开始输入但未完成的状态。
举个例子,
:map <F2> gg
的意思是在命令,可视和命令追加模式把 F2 键映射为命令 gg,也就是说,当在这三种模式下时,按下 F2 键,就相当于按下了键序列 gg,其作用是定位到第一行。
要想避免 vim 把你映射的 command 中的内容再次映射的话,应该使用 noremap,其格式与 map 时相同。这时候,上面的各种 maptype 分别对应如下:
noremap 命令,可视和命令追加模式下的键映射(无二次映射的)
vnoremap 可视模式下的键映射(无二次映射的)
nnoremap 命令模式下的键映射(无二次映射的)
onoremap 命令追加模式下的键映射(无二次映射的)
noremap! 插入和命令行模式下的键映射(无二次映射的)
inoremap 插入模式下的键映射(无二次映射的)
cnoremap 命令行模式下的键映射(无二次映射的)
取消一个键映射用 unmap,格式为 unmap key。其中 key 为之前定义了映射的键,unmap 可以换为如下几种之一:
unmap 取消命令,可视和命令追加模式下的键映射
vunmap 取消可视模式下的键映射
nunmap 取消命令模式下的键映射
ounmap 取消命令追加模式下的键映射
unmap! 取消插入和命令行模式下的键映射
iunmap 取消插入模式下的键映射
cunmap 取消命令行模式下的键映射
★ 把当前目录设为正在编辑的文件所在的目录
:cd %:p:h
说明:只在类 Unix 操作系统下可用。
:lcd %:p:h
说明:在所有操作系统下可用。
★ 打开文件的时候自动将当前目录设为该文件所在的目录
在 .vimrc 中加入如下行:
:au BufEnter * :cd! %:p:h
★ 转换文件格式
由于 Unix、Window 和 MacOS 的换行符的内部表示不一样,因此有时候需要转换文件格式。
将文件格式转换为 Unix 格式:
:set fileformat=unix
将文件格式转换为 Windows 格式:
:set fileformat=dos
★ 让 gvim 启动时窗口最大化(只在 Windows 下可用)
在 _vimrc 中加入如下行
autocmd GUIEnter * simalt ~x
首先说说格式化日期。strtus2有提供一个<s:date/>来格式化日期,
例:<s:date value =Date" format="yyyy-MM-dd" />
这样可保证在不同的游览器中都显示为“2007-11-03”的格式。但这只能实现普通显示,如果要使用编辑组件呢?有两种简便方法:
1、使用struts2的dojo组件,<s:dateteimpicker/>
例:<s:datetimepicker name="Date" displayFormat="yyyy-MM-dd" />
2、使用JSTL
例:<s:textfield name="" value="${}" />
再说说格式化小数。Sturts2没有象JSTL一样提供一个<c:fmt>的格化式标签,所以要实现格式化比较麻烦一点。利用i18n与text来自定义实现小数格式化。
例:首先在class目录下创建一个Format.properties资源文件,
输入 FormatNumeral={0,number,##.000}
然后在页面引入这个定义。
<s:i18n name="Format">
<s:text name="FormatNumeral" >
<s:param value="aNumeral"/>
</s:text>
</s:i18n>
利用这个例子,还可以自定义多种格式化方式,相当灵活了。
- 更多的网页空间:除了使用F11通过全屏阅读外,你还可以通过缩小Firefox工具条图标的大小:右击工具条 -> 定制 -> 使用小图标。
- 快捷键:
- Ctrl + F (查找)
- Alt + N (查找下一个)
- Ctrl + D (加入书签)
- Ctrl + T (新标签页)
- Ctrl + K (到搜索栏)
- Ctrl + L (到地址栏)
- Ctrl + = (增大字体)
- Ctrl + - (缩小字体)
- Ctrl + W (关闭当前页)
- F5 (刷新)
- Alt-Home (主页)
- 自动完成:在地址栏(Ctrl + L),通过 Ctrl + Enter 自动加入“www.”和“.com”;通过 Shift + Enter 自动加入“www.”和“.net”
- 标签切换:与其用鼠标点击,不如用键盘来帮助你呀~
- Ctrl+Tab (从左往右切换标签)
- Ctrl+Shft+Tab (反方向切换标签)
- Ctrl+1-9 (切换到特定一个标签)
- 删除某一条URL记录:到地址栏(Ctrl + L),选择你想删除的地址,“Delete”就可以了。
- about:config:请参阅《About:config Tips and Screenshots》。
- 限制Firefox的内存使用率:在地址栏(Ctrl +
L)中输入about:config,找到“browser.cache”,然后选择“browser.cache.memory.capacity”。
原值为50000,但是你可以设置得更小。如果你的内存大小在512MB到1GB之间,建议设定为15000。
- 最大限度的减少内存使用率:Firefox最小化时可以减少内存占有率。在地址栏(Ctrl + L)中输入about:config,新建一个Boolean,为“config.trim_on_minimize”,然后设置为“true”。重新启动Firefox后才会有效果。
首先Spring 是一个框架,使用Spring并不代表代码质量的提高,就像盖房子选择用上海的地皮还是北京的地皮一样,房子质量与土地所在的城市无关,与房子的具体设计方案和选料有关。
使用Spring 等框架可以简化很多基础性的工作,配置好后可以方便构建业务应用。
框
架使用多了会有局限的感觉,像小鸟被套在笼子里,无法飞出去,虽然在笼子里面吃喝不愁。目前编程的门槛越来越低,诸多开源框架广泛传播,几乎没有什么技术
门槛,会配置就会编程,而一个好的DBA对软件性能会有很大提高,软件的核心逻辑最终会转移到对数据库的操作上,而且对目前从事的工作来讲,感觉技术的瓶
颈越来越多的局限在对数据库的操作上,下一步要认真提高下了。
Spring的优势不言而喻:
1. 提供了一种管理对象的方法,可以把中间层对象有效地组织起来。一个完美的框架“黏合剂”。
2. 采用了分层结构,可以增量引入到项目中。
3. 有利于面向接口编程习惯的养成。
4. 目的之一是为了写出易于测试的代码。
5. 非侵入性,应用程序对Spring API的依赖可以减至最小限度。
6. 一致的数据访问介面。
6. 一个轻量级的架构解决方案。
对Spring的理解
Spring致力于使用POJOs来构建应用程序。由框架提供应用程序的基础设施,将只含有业务逻辑的POJOs作为组件来管理。从而在应用程序中形成两条相对独立发展的平行线,并且在各自的抽象层面上延长了各自的生命周期。
Spring的工作基础是Ioc。Ioc将创建对象的职责从应用程序代码剥离到了框架中,通常2中注入方式:setter 和 ctor参数。
每个Bean定义被当作一个POJO(通过类名和JavaBean的初始属性或构造方法参数两种方式定义的Bean)。
Spring的核心在org.springframework.beans,更高抽象层面是BeanFactory. BeanFactory是一个非常轻量级的容器。
关于可维护性的思考
Spring之类的技术确实带来了应用系统的可维护性的提高吗?
Ioc, AOP之类的技术,本质上都是将原本位于应用程序代码中"硬编码"逻辑,剥离出来放到了配置文件中(或者其他形式)。主流声音都是认为提高了应用程序的可维护性。
但如果从以下方面观察,结合项目实际经验,个人感觉这些技术的应用大大降低了应用程序的可维护性,尤其是面对一个陌生的系统,或者项目人员变动频繁的时候。
1. 中断了应用程序的逻辑,使代码变得不完整,不直观。此时单从Source无法完全把握应用的所有行为。
2. 将原本应该代码化的逻辑配置化,增加了出错的机会以及额外的负担。
3. 时光倒退,失去了IDE的支持。在目前IDE功能日益强大的时代,以往代码重构等让人头痛的举动越来越容易。而且IDE还提供了诸多强大的辅助功能,使得编程的门槛降低很多。通常来说,维护代码要比维护配置文件,或者配置文件+代码的混合体要容易的多。
4. 调试阶段不直观,后期的bug对应阶段,不容易判断问题所在。
假设本地主机ip为172.16.1.1,远程的主机ip为172.16.1.2
第一步,在本地主机上的任意一个xterm中执行xhost,用来允许远程的其它主机可以和本地主机的X server联网:
xhost + 172.16.1.2
如果不指定任何ip地址,则表示权限完全放开,这会带来安全问题,要小心!
第二步,确认本地主机的xfs是运行的.用ps检查一下进程.
第三步,从本地主机(172.16.1.1)上通过网络登录到远程主机172.16.1.2上,你用telnet,ssh,rsh都可以.设置DISPLAY变量.
export DISPLAY=172.16.1.1:0
第四步,现在可以使用远程主机上的X 应用程序了.
这么样,很方便吧,但是你还不能掌控整个桌面环境,这个工作就交给vnc吧!Remote X 在局域网上运行效果很不错,普通的电话拨号就不用试了,速度太慢了.
我相信有不少人在
windows环境用过pcanywhere,但你想不想用一个免费的,可以在
linux,win9x/nt上都可以使用的pcanywhere,这就是vnc.
vnc就是vitual network computing的缩写,它支持许多操作平台,甚至可在浏览器中操作.
我主要介绍vncviewer的用法,以及用linux远程控制linux或nt.
vnc client通过架构在tcp/ip上的vnc协议与vnc server沟通,通过认证后,把X
server的桌面环境,输入设备,和X 资源交给vncserver掌控,vnc server将桌面环境通过vnc 协议送给vnc
client端.让vnc client来操纵vnc server桌面环境和输入设备.
首先下载到vnc的linux版本和windows版本.
当前的linux版本是vnc-3.3.3r1_x86_linux_2.0.tgz
当前的windows版本是vnc-3.3.3r7_x86_win32.zip
1.安装linux版的vnc
(1)安装
tar zxvf vnc-3.3.3r1_x86_linux_2.0.tgz
cd vnc_x86_linux_2.0
cp *vnc* /usr/local/bin/
mkdir /usr/local/vnc
cp -r classes/ /usr/local/vnc/
(2)设置vnc server的访问密码
vncpasswd
(3)启动vnc server
vncserver
注意运行后显示的信息,记下所用的端口号,一般从1开始,因为0被x server占用了.现在,你就能提供vnc服务了.vnc client的用法等会介绍.
2、安装nt版的vnc
1)安装
解开vnc-3.3.3r7_x86_win32.zip包后,会产生winvnc和vncviewer两个目录.winvnc目录中是vnc
server的安装程序,vncviewer目录中是vnc client的安装序.我只关心vnc
server,在winvnc目录中执行setup即可.
2)设置
首先执行install default registry settings.
run winvnc(app mode)就是执行vnc server
这时可看到winvnc运行的小图标,用鼠标右键点击图标,在properties/incoming connections中设定密码.默认配置即可.
现在,你的nt就能提供vnc服务了.
3、使用vncviewer
vnc server启动成功后,你就可用vncviewer来远程控制桌面了.
vncviewer xxx.xxx.xxx.xxx:display number
例如,vncviewer 172.16.1.2:1
按要求输入密码就可以看到远程的桌面了.
注意:viewers需要在16位色的显示模式下工作,如果您的操作系统中没上16位色,那么请您及时的调整您计算机的显示模式。不然vncviewer无法正常工作。
4、linux版vnc server的改进.
linux上的vnc server内定的桌面管理环境是twm,实在是太简陋了.
修改$HOME/.vnc/xstartup这个文件.
把所有内容的行前加上#,再在接尾部份加上:
startkde &
你当然可用你喜好的桌面代替.我这是用kde来代替twm,速度会慢少少,但用起来方便不少.
注意要重新启动vnc server.
5、通过浏览器使用vnc
通过浏览器使用vnc,要注意端口号的变化.
假设vnc server是172.16.1.2:1的话,那么,可用浏览器访问http://172.16.1.2:5801
端口号=display number + 5800
好了,心动不如行动,just do it !
If you've messed up the working tree, but haven't yet committed your
mistake, you can return the entire working tree to the last committed
state with
$ git reset --hard HEAD
If you make a commit that you later wish you hadn't, there are two fundamentally different ways to fix the problem:
-
You can create a new commit that undoes whatever was done by the old
commit. This is the correct thing if your mistake has already been made
public.
-
You can go back and modify the old commit. You should never do this
if you have already made the history public; git does not normally
expect the "history" of a project to change, and cannot correctly
perform repeated merges from a branch that has had its history changed.
Fixing a mistake with a new commit
Creating a new commit that reverts an earlier change is very easy; just pass the git revert command a reference to the bad commit; for example, to revert the most recent commit:
$ git revert HEAD
This will create a new commit which undoes the change in HEAD. You
will be given a chance to edit the commit message for the new commit.
You can also revert an earlier change, for example, the next-to-last:
$ git revert HEAD^
In this case git will attempt to undo the old change while leaving
intact any changes made since then. If more recent changes overlap with
the changes to be reverted, then you will be asked to fix conflicts
manually, just as in the case of <<resolving-a-merge, resolving a
merge>>.
The bash shell is just amazing. There are so many tasks that can be
simplified using its handy features. This tutorial tells about some of
those features, explains what exactly they do and learns you how to use
them.
Difficulty: Basic - Medium
Running a command from your history
Sometimes you know that you ran a command a while ago and you want
to run it again. You know a bit of the command, but you don't exactly
know all options, or when you executed the command. Of course, you
could just keep pressing the Up Arrow until you encounter the
command again, but there is a better way. You can search the bash
history in an interactive mode by pressing Ctrl + r. This
will put bash in history mode, allowing you to type a part of the
command you're looking for. In the meanwhile, it will show the most
recent occasion where the string you're typing was used. If it is
showing you a too recent command, you can go further back in history by
pressing Ctrl + r again and again. Once you found the command
you were looking for, press enter to run it. If you can't find what
you're looking for and you want to try it again or if you want to get
out of history mode for an other reason, just press Ctrl + c. By the way, Ctrl + c can be used in many other cases to cancel the current operation and/or start with a fresh new line.
Repeating an argument
You can repeat the last argument of the previous command in multiple ways. Have a look at this example:
[rechosen@localhost ~]$ mkdir /path/to/exampledir
[rechosen@localhost ~]$ cd !$
The second command might look a little strange, but it will just cd
to /path/to/exampledir. The "!$" syntax repeats the last argument of
the previous command. You can also insert the last argument of the
previous command on the fly, which enables you to edit it before
executing the command. The keyboard shortcut for this functionality is Esc + . (a period). You can also repeatedly press these keys to get the last argument of commands before the previous one.
Some keyboard shortcuts for editing
There are some pretty useful keyboard shortcuts for editing in bash. They might appear familiar to Emacs users:
- Ctrl + a => Return to the start of the command you're typing
- Ctrl + e => Go to the end of the command you're typing
- Ctrl + u => Cut everything before the cursor to a special clipboard
- Ctrl + k => Cut everything after the cursor to a special clipboard
- Ctrl + y => Paste from the special clipboard that Ctrl + u and Ctrl + k save their data to
- Ctrl + t => Swap the two characters before the cursor
(you can actually use this to transport a character from the left to
the right, try it!)
- Ctrl + w => Delete the word / argument left of the cursor
- Ctrl + l => Clear the screen
Dealing with jobs
If you've just started a huge process (like backupping a lot of
files) using an ssh terminal and you suddenly remember that you need to
do something else on the same server, you might want to get the huge
process to the background. You can do this by pressing Ctrl + z, which will suspend the process, and then executing the bg command:
[rechosen@localhost ~]$ bg
[1]+ hugeprocess &
This will make the huge process continue happily in the background,
allowing you to do what you need to do. If you want to background
another process with the huge one still running, just use the same
steps. And if you want to get a process back to the foreground again,
execute fg:
[rechosen@localhost ~]$ fg
hugeprocess
But what if you want to foreground an older process that's still
running? In a case like that, use the jobs command to see which
processes bash is managing:
[rechosen@localhost ~]$ jobs
[1]- Running hugeprocess &
[2]+ Running anotherprocess &
Note: A "+" after the job id means that that job is the 'current
job', the one that will be affected if bg or fg is executed without any
arguments. A "-" after the job id means that that job is the 'previous
job'. You can refer to the previous job with "%-".
Use the job id (the number on the left), preceded by a "%", to specify which process to foreground / background, like this:
[rechosen@localhost ~]$ fg %3
And:
[rechosen@localhost ~]$ bg %7
The above snippets would foreground job [3] and background job [7].
Using several ways of substitution
There are multiple ways to embed a command in an other one. You
could use the following way (which is called command substitution):
[rechosen@localhost ~]$ du -h -a -c $(find . -name *.conf 2>&-)
The above command is quite a mouthful of options and syntax, so I'll explain it.
- The du command calculates the actual size of files. The -h option
makes du print the sizes in human-readable format, the -a tells du to
calculate the size of all files, and the -c option tells du to produce
a grand total. So, "du -h -a -c" will show the sizes of all files
passed to it in a human-readable form and it will produce a grand total.
- As you might have guessed, "$(find . -name *.conf 2>&-)"
takes care of giving du some files to calculate the sizes of. This part
is wrapped between "$(" and ")" to tell bash that it should run the
command and return the command's output (in this case as an argument
for du). The find command searches for files named <can be
anything>.conf in the current directory and all accessible
subdirectories. The "." indicates the current directory, the -name
option allows to specify the filename of the file to search for, and
"*.conf" is an expression that matches any string ending with the
character sequence ".conf".
- The only thing left to explain is the "2>&-". This part of
the syntax makes bash discard the errors that find produces, so du
won't get any non-filename input. There is a huge amount of explanation
about this syntax near the end of the tutorial (look for "2>&1"
and further).
And there's another way to substitute, called process substitution:
[rechosen@localhost ~]$ diff <(ps axo comm) <(ssh user@host ps axo comm)
The command in the snippet above will compare the running processes
on the local system and a remote system with an ssh server. Let's have
a closer look at it:
- First of all, diff. The diff command can be used to compare two files. I won't tell much about it here, as there is an extensive tutorial about diff and patch on this site.
- Next, the "<(" and ")". These strings indicate that bash should
substitute the command between them as a process. This will create a
named pipe (usually in /dev/fd) that, in our case, will be given to
diff as a file to compare.
- Now the "ps axo comm". The ps command is used to list processes
currently running on the system. The "a" option tells ps to list all
processes with a tty, the "x" tells ps to list processes without a tty,
too, and "o comm" tells ps to list the commands only ("o" indicates the
starting of a user-defined output declaration, and "comm" indicates
that ps should print the COMMAND column).
- The "ssh user@host ps axo comm" will run "ps axo comm" on a remote
system with an ssh server. For more detailed information about ssh, see
this site's tutorial about ssh and scp.
Let's have a look at the whole snippet now:
- After interpreting the line, bash will run "ps axo comm" and redirect the output to a named pipe,
- then it will execute "ssh user@host ps axo comm" and redirect the output to another named pipe,
- and then it will execute diff with the filenames of the named pipes as argument.
- The diff command will read the output from the pipes and compare
them, and return the differences to the terminal so you can quickly see
what differences there are in running processes (if you're familiar
with diff's output, that is).
This way, you have done in one line what would normally require at least two: comparing the outputs of two processes.
And there even is another way, called xargs. This command can feed
arguments, usually imported through a pipe, to a command. See the next
chapter for more information about pipes. We'll now focus on xargs
itself. Have a look at this example:
[rechosen@localhost ~]$ find . -name *.conf -print0 | xargs -0 grep -l -Z mem_limit | xargs -0 -i cp {} {}.bak
Note: the "-l" after grep is an L, not an i.
The command in the snippet above will make a backup of all .conf
files in the current directory and accessible subdirectories that
contain the string "mem_limit".
- The find command is used to find all files in the current directory
(the ".") and accessible subdirectories with a filename (the "-name"
option) that ends with ".conf" ("*.conf" means
"<anything>.conf"). It returns a list of them, with null characters as separators ("-print0" tells find to do so).
- The output of find is piped (the "|" operator, see the next chapter
for more information) to xargs. The "-0" option tells xargs that the
names are separated by null characters, and "grep -l -Z
mem_limit" is the command that the list of files will be feeded to as
arguments. The grep command will search the files it gets from xargs
for the string "mem_limit", returning a list of files (the -l option
tells grep not to return the contents of the files, but just the
filenames), again separated by null characters (the "-Z" option causes grep to do this).
- The output of grep is also piped, to "xargs -0 -i cp {} {}.bak". We
know what xargs does, except for the "-i" option. The "-i" option tells
xargs to replace every occasion of the specified string with the
argument it gets through the pipe. If no string is specified (as in our
case), xargs will assume that it should replace the string "{}". Next,
the "cp {} {}.bak". The "{}" will be replaced by xargs with the
argument, so, if xargs got the file "sample.conf" through the pipe, cp
will copy the file "sample.conf" to the file "sample.conf.bak",
effectively making a backup of it.
These substitutions can, once mastered, provide short and quick solutions for complicated problems.
Piping data through commands
One of the most powerful features is the ability to pipe data
through commands. You could see this as letting bash take the output of
a command, then feed it to an other command, take the output of that,
feed it to another and so on. This is a simple example of using a pipe:
[rechosen@localhost ~]$ ps aux | grep init
If you don't know the commands yet: "ps aux" lists all processes
executed by a valid user that are currently running on your system (the
"a" means that processes of other users than the current user should
also be listed, the "u" means that only processes executed by a valid
user should be shown, and the "x" means that background processes
(without a tty) should also be listed). The "grep init" searches the
output of "ps aux" for the string "init". It does so because bash pipes
the output of "ps aux" to "grep init", and bash does that because of
the "|" operator.
The "|" operator makes bash redirect all data that the command left
of it returns to the stdout (more about that later) to the stdin of the
command right of it. There are a lot of commands that support taking
data from the stdin, and almost every program supports returning data
using the stdout.
The stdin and stdout are part of the standard streams; they were
introduced with UNIX and are channels over which data can be
transported. There are three standard streams (the third one is stderr,
which should be used to report errors over). The stdin channel can be
used by other programs to feed data to a running process, and the
stdout channel can be used by a program to export data. Usually, stdout
output (and stderr output, too) is received by the terminal environment
in which the program is running, in our case bash. By default, bash
will show you the output by echoing it onto the terminal screen, but
now that we pipe it to an other command, we are not shown the data.
Please note that, as in a pipe only the stdout of the command on the
left is passed on to the next one, the stderr output will still go to
the terminal. I will explain how to alter this further on in this
tutorial.
If you want to see the data that's passed on between programs in a
pipe, you can insert the "tee" command between it. This program
receives data from the stdin and then writes it to a file, while also
passing it on again through the stdout. This way, if something is going
wrong in a pipe sequence, you can see what data was passing through the
pipes. The "tee" command is used like this:
[rechosen@localhost ~]$ ps aux | tee filename | grep init
The "grep" command will still receive the output of "ps aux", as tee
just passes the data on, but you will be able to read the output of "ps
aux" in the file <filename> after the commands have been
executed. Note that "tee" tries to replace the file <filename> if
you specify the command like this. If you don't want "tee" to replace
the file but to append the data to it instead, use the -a option, like
this:
[rechosen@localhost ~]$ ps aux | tee -a filename | grep init
As you have been able to see in the above command, you can place a
lot of command with pipes after each other. This is not infinite,
though. There is a maximum command-line length, which is usually
determined by the kernel. However, this value usually is so big that
you are very unlikely to hit the limit. If you do, you can always save
the stdout output to a file somewhere inbetween and then use that file
to continue operation. And that introduces the next subject: saving the
stdout output to a file.
Saving the stdout output to a file
You can save the stdout output of a command to a file like this:
[rechosen@localhost ~]$ ps aux > filename
The above syntax will make bash write the stdout output of "ps aux"
to the file filename. If filename already exists, bash will try to
overwrite it. If you don't want bash to do so, but to append the output
of "ps aux" to filename, you could do that this way:
[rechosen@localhost ~]$ ps aux >> filename
You can use this feature of bash to split a long line of pipes into multiple lines:
[rechosen@localhost ~]$ command1 | command2 | ... | commandN > tempfile1
[rechosen@localhost ~]$ cat tempfile1 | command1 | command2 | ... | commandN > tempfile2
And so on. Note that the above use of cat is, in most cases, a
useless one. In many cases, you can let command1 in the second snippet
read the file, like this:
[rechosen@localhost ~]$ command1 tempfile1 | command2 | ... | commandN > tempfile2
And in other cases, you can use a redirect to feed a file to command1:
[rechosen@localhost ~]$ command1 < tempfile1 | command2 | ... | commandN > tempfile2
To be honest, I mainly included this to avoid getting the Useless Use of Cat Award =).
Anyway, you can also use bash's ability to write streams to file for
logging the output of script commands, for example. By the way, did you
know that bash can also write the stderr output to a file, or both the
stdout and the stderr streams?
Playing with standard streams: redirecting and combining
The bash shell allows us to redirect streams to other streams or to
files. This is quite a complicated feature, so I'll try to explain it
as clearly as possible. Redirecting a stream is done like this:
[rechosen@localhost ~]$ ps aux 2>&1 | grep init
In the snippet above, "grep init" will not only search the stdout
output of "ps aux", but also the stderr output. The stderr and the
stdout streams are combined. This is caused by that strange
"2>&1" after "ps aux". Let's have a closer look at that.
First, the "2". As said, there are three standard streams (stin,
stdout and stderr).These standard streams also have default numbers:
- 0: stdin
- 1: stdout
- 2: sterr
As you can see, "2" is the stream number of stderr. And ">", we
already know that from making bash write to a file. The actual meaning
of this symbol is "redirect the stream on the left to the stream on the
right". If there is no stream on the left, bash will assume you're
trying to redirect stdout. If there's a filename on the right, bash
will redirect the stream on the left to that file, so that everything
passing through the pipe is written to the file.
Note: the ">" symbol is used with and without a space behind
it in this tutorial. This is only to keep it clear whether we're
redirecting to a file or to a stream: in reality, when dealing with
streams, it doesn't matter whether a space is behind it or not. When
substituting processes, you shouldn't use any spaces.
Back to our "2>&1". As explained, "2" is the stream number of
stderr, ">" redirects the stream somewhere, but what is "&1"?
You might have guessed, as the "grep init" command mentioned above
searches both the stdout and stderr stream, that "&1" is the stdout
stream. The "&" in front of it tells bash that you don't mean a
file with filename "1". The streams are sent to the same destination,
and to the command receiving them it will seem like they are combined.
If you'd want to write to a file with the name "&1", you'd have to escape the "&", like this:
[rechosen@localhost ~]$ ps aux > \&1
Or you could put "&1" between single quotes, like this:
[rechosen@localhost ~]$ ps aux > '&1'
Wrapping a filename containing problematic characters between single
quotes generally is a good way to stop bash from messing with it
(unless there are single quotes in the string, then you'd have have
escape them by putting a \ in front of them).
Back again to the "2>&1". Now that we know what it means, we can also apply it in other ways, like this:
[rechosen@localhost ~]$ ps aux > filename 2>&1
The stdout output of ps aux will be sent to the file filename, and
the stderr output, too. Now, this might seem unlogical. If bash would
interpret it from the left to the right (and it does), you might think
that it should be like:
[rechosen@localhost ~]$ ps aux 2>&1 > filename
Well, it shouldn't. If you'd execute the above syntax, the stderr
output would just be echoed to the terminal. Why? Because bash does not
redirect to a stream, but to the current final destination of the
stream. Let me explain it:
- First, we're telling bash to run the command "ps" with "aux" as an argument.
- Then, we're telling to redirect stderr to stdout. At the moment,
stdout is still going to the terminal, so the stderr output of "ps aux"
is sent to the terminal.
- After that, we're telling bash to redirect the stdout output to the
file filename. The stdout output of "ps aux" is sent to this file
indeed, but the stderr output isn't: it is not affected by stream 1.
If we put the redirections the other way around ("> filename" first), it does work. I'll explain that, too:
- First, we're telling bash to run the command "ps" with "aux" as an argument (again).
- Then, we're redirecting the stdout to the file filename. This causes the stdout output of "ps aux" to be written to that file.
- After that, we're redirecting the stderr stream to the stdout
stream. The stdout stream is still pointing to the file filename
because of the former statement. Therefore, stderr output is also
written to the file.
Get it? The redirects cause a stream to go to the same final
destination as the specified one. It does not actually merge the
streams, however.
Now that we know how to redirect, we can use it in many ways. For example, we could pipe the stderr output instead of the stdout output:
[rechosen@localhost ~]$ ps aux 2>&1 > /dev/null | grep init
The syntax in this snippet will send the stderr output of "ps aux"
to "grep init", while the stdout output is sent to /dev/null and
therefore discarded. Note that "grep init" will probably not find
anything in this case as "ps aux" is unlikely to report any errors.
When looking more closely to the snippet above, a problem arises. As
bash reads the command statements from the left to the right, nothing
should go through the pipe, you might say. At the moment that
"2>&1" is specified, stdout should still point to the terminal,
shouldn't it? Well, here's a thing you should remember: bash reads
command statements from the left to the right, but, before that,
determines if there are multiple command statements and in which way
they are separated. Therefore, bash already read and applied the
"|" pipe symbol and stdout is already pointing to the pipe. Note that
this also means that stream redirections must be specified before the
pipe operator. If you, for example, would move "2>&1" to the end
of the command, after "grep init", it would not affect ps aux anymore.
We can also swap the stdout and the stderr stream. This allows to
let the stderr stream pass through a pipe while the stdout is printed
to the terminal. This will require a 3rd stream. Let's have a look at
this example:
[rechosen@localhost ~]$ ps aux 3>&1 1>&2 2>&3 | grep init
That stuff seems to be quite complicated, right? Let's analyze what we're doing here:
- "3>&1" => We're redirecting stream 3 to the same final
destination as stream 1 (stdout). Stream 3 is a non-standard stream,
but it is pretty much always available in bash. This way, we're
effectively making a backup of the destination of stdout, which is, in
this case, the pipe.
- "1>&2" => We're redirecting stream 1 (stdout) to the same
final destination as stream 2 (stderr). This destination is the
terminal.
- "2>&3" => We're redirecting stream 2 (stderr) to the
final destination of stream 3. In the first step of these three ones,
we set stream 3 to the same final destination as stream 1 (stdout),
which was the pipe at that moment, and after that, we redirected stream
1 (stdout) to the final destination of stream 2 at that moment, the
terminal. If we wouldn't have made a backup of stream 1's final
destination in the beginning, we would not be able to refer to it now.
So, by using a backup stream, we can swap the stdout and stderr
stream. This backup stream does not belong to the standard streams, but
it is pretty much always available in bash. If you're using it in a
script, make sure you aren't breaking an earlier command by playing
with the 3rd stream. You can also use stream 4, 5, 6 7 and so on if you
need more streams. The highest stream number usually is 1023 (there are
1024 streams, but the first stream is stream 0, stdin). This may be
different on other linux systems. Your mileage may vary. If you try to
use a non-existing stream, you will get an error like this:
bash: 1: Bad file descriptor
If you want to return a non-standard stream to it's default state, redirect it to "&-", like this:
[rechosen@localhost ~]$ ps aux 3>&1 1>&2 2>&3 3>&- | grep init
Note that the stream redirections are always reset to their initial
state if you're using them in a command. You'll only need to do this
manually if you made a redirect using, for example, exec, as redirects
made this way will last until changed manually.
Final words
Well, I hope you learned a lot from this tutorial. If the things you
read here were new for you, don't worry if you can't apply them
immediately. It already is useful if you just know what a statement
means if you stumble upon it sometime. If you liked this, please help
spreading the word about this blog by posting a link to it here and
there. Thank you for reading, and good luck working with bash!
aptitude 与 apt-get 一样,是 Debian 及其衍生系统中功能极其强大的包管理工具。与 apt-get
不同的是,aptitude 在处理依赖问题上更佳一些。举例来说,aptitude
在删除一个包时,会同时删除本身所依赖的包。这样,系统中不会残留无用的包,整个系统更为干净。以下是笔者总结的一些常用 aptitude
命令,仅供参考。
命令 |
作用 |
aptitude update |
更新可用的包列表 |
aptitude upgrade |
升级可用的包 |
aptitude dist-upgrade |
将系统升级到新的发行版 |
aptitude install pkgname |
安装包 |
aptitude remove pkgname |
删除包 |
aptitude purge pkgname |
删除包及其配置文件 |
aptitude search string |
搜索包 |
aptitude show pkgname |
显示包的详细信息 |
aptitude clean |
删除下载的包文件 |
aptitude autoclean |
仅删除过期的包文件 |
当然,你也可以在文本界面模式中使用 aptitude。
POSIX标准定义的头文件
<dirent.h> 目录项
<fcntl.h> 文件控制
<fnmatch.h> 文件名匹配类型
<glob.h> 路径名模式匹配类型
<grp.h> 组文件
<netdb.h> 网络数据库操作
<pwd.h> 口令文件
<regex.h> 正则表达式
<tar.h> TAR归档值
<termios.h> 终端I/O
<unistd.h> 符号常量
<utime.h> 文件时间
<wordexp.h> 字符扩展类型
-------------------------
<arpa/inet.h> INTERNET定义
<net/if.h> 套接字本地接口
<netinet/in.h> INTERNET地址族
<netinet/tcp.h> 传输控制协议定义
-------------------------
<sys/mman.h> 内存管理声明
<sys/select.h> Select函数
<sys/socket.h> 套接字借口
<sys/stat.h> 文件状态
<sys/times.h> 进程时间
<sys/types.h> 基本系统数据类型
<sys/un.h> UNIX域套接字定义
<sys/utsname.h> 系统名
<sys/wait.h> 进程控制
------------------------------
POSIX定义的XSI扩展头文件
<cpio.h> cpio归档值
<dlfcn.h> 动态链接
<fmtmsg.h> 消息显示结构
ftw.h> 文件树漫游
<iconv.h> 代码集转换使用程序
<langinfo.h> 语言信息常量
<libgen.h> 模式匹配函数定义
<monetary.h> 货币类型
<ndbm.h> 数据库操作
<nl_types.h> 消息类别
<poll.h> 轮询函数
<search.h> 搜索表
<strings.h> 字符串操作
<syslog.h> 系统出错日志记录
<ucontext.h> 用户上下文
<ulimit.h> 用户限制
<utmpx.h> 用户帐户数据库
-----------------------------
<sys/ipc.h> IPC(命名管道)
<sys/msg.h> 消息队列
<sys/resource.h>资源操作
<sys/sem.h> 信号量
<sys/shm.h> 共享存储
<sys/statvfs.h> 文件系统信息
<sys/time.h> 时间类型
<sys/timeb.h> 附加的日期和时间定义
<sys/uio.h> 矢量I/O操作
------------------------------
POSIX定义的可选头文件
<aio.h> 异步I/O
<mqueue.h> 消息队列
<pthread.h> 线程
<sched.h> 执行调度
<semaphore.h> 信号量
<spawn.h> 实时spawn接口
<stropts.h> XSI STREAMS接口
<trace.h> 事件跟踪
什么是HTTP Referer
简言之,HTTP
Referer是header的一部分,当浏览器向web服务器发送请求的时候,一般会带上Referer,告诉服务器我是从哪个页面链接过来的,服务器
籍此可以获得一些信息用于处理。比如从我主页上链接到一个朋友那里,他的服务器就能够从HTTP
Referer中统计出每天有多少用户点击我主页上的链接访问他的网站。
Referer其实应该是英文单词Referrer,不过拼错的人太多了,所以编写标准的人也就将错就错了。
我的问题
我刚刚把feed阅读器改变为Gregarius,但他不像我以前用的liferea,访问新浪博客的时候,无法显示其中的图片,提示“此图片仅限于新浪博客用户交流与沟通”,我知道,这就是HTTP Referer导致的。
由于我上网客户端配置的特殊性,首先怀疑是squid的问题,但通过实验排除了,不过同时发现了一个Squid和Tor、Privoxy协同使用的隐私泄露问题,留待以后研究。
Gregarius能处理这个问题么?
答案是否定的,因为Gregarius只是负责输出html代码,而对图像的访问是有客户端浏览器向服务器请求的。
不过,安装个firefox扩展也许能解决问题,文中推荐的”Send Referrer”我没有找到,但发现另外一个可用的:”RefControl“,可以根据访问网站的不同,控制使用不同的Referer。
但是我不喜欢用Firefox扩展来解决问题,因为我觉得他效率太低,所以我用更好的方式——Privoxy。
Privoxy真棒
在Privoxy的default.action中添加两行:
{+hide-referrer{forge}}
.album.sina.com.cn
这样Gregarius中新浪博客的图片就出来了吧?+hide-referrer
是Privoxy的一个过滤器,设置访问时对HTTP Referer的处理方式,后面的forge
代表用访问地址当作Refere的,还可以换成block
,代表取消Referer,或者直接把需要用的Referer网址写在这里。
用Privoxy比用Firefox简单的多,赶紧换吧。
From https to http
我还发现,从一个https页面上的链接访问到一个非加密的http页面的时候,在http页面上是检查不到HTTP Referer的,比如当我点击自己的https页面下面的w3c xhtml验证图标(网址为http://validator.w3.org/check?uri=referer),从来都无法完成校验,提示:
No Referer header found!
原来,在http协议的rfc文档中有定义:
15.1.3 Encoding Sensitive Information in URI's
...
Clients SHOULD NOT include a Referer header field in a (non-secure)
HTTP request if the referring page was transferred with a secure
protocol.
这样是出于安全的考虑,访问非加密页时,如果来源是加密页,客户端不发送Referer,IE一直都是这样实现的,Firefox浏览器也不例外。但这并不影响从加密页到加密页的访问。
Firefox中关于Referer的设置
都在里,有两个键值:
-
network.http.sendRefererHeader (default=2)
设置Referer的发送方式,0为完全不发送,1为只在点击链接时发送,在访问页面中的图像什么的时候不发送,2为始终发送。参见Privacy Tip #3: Block Referer Headers in Firefox
-
network.http.sendSecureXSiteReferrer (default=true)
设置从一个加密页访问到另外一个加密页的时候是否发送Referer,true为发送,false为不发送。
利用Referer防止图片盗链
虽然Referer并不可靠,但用来防止图片盗链还是足够的,毕竟不是每个人都会修改客户端的配置。实现一般都是通过apache的配置文件,首先设置允许访问的地址,标记下来:
# 只允许来自domain.com的访问,图片可能就放置在domain.com网站的页面上
SetEnvIfNoCase Referer "^http://www.domain.com/" local_ref
# 直接通过地址访问
SetEnvIf Referer "^$" local_ref
然后再规定被标记了的访问才被允许:
<FilesMatch ".(gif|jpg)">
Order Allow,Deny
Allow from env=local_ref
</FilesMatch>
或者
<Directory /web/images>
Order Deny,Allow
Deny from all
Allow from env=local_ref
</Directory>
这方面的文章网上很多,参考:
不要使用Rerferer的地方
不要把Rerferer用在身份验证或者其他非常重要的检查上,因为Rerferer非常容易在客户端被改变,不管是通过上面介绍的Firefox扩展,或者是Privoxy,甚至是libcurl的调用,所以Rerferer数据非常之不可信。
如果你想限制用户必须从某个入口页面访问的话,与其使用Referer,不如使用session,在入口页面写入session,然后在其他页面检查,如果用户没有访问过入口页面,那么对应的session就不存在,参见这里的讨论。不过和上面说的一样,也不要过于相信这种方式的“验证”结果。
个人感觉现在Rerferer除了用在防盗链,其他用途最多的就是访问统计,比如统计用户都是从哪里的链接访问过来的等等。
1 变量替换
public static void fun1() {
StringTemplate st = new StringTemplate(
"对象变量替换 姓名:$KeyList.Name$, 年龄:$KeyList.Age$ ");
HashMap ht = new HashMap();
ht.put("Name", "李四");
ht.put("Age", "35");
st.setAttribute("KeyList", ht);
System.out.print(st.toString());
}
2 自定义集合替换
public static void fun2() {
StringTemplate st = new StringTemplate(
"自定义集合替换 $List:{姓名:$it.Name$ 年龄:$it.Age$}$");
st.setAttribute("List.{Name,Age}", "王二", "29");
System.out.print(st.toString());
}
3 对象变量替换
定义类
private static class User {
String name;
String age;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getAge() {
return age;
}
public void setAge(String age) {
this.age = age;
}
}
public static void fun3() {
User us = new User();
us.name = "张三";
us.age = "23";
List<User> uss = new ArrayList<User>();
uss.add(us);
uss.add(us);
uss.add(us);
uss.add(us);
uss.add(us);
StringTemplate st = new StringTemplate(
"<table>$Item:{<tr
class=black><td>$it.name$</td></tr>},{<tr
class=red><td>$it.age$</td></tr>};separator=\"\n\"$</table>");
st.setAttribute("Item", uss);
System.out.print(st.toString());
}
交叉替换
public static void fun4() {
User us = new User();
us.name = "张三";
us.age = "23";
List<User> uss = new ArrayList<User>();
uss.add(us);
uss.add(us);
uss.add(us);
uss.add(us);
uss.add(us);
StringTemplateGroup sg = new StringTemplateGroup("GroupTest");
sg.defineTemplate("RowRed",
"<tr class=red><td>$it.Name$</td><td>$it.Age$</td></tr>\n");
sg.defineTemplate("Rowblack",
"<tr class=black><td>$it.Name$</td><td>$it.Age$</td></tr>\n");
StringTemplate st = sg.defineTemplate("List",
"<table>$Item:RowRed(),Rowblack()$</table>");
st.setAttribute("Item", uss);
System.out.print(st.toString());
}
4 条件判断
public static void fun5() {
StringTemplate st = new StringTemplate(
"当前用户登陆状态: $if(IsAdmin)$ 用户登陆成功! $else$ 用户没有登陆! $endif$");
st.setAttribute("IsAdmin", true);
System.out.print(st.toString());
}
5 sql语句实现
theQuery.st内容
SELECT $column; separator=","$ FROM $table$;
public static void fun6() {
// 一个sql语句的实现
StringTemplateGroup group = new StringTemplateGroup("myGroup", "c:/");
StringTemplate query = group.getInstanceOf("theQuery");
query.setAttribute("column", "name");
query.setAttribute("column", "email");
query.setAttribute("table", "User");
System.out.print(query.toString());
}
6 使用AttributeRenderer
private static class DateRenderer implements AttributeRenderer {
public String toString(Object o) {
SimpleDateFormat f = new SimpleDateFormat("yyyy.MM.dd");
return f.format(((Calendar) o).getTime());
}
}
public static void fun7() {
StringTemplate st = new StringTemplate("date: <created>",
AngleBracketTemplateLexer.class);
st.setAttribute("created", new GregorianCalendar(2005, 07 - 1, 05));
st.registerRenderer(GregorianCalendar.class, new DateRenderer());
String result = st.toString();
System.out.print(result.toString());
}
在Linux上习惯了自动补全功能,在Windows上还真有些不习惯,好在微软也体谅我们大家,通过修改注册表可以实现此功能。HKEY_LOCAL_MACHINE-->Software-->Microsoft-->Command Prosessor-->PathCompletionChar的键值改为9(16进制)后,在Dos窗口中就可以使用自动补齐功能了。
1.抽象:
抽象就是忽略一个主题中与当前目标无关的那些方面,以便更充分地注意与当前目标有关的方面。抽象并不打算了解全部问题,而只是选择其中的一部分,暂时不用部分细节。抽象包括两个方面,一是过程抽象,二是数据抽象。
2.继承:
继
承是一种联结类的层次模型,并且允许和鼓励类的重用,它提供了一种明确表述共性的方法。对象的一个新类可以从现有的类中派生,这个过程称为类继承。新类继
承了原始类的特性,新类称为原始类的派生类(子类),而原始类称为新类的基类(父类)。派生类可以从它的基类那里继承方法和实例变量,并且类可以修改或增
加新的方法使之更适合特殊的需要。
3.封装:
封装是把过程和数据包围起来,对数据的访问只能通过已定义的界面。面向对象计算始于这个基本概念,即现实世界可以被描绘成一系列完全自治、封装的对象,这些对象通过一个受保护的接口访问其他对象。
4.多态性:
多态性是指允许不同类的对象对同一消息作出响应。多态性包括参数化多态性和包含多态性。多态性语言具有灵活、抽象、行为共享、代码共享的优势,很好的解决了应用程序函数同名问题。
Oracle数据库由
数据文件,控制文件和联机日志文件三种文件组成。
由于磁盘空间的变化,或者基于数据库磁盘I/O性能的调整等,我們可能会考虑移动数据库文件。(
注:恢复数据库时非常有用,属于冷备份)
查询当前数据库中,相关文件路径
select * from v$datafile;
select * from v$controlfile;
select * from v$logfile;
根据以上路径,找到当前数据库相应文件路径。
一.移动数据文件:
可以用ALTER DATABASE,ALTER TABLESPACE两种方法移动数据文件。
1. ALTER DATABASE方法;
-- 用此方法,可以移动任何表空间的数据文件。
STEP 1. 停数据库:
sqlplus /nolog
SQL> CONNECT INTERNAL;
SQL> SHUTDOWN;
SQL> EXIT;
STEP 2.用操作系统命令移动数据文件:
将数据文件 'test.dbf' 从/ora/oracle/data1目录移动到/ora/oracle/data2目录下:
mv /ora/oracle/data1/test.dbf /ora/oracle/data2
STEP 3. Mount数据库,用ALTER DATABASE命令将数据文件改名:
sqlplus /nolog
SQL> CONNECT INTERNAL;
SQL> STARTUP MOUNT;
SQL> ALTER DATABASE RENAME FILE '/ora/oracle/data1/test.dbf' TO '/ora/oracle/data2/test.dbf';
STEP 4. 打开数据库:.
SQL> ALTER DATABASE OPEN;
SQL>SELECT NAME,STATUS FROM V$DATAFILE;
2. ALTER TABLESPACE方法:
用此方法,要求此数据文件既不属于SYSTEM表空间,也不属于含有ACTIVE回滚段或临时段的表空间。觉得麻烦,省略不用了。
二. 移动控制文件:
控制文件 在 INIT.ORA文件中指定。移动控制文件相对比较简单,下数据库,
编辑INIT.ORA,移动控制文件,重启动数据库。
STEP 1. 停数据库:
sqlplus /nolog
SQL> CONNECT INTERNAL;
SQL> SHUTDOWN;
SQL> EXIT;
STEP 2.用操作系统命令 移动控制文件:
--将控制文件'ctl3orcl.ctl' 从/ora/oracle/data1目录移动到/ora/oracle/data2目录下:
mv /ora/oracle/data1/ctl3orcl.ctl /ora/oracle/data2
STEP 3. 编辑INIT.ORA文件:
INIT.ORA文件的在ORACLE_HOME/dbs目录下,
修改参数 "control_files",其中指定移动后的控制文件:
control_files = (/ora/oracle/data1/ctrlorcl1.ctl,/ora/oracle/data1/ctrlorcl2.ctl,/ora/oracle/data2/ctrlorcl3.ctl)
STEP 4. 重启动数据库:
sqlplus /nolog
SQL> CONNECT INTERNAL;
SQL> STARTUP;
SQL>SELECT name FROM V$CONTROLFILE;
SQL> EXIT;
三. 移动联机日志文件:
STEP 1. 停数据库:
sqlplus /nolog
SQL> CONNECT INTERNAL;
SQL> SHUTDOWN;
SQL> EXIT;
STEP 2. 用操作系统命令移动联机日志文件:
--将联机日志文件'redolog1.log' 从/ora/oracle/data1目录移动到/ora/oracle/data2目录下:
mv /ora/oracle/data1/redolog1.log /ora/oracle/data2
STEP 3. Mount数据库,用ALTER DATABASE 命令改联机日志文件名:.
sqlplus /nolog
SQL> CONNECT INTERNAL;
SQL> STARTUP MOUNT ;
SQL> ALTER DATABASE RENAME FILE '/ora/oracle/data1/redolog1.log' TO '/ora/oracle/data2/redolog1.log';
STEP 4.重启动数据库: .
SQL> ALTER DATABASE OPEN;
SQL>SELECT MEMBER FROM V$LOGFILE;
最近接触Jakarta-Common-BeanUtils这个东东比较 多,于是对Apache Jakarta
Project产生了兴趣,上他们的官方网站上看了下感觉有用的东西好多,眼花缭乱的,又没有中文网站,又上各大论坛搜了下,也没有发现一个集中该项目的
资料,于是决定自己整理翻译一下,有助于各位网友更好的了解该项目。如果有什么描述不对不全面的地方,希望各位提出来给大家分享。
Jakarta项目是ASF(The Apache Software
Foundation)的一部分。ASF是一个非赢利组织,她鼓励基于开放的软件许可下进行合作、注重实效的开发,并提供各个领域的高质量软件,她涉及到
Http服务器,编译工具,类库,开发架构,服务器端Java技术,J2EE容器,数据库工具,日志工具,XML解析等等诸多领域。ASF提供的java
项目有一部分在Jakarta中,还有一些成为独立的诸如Tomcat的项目,Jakarta项目则提供了多种多样开源的java解决通用方案。
先介绍一下ASF中和Jakarta项目并列的java项目:
Ant ——java构建工具,使用一个配置文件就可以完成java的项目的,编译、打包、测试、运行、部署、生成文档等诸多工作。
Avalon ——是一个包括核心框架、工具、组件和容器的面向组件编程(AOP)的完整开发平台。使用关键设计模式,如反向控制模式(IoC)和分离考虑模(SoC)。 Avalon提供各种基本组件和缺省的应用程序模块,帮助你快速的建立你自己的应用程序解决方案。
Excalibur ——集多个开源项目(Avalon Framework、LogKit和Cornerstone)于一身的轻量级可嵌入式反向控制容器。
Gump ——是Apache组织自己也使用的一个持续集成工具,全面支持Ant和Maven,当有新的改动提交到版本控制系统,它可以检查出潜在的冲突,并及时通知项目组的所有成员并自动生成改动的详细报告。
James ——是一套用java开发的邮件、新闻组、消息服务器,提供了比较完善的配置方案,尤其是关于邮件内容存储和用户信息存储。 支持 SMTP, POP3 , NNTP , IMAP 。
Logging ——可靠,快速的日志工具。
Lucene
——Java开发的高性能,全方位的文本搜索引擎。替文件的每一个字作索引,索引让搜寻的效率比传统的逐字比较大大提高,
Lucen提供一组解读,过滤,分析文件,编排和使用索引的API,它的强大之处除了高效和简单外,是最重要的是使使用者可以随时应自已需要自订其功能。
Maven ——是一个潜在的基于java的apache ant的构建工具的替代者。提供了更强大的功能和易用性。
Portals
——提供了功能全面的、具有商业价值的企业门户。门户概念:门户对企业内的各种资源如信息管理系统进行整合并通过单一接口对外提供服务,雇员、合伙人及顾
客可以通过任何装置在任何地点通过门户入口享受到企业提供的服务,分析家们预计,门户将成为下一代的桌面环境。
Struts ——一个实现MVC model2的web应用程序开发框架。通过一个配置文件可以很好的对各种组件进行装配,结构清晰,应用的最广泛的额web开发框架。
Tapestry——类似 Struts,也是一个基于servlet的应用程序框架,支持MVC,出现的较晚,不如Struts普及,主要利用javabean和xml技术进行开发,
Tomcat ——Serlvet容器,同时具有传统的Web服务器的功能,如:处理Html页面。能够处理动静态网页。
Watchdog ——用来检查一个servlet或jsp容器在相应规范上的执行情况的兼容性。但是该项目已经静止了,只支持Servlet2.3、JSP1.2,相应的Serlvet容器如Tomcat也只支持Tomcat4.x。
下面介绍下Jakarta的各个子项目:
Alexandria——已经不再开发
BCEL ——The Byte Code Engineering Library
(formerly known as JavaClass) 字节码引擎类库,它让用户方便的分析,创建,操作java的class文件成为可能。
它可以让您深入 JVM 汇编语言进行类操作的细节。
BSF ——bean脚本框架 在java应用内对脚本语言提供了支持,通过脚本语言可以访问java的对象和方法。
Cactus ——一个基于JUnit框架的简单测试框架,用来单元测试服务端Java代码。 Cactus单元测试服务端内容包括servlet,EJB, tag librarie, filter等
Commons ——提供很多日常开发中使用率很高的功能解决方案,已被很多著名的开源项目采用。具体的项目列表后面会有。
ECS ——使用Java语言和面向对象方法创建标记语言文档(HTML,XML)的开源项目。
HiveMind ——是一个服务和配置的微型内核, 一套通过简单的java对象和接口创建复杂应用的框架。
HttpComponents ——提供了java.net包所不能提供的更多强大的、方便的http协议处理功能。
JCS ——一个分布式的缓存系统,用来提高应用程序的性能,并且提供很多强大的额外功能。
JMeter
——纯java开发的一套桌面应用程序。用来进行功能测试和性能测试。它可以用来测试静止资料库或者活动资料库中的服务器的运行情况,可以用来模拟对服务
器或者网络系统加以重负荷以测试它的抵抗,或者用来分析不同负荷类型下的所有运行情况。它也提供了一个可替换的界面用来定制数据显示,测试同步及测试的创
建和执行。
ORO ——一套文本处理工具,能提供perl5.0兼容的正则表达式、 AWK-like正则表达式, glob 表达式。还提供替换,分割,文件名过虑等功能。
POI ——一套用来创建,读写基于OLE 2组件文档格式的文件。使用Java来读写MS Excel ,Word文件。
Regexp ——一套纯java的正则表达式相关的包。
Slide ——主要模块是一个可以做为内容管理框架底层的内容仓库.它可以把内容信息存取到专门的,异质的,分布式的数据库中.Slide还增加了security, locking, versioning等特性.
Taglibs ——一套在开发web应用时十分有用的,jsp 通用 tag包。
Turbine ——类似 Struts,也是一个基于servlet的应用程序框架,支持MVC。提供了大量可重用的组件。此框架包中包含了大量组件,但是这些组件是离散的。
Velocity
——是一个基于java的模板引擎(template engine)。它允许任何人仅仅简单的使用模板语言(template
language)来引用由java代码定义的对象。
当Velocity应用于web开发时,界面设计人员可以和java程序开发人员同步开发一个遵循MVC架构的web站点,也就是说,页面设计人员可以只
关注页面的显示效果,而由java程序开发人员关注业务逻辑编码。 Velocity将
java代码从web页面中分离出来,这样为web站点的长期维护提供了便利, 同时也为我们在JSP和PHP之外又提供了一种可选的方案。
Velocity的能力远不止web站点开发这个领域,例如,它可以从模板(template)产生SQL和PostScript、XML,它也可以被当
作一个独立工具来产生源代码和报告,或者作为其他系统的集成组件使用。Velocity也可以为Turbine
web开发架构提供模板服务(template service)。
Velocity+Turbine提供一个模板服务的方式允许一个web应用以一个真正的MVC模型进行开发。
Apache java项目全介绍2
下面介绍一下Jakarta下的Commons:一个csdn网友描述得很贴切,Commons就好比一个java百宝箱。
commons分为3部分Commons Proper、Commons Sandbox和Commons Dormant。
Commons Proper:提供了设计良好可重用的java组件,并都经过了广泛、严格的测试。
Commons Sandbox:处于实验、测试阶段的组件。
Commons Dormant:处于停滞状态,从Sandbox退出的,不活跃的组件,谨慎使用。
Commons Proper组件:
Attributes—— 支持源代码级的元数据。
BeanUtils——提供对 Java 反射和自省API的包装,处理javabean的工具。
Betwixt——将JavaBeans与XML相互转换。
Chain——对Chain of Responsibility(责任链)设计模式的实现。使多个对象都有机会处理请求,从而避免请求的发送者和接收者之间的耦合关系。将这些对象连成一条链,并沿着这条链传递请求,直到有一个对象处理它为止。
CLI——处理命令行的命令的解析。
Codec——包含一些通用的编码解码算法。包括一些语音编码器, Hex, Base64, 以及URL encoder。
Collections——扩展和增加标准的 Java Collection框架。
Configuration——操作各种格式的配置文件。Properties文件 /XML文件 /JNDI /JDBC 数据源 /System properties /Applet parameters / Servlet parameters
Daemon——创建类似unix守护线程的java代码,可以安全地执行一些后台操作,线程不被某个应用程序控制,而是由操作系统控制类似windows的service,可以设置一个服务依赖于另一个服务,一个服务关闭前必须先执行另一个服务。
DBCP——一个数据库连接池
DbUtils——一个JDBC的工具类,比如可以将ResultSets生成javabean。
Digester——基于规则的XML文档解析,主要用于XML到Java对象的映射.
Discovery——提供工具来定位资源 (包括类) ,通过使用各种模式来映射服务/引用名称和资源名称。
EL——JSP 2.0引入的表达式
Email——处理e-mail
FileUpload——web应用中的文件上传组件
HttpClient——使用HTTP协议的客户端开发框架
IO——帮助进行IO功能开发
Jelly
——Jelly能够把XML转换成可执行代码,所以Jelly是一个基于XML与Java的脚本和处理引擎。
Jelly借鉴了JSP定指标签,Velocity,
Cocoon和Xdoclet中的脚本引擎的许多优点。Jelly可以用在命令行,Ant或者Servlet之中。
Jexl——Jexl是一个表达式语言,通过借鉴来自于Velocity的经验扩展了JSTL定义的表达式语言。
JXPath——使用XPath语法操作javabean的工具。
Lang——提供对java.lang包的扩展。
Launcher——跨平台的java程序的启动
Logging
——提供的是一个日志(Log)接口(interface),同时兼顾轻量级和不依赖于具体的日志实现工具。它提供给中间件/日志工具开发者一个简单的日
志操作抽象,允许程序开发人员使用不同的具体日志实现工具。用户被假定已熟悉某种日志实现工具的更高级别的细节。JCL提供的接口,对其它一些日志工具,
包括Log4J, Avalon LogKit, and JDK 1.4等,进行了简单的包装,此接口更接近于Log4J和LogKit的实现.
Math——Math 是一个轻量的,自包含的数学和统计组件,解决了许多非常通用但没有及时出现在Java标准语言中的实践问题.
Modeler—— 支持兼容JMX规范的MBeans开发。
Net——集合了网络工具和协议工具的实现
Pool——Commons-Pool 提供了通用对象池接口,一个用于创建模块化对象池的工具包,以及通常的对象池实现。
Primitives——对java原始类型的支持。
SCXML——处理SCXML
Transaction——事务处理,实现了多层次锁、事务集合、事务文件的访问。
Validator——提供了一个简单的,可扩展的框架来在一个XML文件中定义校验器 (校验方法)和校验规则。支持校验规则的和错误消息的国际化。
VFS——访问各种文件系统,可以是本地文件、HTTP服务器上的文件、zip中的文件。
Commons Sandbox组件:
Compress——处理压缩文件如tar, zip 和 bzip2 格式。
CSV——处理CSV文件
Exec——安全地处理外部进程
Finder——实现类似UNIX find命令的功能
I18n——处理软件的I18n功能
Id——生成id号功能
Javaflow——捕捉程序运行状态
JCI——java编译接口
OpenPGP——处理加密方法OpenPGP.
Pipeline——处理类似工作队列的管道工具
Proxy——生成动态代理
Alias Type
Transaction manager aliases
JDBC com.ibatis.sqlmap.engine.transaction.jdbc.JdbcTransactionConfig
JTA com.ibatis.sqlmap.engine.transaction.jta.JtaTransactionConfig
EXTERNAL com.ibatis.sqlmap.engine.transaction.external.ExternalTransactionConfig
Data types
string java.lang.String
byte java.lang.Byte
long java.lang.Long
short java.lang.Short
int java.lang.Integer
integer java.lang.Integer
double java.lang.Double
float java.lang.Float
boolean java.lang.Boolean
date java.util.Date
decimal java.math.BigDecimal
object java.lang.Object
map java.util.Map
hashmap java.util.HashMap
list java.util.List
arraylist java.util.ArrayList
collection java.util.Collection
iterator java.util.Iterator
Data source factory types
SIMPLE com.ibatis.sqlmap.engine.datasource.SimpleDataSourceFactory
DBCP com.ibatis.sqlmap.engine.datasource.DbcpDataSourceFactory
JNDI com.ibatis.sqlmap.engine.datasource.JndiDataSourceFactory
Cache controller types
FIFO com.ibatis.sqlmap.engine.cache.fifo.FifoCacheController
LRU com.ibatis.sqlmap.engine.cache.lru.LruCacheController
MEMORY com.ibatis.sqlmap.engine.cache.memory.MemoryCacheController
OSCACHE com.ibatis.sqlmap.engine.cache.oscache.OSCacheController
XML result types
Dom com.ibatis.sqlmap.engine.type.DomTypeMarker
domCollection com.ibatis.sqlmap.engine.type.DomCollectionTypeMarker
Xml com.ibatis.sqlmap.engine.type.XmlTypeMarker
XmlCollection com.ibatis.sqlmap.engine.type.XmlCollectionTypeMarker
事务处理是所有大型数据库产品的一个关键问题,各数据库厂商都在这个方面花费了很大精力,不同的事务处理方式会导致数据库性能和功能上的巨大差异。
事务处理也是数据库管理员与数据库应用程序开发人员必须深刻理解的一个问题,对这个问题的疏忽可能会导致应用程序逻辑错误以及效率低下。
下
面我们针对Oracle及SQL
Server这两种当前广泛使用的大型数据库产品,探讨一下它们在事务处理方面的一些差异。如没有特殊说明,本文内容适用的数据库产品版本为
Oracle9i及SQL Server 2000,其中的示例SQL语句,对于Oracle是在SQL*Plus中执行,而对于SQL Server
2000是在osql中执行。
1. 事务的概念
事务可以看作是由对数据库的若干操作组成的一个单元,这些操作要么都完成,要么都取消,从而保证数据满足一致性的要求。事务的一个典型例子是银行中的转帐
操作,帐户A把一定数量的款项转到帐户B上,这个操作包括两个步骤,一个是从帐户A上把存款减去一定数量,二是在帐户B上把存款加上相同的数量。这两个步
骤显然要么都完成,要么都取消,否则银行就会受损失。显然,这个转帐操作中的两个步骤就构成一个事务.
数据库中的事务还有如下ACID特征。
ACID分别是四个英文单词的首写字母,这四个英文单词是Atomicity、Consistency、Isolation、Durability,分别翻译为原子性、一致性、隔离性、持久性。
原子性:指事务中的操作,或者都完成,或者都取消。
一致性:指事务中的操作保证数据库中的数据不会出现逻辑上不一致的情况,一致性一般会隐含的包括在其他属性之中。
隔离性:指当前的事务与其他未完成的事务是隔离的。在不同的隔离级别下,事务的读取操作,可以得到的结果是不同的。
持久性:指对事务发出COMMIT命令后,即使这时发生系统故障,事务的效果也被持久化了。与此相反的是,当在事务执行过程中,系统发生故障,则事务的操作都被回滚,即数据库回到事务开始之前的状态。
对数据库中的数据修改都是在内存中完成的,这些修改的结果可能已经写到硬盘也可能没有写到硬盘,如果在操作过程中,发生断电或系统错误等故障,数据库可以
保证未结束的事务对数据库的数据修改结果即使已经写入磁盘,在下次数据库启动后也会被全部撤销;而对于结束的事务,即使其修改的结果还未写入磁盘,在数据
库下次启动后会通过事务日志中的记录进行“重做”,即把丢失的数据修改结果重新生成,并写入磁盘,从而保证结束事务对数据修改的永久化。这样也保证了事务
中的操作要么全部完成,要么全部撤销。
2. 事务设置及类型的区别
在SQL Server中有三种事务类型,分别是:隐式事务、显式事务、自动提交事务,缺省为自动提交。
自动提交,是指对于用户发出的每条SQL语句,SQL Server都会自动开始一个事务,并且在执行后自动进行提交操作来完成这个事务,也可以说在这种事务模式下,一个SQL语句就是一个事务。
显式事务,是指在自动提交模式下以Begin
Transaction开始一个事务,以Commit或Rollback结束一个事务,以Commit结束事务是把事务中的修改永久化,即使这时发生断电
这样的故障。例如下面是SQL Server中的一个显式事务的例子。
Begin Tran
Update emp Set ename=’Smith’ Where empno=7369
Insert Into dept Values(60,’HR’,’GZh’)
Commit
<!--[if !vml]--><!--[endif]-->
隐式事务,是指在当前会话中用Set Implicit_Transactions On命令设置的事务类型,这时任何DML语句(Delete、Update、Insert)都会开始一个事务,而事务的结束也是用Commit或Rollback。
在Oracle中没有SQL Server的这些事务类型,缺省情况下任何一个DML语句都会开始一个事务,直到用户发出Commit或Rollback操作,这个事务才会结束,这与SQL Server的隐式事务模式相似。
3. 事务隔离级别
在SQL92标准中,事务隔离级别分为四种,分别为:Read Uncommitted、Read
Committed、Read Repeatable、Serializable,其中Read Uncommitted与Read
Committed为语句级别的,而Read Repeatable与Serializable是针对事务级别的。
在Oracle和SQL Server中设置事务隔离级别的语句是相同的,都使用SQL92标准语法,即:
Set Transaction Isolation Level Read Committed
上面示例中的Read Committed可以被替换为其他三种隔离级别中的任意一种。
1) SQL Server中的隔离级别及实现机制
在SQL Server中提供了所有这四种隔离级别。
下面我们讨论在SQL Server中,这几种隔离级别的含义及其实现方式。
Read Uncommitted:一个会话可以读取其他事务未提交的更新结果,如果这个事务最后以回滚结束,这时的读取结果就可能是错误的,所以多数的数据库应用都不会使用这种隔离级别。
Read Committed:这是SQL
Server的缺省隔离级别,设置为这种隔离级别的事务只能读取其他事务已经提交的更新结果,否则,发生等待,但是其他会话可以修改这个事务中被读取的记
录,而不必等待事务结束,显然,在这种隔离级别下,一个事务中的两个相同的读取操作,其结果可能不同。
Read
Repeatable:在一个事务中,如果在两次相同条件的读取操作之间没有添加记录的操作,也没有其他更新操作导致在这个查询条件下记录数增多,则两次
读取结果相同。换句话说,就是在一个事务中第一次读取的记录保证不会在这个事务期间发生改变。SQL
Server是通过在整个事务期间给读取的记录加锁实现这种隔离级别的,这样,在这个事务结束前,其他会话不能修改事务中读取的记录,而只能等待事务结
束,但是SQL Server不会阻碍其他会话向表中添加记录,也不阻碍其他会话修改其他记录。
Serializable:在一个事务中,读取操作的结果是在这个事务开始之前其他事务就已经提交的记录,SQL
Server通过在整个事务期间给表加锁实现这种隔离级别。在这种隔离级别下,对这个表的所有DML操作都是不允许的,即要等待事务结束,这样就保证了在
一个事务中的两次读取操作的结果肯定是相同的。
2) Oracle中的隔离级别及实现机制
在Oracle中,没有Read Uncommitted及Repeatable
Read隔离级别,这样在Oracle中不允许一个会话读取其他事务未提交的数据修改结果,从而避免了由于事务回滚发生的读取错误。Oracle中的
Read Committed和Serializable级别,其含义与SQL Server类似,但是实现方式却大不一样。
在Oracle中,存在所谓的回滚段(Oracle9i之前版本)或撤销段(Oracle9i版本),Oracle在修改数据记录时,会把这些记录被修改
之前的结果存入回滚段或撤销段中,就是因为这种机制,Oracle对于事务隔离级别的实现与SQL
Server截然不同。在Oracle中,读取操作不会阻碍更新操作,更新操作也不会阻碍读取操作,这样在Oracle中的各种隔离级别下,读取操作都不
会等待更新事务结束,更新操作也不会因为另一个事务中的读取操作而发生等待,这也是Oracle事务处理的一个优势所在。
Oracle缺省的设置是Read
Committed隔离级别(也称为语句级别的隔离),在这种隔离级别下,如果一个事务正在对某个表进行DML操作,而这时另外一个会话对这个表的记录进
行读取操作,则Oracle会去读取回滚段或撤销段中存放的更新之前的记录,而不会象SQL Server一样等待更新事务的结束。
在Serializable隔离级别(也称为事务级别的隔离),事务中的读取操作只能读取这个事务开始之前已经提交的数据结果。
如果在读取时,其他事务正 在对记录进行修改,则Oracle就会在回滚段或撤销段中去寻找对应的原来未经更改的记录(而且是在读取操作所在的事务开始之前存放于回滚段或撤销段的记 录),这时读取操作也不会因为相应记录被更新而等待。
4. DDL语句对事务的影响
1) Oracle中DDL语句对事务的影响
在Oracle中,执行DDL语句(如Create Table、Create View等)时,会在执行之前自动发出一个Commit命令,并在随后发出一个Commit或者Rollback命令,也就是说,DDL会象如下伪码一样执行:
Commit;
DDL_Statement;
If (Error) then
Rollback;
Else
Commit;
End if;
我们通过分析下面例子来看Oracle中,DDL语句对事务的影响:
<!--[if !vml]--><!--[endif]-->Insert into some_table values(‘Before’);
<!--[if !vml]--><!--[endif]-->Creaate table T(x int);
<!--[if !vml]--><!--[endif]-->Insert into some_table values(‘After’);
<!--[if !vml]--><!--[endif]-->Rollback;
由于在Oracle执行Create table语句之前进行了提交,而在Create
table执行后也会自动发出Commit命令,所以只有插入After的行被回滚,而插入Before的行不会被回滚,Create
table命令的结果也不会被回滚,即使Create
table语句失败,所进行的Before插入也会被提交。如果最后发出Commit命令,因为插入Before及Create
table的操作结果已经在之前提交,所以Commit命令影响的只有插入After的操作。
2) SQL Server中DDL语句对事务的影响
在SQL Server中,DDL语句对事务的影响与其他DML语句相同,也就是说,在DML语句发出之前或之后,都不会自动发出Commit命令。
在SQL Server 2000中,对于与上面Oracle同样的例子,最后发出Rollback后,数据库会回滚到插入Before之前的状态,即插入Before和After的行都会被回滚,数据表T也不会被创建。
如果最后发出Commit操作,则会把三个操作的结果全部提交。
5. 用户断开数据库连接对事务的影响
另外,对应于Oracle的管理客户端工具SQL*Plus,在SQL Server
2000中是osql,两种管理工具都是命令行工具,使用方式及作用也类似,但是在SQL*Plus中,用户退出连接时,会自动先发出Commit命令,
然后再退出,而在osql中,如果用户退出连接,会自动发出Rollback命令,这对于SQL
Server的自动提交模式没有什么影响,但如果处于隐式事务模式,其影响是显而易见的。对于两种数据库产品的其他客户端管理工具也有类似的不同之处。
SAX (Simple API for XML) 和 DOM (Document Object Model) 是当前两个主要的XML API,几乎所有商用的xml 解析器都同时实现了这两个接口。因此如果你的程序使用了SAX或者DOM APIs,那么你的程序对xml解析器是透明。
1. DOM以一个分层的对象模型来映射xml文档。而SAX将文档中的元素转化为对象来处理。
2. DOM将文档载入到内存中处理,而SAX则相反,它可以检测一个即将到来的 XML流,由此并不需要所有的XML代码同时载入到内存中。
SAX 处理是如何工作的
SAX 在读取 XML 流的同时处理它们,这很像以前的自动收报机纸带(ticker tape)。请考虑下面的 XML 代码片断:
<?xml version="1.0"?>
<samples>
<server>UNIX</server>
<monitor>color</monitor>
</samples>
分析这个代码片断的 SAX 处理器一般情况下将产生以下事件:
Start document
Start element (samples)
Characters (white space)
Start element (server)
Characters (UNIX)
End element (server)
Characters (white space)
Start element (monitor)
Characters (color)
End element (monitor)
Characters (white space)
End element (samples)
SAX API 允许开发人员捕捉这些事件并对它们作出反应。
SAX 处理涉及以下步骤:
1.
创建一个事件处理程序。
2.创建 SAX 解析器。
3.向解析器分配事件处理程序。
4.解析文档,同时向事件处理程序发送每个事件。
基于事件的处理的优点和缺点
这种处理的优点非常类似于流媒体的优点。分析能够立即开始,而不是等待所有的数据被处理。而且,由于应用程序只是在读取数据时检查数据,
因此不需 要将数据存储在内存中。这对于大型文档来说是个巨大的优点。事实上,应用程序甚至不必解析整个文档;它可以在某个条件得到满足时
停止解析。一般来说,SAX 还比它的替代者 DOM 快许多。
另一方面,由于应用程序没有以任何方式存储数据,使用 SAX 来更改数据或在数据流中往后移是不可能的。
DOM 和基于树的处理
DOM 是处理 XML 数据的传统方法。使用 DOM 时,数据以树状结构的形式被加载到内存中。
例如,在“SAX 处理是如何工作的”中用作例子的相同文档在 DOM 中将表示为节点,DOM 使用父子关系。
基于树的处理的优点和缺点
DOM
以及广义的基于树的处理具有几个优点。首先,由于树在内存中是持久的,因此可以修改它以便应用程序能对数据和结构作出更改。
它还可以在任何时候在树中上下导航,而不是像 SAX 那样是一次性的处理。DOM 使用起来也要简单得多。
另一方面,在内存中构造这样的树涉及大量的开销。大型文件完全占用系统内存容量的情况并不鲜见。此外,创建一棵 DOM 树可能是一个缓慢的过程。
如何在 SAX 和 DOM 之间选择
选择 DOM 还是选择 SAX,这取决于下面几个因素:
1.应用程序的目的:如果打算对数据作出更改并将它输出为 XML,那么在大多数情况下,DOM 是适当的选择。并不是说使用 SAX 就不能更改数据,
但是该过程要复杂得多,因为您必须对数据的一份拷贝而不是对数据本身作出更改。
2.数据容量: 对于大型文件,SAX 是更好的选择。
数据将如何使用:如果只有数据中的少量部分会被使用,那么使用 SAX 来将该部分数据提取到应用程序中可能更好。 另一方面,
如果您知道自己以后会回头引用已处理过的大量信息,那么 SAX 也许不是恰当的选择。
3.对速度的需要: SAX 实现通常要比 DOM 实现更快。
SAX 和 DOM 不是相互排斥的,记住这点很重要。您可以使用 DOM 来创建 SAX 事件流,也可以使用 SAX 来创建 DOM 树。事实上,用于创建 DOM 树的大多数解析器实际上都使用 SAX 来完成这个任务!
1、增加拦截器
<interceptor-ref name="execAndWait">
<!--等待时间,执行时间没有超过此值,将不显示等待画面(毫秒)-->
<param name="delay">1000</param>
<!-- 间隔检查时间,检查后台进程有没有执行完毕,如果完成了它就立刻返回-->
<param name="delaySleepInterval">50</param>
</interceptor-ref>
此拦截器必须放在所有拦截器的最后。
2、增加result
<result name="wait">wait.jsp</result>
如果没有找到"wait"结果,struts2会自动生成一个wait结果(\org\apache\struts2\interceptor
\wait.ftl).这个结果是用FreeMarker做的,所以需要Freemarker支持才能正常工作。如果你不想在程序中加入
FreeMarker,那就必须自己实现一个wait结果。这一般来说是有必要的,因为默认的wait页面很简单。
3、Action实现SessionAware接口
因为这个action将会以单独的线程执行,所以你不能用ActionContext,因为它是ThreadLocal.这也就是说如果你要访问
session数据,你必须实现 SessionAware结构而不是调用ActionContext.getSesion() 。
public interface SessionAware{
public void setSession(Map map);
}
public abstract class AbsBasicAction extends ActionSupport implements SessionAware{
/** 当前 Session */
protected Map session ;
public void setSession(Map session) {
this.session = session ;
}
}
4、实现 wait 结果 映射的 wait.jsp
必须设置该页面的meta信息,每隔5秒,重新请求一次前面的action。
<meta http-equiv="refresh" content="2;url=<s:url includeParams="all" />" />
When you create a html table and apply a border using CSS, then the
empty cells do not have a border. So you pull out your CSS book and
find
empty-cells:show; and think your problems are solved but
empty-cells is unsupported in IE…go figure. But I have found a combination of styles that work. Try this…apply the
border-collapse:collapse; and
empty-cells:show; to the table level and IE will display borders around empty cells.
Spring数据访问的根异常是DataAccessException,所有的Spring的数据访问异常都继承DataAccessException。
DataAccessException的nestedRuntimeException子类中,已经将所有的异常栈都进行了记录。
CleanupFailureDataAccessException:清除失败异常
DataAccessResourceFailureException:资源访问异常
DataIntegrityViolationException:完整性约束异常
DataRetrievalFailureException:数据获取异常
DeadlockLoserDataAccessException:死锁访问异常
IncorrectUpdateSemanticsDataAccessException:更新出错异常
InvalidDataAccessApiUsageException:无效数据访问API使用异常
InvalidDataAccessResourceUsageException:无效数据访问资源使用异常
OptimisticLockingFailureException:乐观锁失败异常
TypeMismatchDataAccessException:类型匹配失败异常
UncategorizedDataAccessException:其他原因异常
搞了一天mylyn也没搞定,郁闷透顶了。今天偶尔翻日志,才发现原来是springide惹得货,似乎跟mylyn存在冲突。唉,这个破烂
myeclipse,没有sping可以,没有mylyn可是很难生存的啊。 于是对springide痛下杀手,终于搞定了。。呵呵
不过今天发现了一个不错的xml编辑器,有语法提示功能,省了好多脑细胞。。。
名字是Rinzo XML Editor, URL: http://editorxml.sourceforge.net/
在为数不少得公司中,都会存在一些一看就不合理的但却依然长久存在的问题。每每有人对此提出异议,总是被人如得了尚方宝剑般毋庸置疑的贴上“历史问题”的标签,似乎这样就可以盖棺定论,无需再议了。
但是任何人都知道,历史是向前发展的。没道理事情曾经是这样就要一直这样下去。“历史问题”的标签如果真那么牢不可破,人们恐怕还住在北京或云南某山洞里吧。
有次看电视讲到,美国小朋友都写过得一篇作文叫做“世界因我而变”,小朋友看到这个题目,都抓耳挠腮,觉得自己不能改变世界。这时老师解释道:“今天我早起,准时到达学校,给了你们一堂生动得课,于是世界就这样改变了”。深层一点讲,这是向孩子们幼小的心灵里注入了一种极为积极的人生态度。改变世界不需要做什么惊天动地的大事,只需要承担自己的责任,改变力所能及的不合理便可。
客观一点说,中国人是消极思维很重的,要不也不会对“历史问题”如此推崇备至。黄老之学恐怕就是这种思想的理论根据了吧,悠久的历史其实是一个沉重的包袱,它压的中华民族不敢大步走路呢。。。
对于QQ、MSN这类聊天工具,大家应该是再熟悉不过了,那么在Unix系统中能否进行此类的聊天操作呢?答案当然是肯定的。Unix提供了多种方式让用户相互通信,它允许以文本的方式给某个用户或所有用户传递信息,而信息来源可以是文本文件或直接由键盘输入的内容。
(1)使用write发送消息
使用write命令可以向任何已登录同一系统的另一位用户发送消息,这时在对方用户终端上就会自动显示出信息,而对方也同样可以使用write 命令来回复消息,这样双方之间就能互相通信了。
write命令的格式如下:
write user-name
此处user-name是将要与之通信的用户名,可以使用who
命令来查看系统中已登陆的用户名。输入write命令后,即可进入发送消息状态,这时就可以通过键盘输入消息,回车即可向对方发送此条消息了。要想退出发
送消息状态,可以使用“Ctrl + d”退出write 命令回到shell。
(2)使用talk进行双方对话
talk命令允许双方实现类似于MSN、QQ这样的实时对话,其命令格式如下:
talk user-name
当用户向另一用户发送对话请求,只有在对方同意接受对话请求,即对方也使用talk命令后,双方的对话才会建立。这时双方终端上都会显示
“[connect
established]”,这样双方就可以从键盘输入文字,talk会把这些输入显示在对方的屏幕上,若任意一方按键就会退出对话状态。
(3)使用wall进行广播式消息传送
使用wall命令可以向所有用户广播通知信息,而所有登录系统的用户都可收到该消息。wall命令有以下几种格式:
wall
wall filename
wall “send a message to all the people!”
(4)使用mesg允许或拒绝接收消息
当一位用户正在全身心的投入某项工作的时候,可能会不想被另外一位用户用上述方式突然发送来的消息打断思路,那么就可使用mesg命令来设置是否允许接收由其它用户发来的信息。
mesg命令的格式如下:
mesg –n
或
mesg -y
其中,y表示允许接收信息,n 表示拒绝接收信息。如果mesg后面没有跟参数,则报告当前的许可状态。
1、PL/SQL Developer记住登陆密码
在使用PL/SQL Developer时,为了工作方便希望PL/SQL Developer记住登录Oracle的用户名和密码;
设置方法:PL/SQL Developer 7.1.2
->tools->Preferences->Oracle->Logon History , “Store
history”是默认勾选的,勾上“Store with password” 即可,重新登录在输入一次密码则记住了。
2、执行单条SQL语句
在使用PL/SQL Developer的SQL Window时,按F8键,PL/SQL Developer默认是执行该窗口的所有SQL语句,需要设置为鼠标所在的那条SQL语句,即执行当前SQL语句;
设置方法:PL/SQL Developer 7.1.2 -->tools->Preferences-->Window types ,勾上“AutoSelect Statement” 即可。
3、格式化SQL语句
在使用PL/SQL Developer的SQL Window时,有时候输入的SQL语句太长或太乱,希望能用比较通用的写法格式话一下,这样看起来会好看些,也好分析;
使用方法:选中需要格式化的SQL语句,然后点击工具栏的PL/SQL beautifier按钮即可.
4、查看执行计划
在使用PL/SQL Developer的SQL Window时,有时候输入的SQL语句执行的效率,分析下表结构,如何可以提高查询的效率,可以通过查看Oracle提供的执行计划;
使用方法:选中需要分析的SQL语句,然后点击工具栏的Explain plan按钮(即执行计划),或者直接按F5即可。
5、调试存储过程
在使用PL/SQL Developer操作Oracle时,有时候调用某些存储过程,或者调试存储过程;
调用存储过程的方法:首先,在PL/SQL
Developer左边的Browser中选择Procedures,查找需要调用的存储过程;然后,选中调试的存储过程,点击右键,选择Test,在弹
出来的Test scrīpt窗口中,对于定义为in类型的参数,需要给该参数的Value输入值;最后点击上面的条数按钮:Start
debugger 或者按F9;最后点击:RUN 或者Ctrl+R
6.左下角显示window list
点击菜单 tools -> window list,将弹出的小窗口拖到左下角合适位置,然后点击菜单 window->save layout
7.防止登录超时
tools->Preferences-->Oracle->Connection 选择 "check connection"
8.不备份sql文件
tools->Preferences->Files->backup,页面中backup files中选择 disabled
系统要求进行SQL优化,对效率比较低的SQL进行优化,使其运行效率更高,其中要求对SQL中的部分in/not in修改为exists/not exists
修改方法如下:
in的SQL语句
SELECT id, category_id, htmlfile, title, convert(varchar(20),begintime,112) as pubtime
FROM tab_oa_pub WHERE is_check=1 and
category_id in (select id from tab_oa_pub_cate where no='1')
order by begintime desc
修改为exists的SQL语句
SELECT id, category_id, htmlfile, title, convert(varchar(20),begintime,112) as pubtime
FROM tab_oa_pub WHERE is_check=1 and
exists (select id from tab_oa_pub_cate where tab_oa_pub.category_id=convert(int,no) and no='1')
order by begintime desc
分析一下exists真的就比in的效率高吗?
我们先讨论IN和EXISTS。
select * from t1 where x in ( select y from t2 )
事实上可以理解为:
select *
from t1, ( select distinct y from t2 ) t2
where t1.x = t2.y;
——如果你有一定的SQL优化经验,从这句很自然的可以想到t2绝对不能是个大表,因为需要对t2进行全表的“唯一排序”,如果t2很大这个排序的性能是
不可忍受的。但是t1可以很大,为什么呢?最通俗的理解就是因为t1.x=t2.y可以走索引。但这并不是一个很好的解释。试想,如果t1.x和t2.y
都有索引,我们知道索引是种有序的结构,因此t1和t2之间最佳的方案是走merge
join。另外,如果t2.y上有索引,对t2的排序性能也有很大提高。
select * from t1 where exists ( select null from t2 where y = x )
可以理解为:
for x in ( select * from t1 )
loop
if ( exists ( select null from t2 where y = x.x )
then
OUTPUT THE RECORD!
end if
end loop
——这个更容易理解,t1永远是个表扫描!因此t1绝对不能是个大表,而t2可以很大,因为y=x.x可以走t2.y的索引。
综合以上对IN/EXISTS的讨论,我们可以得出一个基本通用的结论:IN适合于外表大而内表小的情况;EXISTS适合于外表小而内表大的情况。
我们要根据实际的情况做相应的优化,不能绝对的说谁的效率高谁的效率低,所有的事都是相对的
not in 和not exists
如果查询语句使用了not in 那么内外表都进行全表扫描,没有用到索引;
而not extsts 的子查询依然能用到表上的索引。
所以无论那个表大,用not exists都比not in要快。
原文:http://blog.csdn.net/jwisdom/archive/2007/09/27/1803577.aspx
死了都要try!
不catch我不痛快!
程序毁灭throw还在!
把每天,
当作是程序来更改,
一改一天,
累到泪水都掉下来,
不理会,
老板是好还是坏,
只要有工资,
来还贷.
改,不是需求做的太坏,
那是sb客户想要什么,
自己都不明白.
忍受现在,
自己一生还不完的贷,
很多模块,
不能完成,
我还得改.
死了都要try!
不catch我不痛快!
程序毁灭throw还在!
Xubuntu中只有一个选单叫做应用程序(Application),这个选单是Xfce menu模块,使用者只要在选单上按右键选择编辑之后,有可能会造成选单消失的状况。在Ubtw社群论坛中许多使用者都提出了这问题,暂时性解决方法如下:
Xfce menu的设定檔叫作menu.xml,所有的选单内容会存在这个档案内,预设的使用者设定文件位置在
~/.config/xfce4/Desktop
之下,使用者修改过的内容也会存在这里。而Xubuntu内还有一套完整的初始menu.xml跟当地化(locale)的档案,位置在
/etc/xdg/xfce4/desktop
将里面的menu.xml拷贝到使用者设定文件位置
sudo
cp /etc/xdg/xfce4/desktop/menu.xml ~/.config/xfce4/Desktop
接着请把档案权限修改为只能读取不能修改,这样不管你是不是有使用选单编辑器(menu editor)都无法影响。因为我用英文版所以拷贝的是menu.xml,如果是使用中文版的朋友应该是要把menu.xml.zh这个档案拷贝过去之后修改成menu.xml。怕又出问题的人(因为我刚找到方法测试了一下,乱按乱改menu editor之后都没问题),记得备份一下你的选单档案。
--
Update: 直接砍掉
~/.config/xfce4/Desktop/menu.xml 也可以
时常碰到系统需要导出大量数据的情况。每次碰到,当事人无不抓耳挠腮,痛苦非常。
大量数据的在线导出除了耗费大量内存,造成服务器的沉重负担。甚至会造成用户死机,所以一直是个头疼的问题。
近日夜观天象,忽见紫气东来,猛然想到,前台不行,就后台做嘛。
说道后台任务调度,无疑会想到quartz,它的SimpleTrigger正好可以做这个事情。
思路如下:
用户导出时,提示用户输入邮件地址,然后后台建立一个quartz任务,任务中,将数据导出到文件中。
任务结束时,将文件的链接发到用户填写的邮箱地址中,这样用户就可以根据这个地址,去下载导出的
文件了。
网页打开的最佳速度
2秒!
许多研究都表明,用户最满意的打开网页时间,是在2秒以下。用户能够忍受的最长等待时间的中位数,在6~8秒之间。这就是说,8秒是一个临界值,如果你的网站打开速度在8秒以上,那么很可能,大部分访问者最终都会离你而去。
研究显示,如果等待12秒以后,网页还是没有载入,那么99%以上的用户会关闭这个网页,不再等待。
但是,如果在等待载入期间,网站能够向用户显示反馈消息,比如一个进度条,那么用户可以忍受的时间会延长到38秒。
对访问者的心理影响
根据一些抽样调查,访问者倾向于认为,打开速度较快的网站质量更高,更可信,也更有趣。
相对应地,网页打开速度越慢,访问者的心理挫折感就越强,就会对网站的可信性和质量产生怀疑。在这种情况下,用户会觉得网站的后台可能出现了一些错
误,因为在很长一段时间内,他没有得到任何提示。而且,缓慢的打开速度会让用户忘了下一步要干什么,不得不重新回忆,这会进一步恶化用户的使用体验。
这个指标对电子商务网站尤其重要。载入速度越快,就越容易使访问者变成你的客户,降低客户选择商品后、最后却放弃结账的比例。
不过,网站反应速度也不宜太快,否则用户会增加与服务器互动的频率,这可能加大出现错误的概率。
一些实证结果
Google做过一个试验,显示10条搜索结果的页面载入需要0.4秒,显示30条搜索结果的页面载入需要0.9秒,结果后者使得Google总的流量和收入减少了20%。
Google地图上线的时候,首页大小有100KB,后来下降到70~80KB。结果,流量在第一个星期上升了10%,接下来的3个星期又再上升了25%。
Amazon的统计也显示了相近的结果,首页打开时间每增加100毫秒,网站销售量会减少1%。
宽带与窄带的区别
有研究显示,宽带用户比窄带用户更没有耐心。宽带用户愿意忍受的最长等待时间,往往只有4~6秒。
网站制作者必须记住,在ADSL条件下,3~5秒就能载入的网页,在窄带条件下需要20~30秒才能打开。因此,网页总的大小——包括图片、Javascript和CSS文件的大小——不宜过大,这样对宽带和窄带用户都有利。
1. /*+ALL_ROWS*/
表明對語句塊選擇基於開銷的優化方法,並獲得最佳吞吐量,使資源消耗最小化.
例如:
SELECT /*+ALL_ROWS*/ EMP_NO,EMP_NAM,DAT_IN FROM BSEMPMS WHERE EMP_NO='SCOTT';
2./*+FIRST_ROWS*/
表明對語句塊選擇基於開銷的優化方法,並獲得最佳回應時間,使資源消耗最小化.
例如:
SELECT/*+FIRST_ROWS*/ EMP_NO,EMP_NAM,DAT_IN FROM BSEMPMS WHERE EMP_NO='SCOTT';
3./*+CHOOSE*/
表明如果資料字典中有訪問表的統計資訊,將基於開銷的優化方法,並獲得最佳的吞吐量;
表明如果資料字典中沒有訪問表的統計資訊,將基於規則開銷的優化方法;
例如:
SELECT/*+CHOOSE*/ EMP_NO,EMP_NAM,DAT_IN FROM BSEMPMS WHERE EMP_NO='SCOTT';
4./*+RULE*/
表明對語句塊選擇基於規則的優化方法.
例如:
SELECT/*+ RULE */ EMP_NO,EMP_NAM,DAT_IN FROM BSEMPMS WHERE EMP_NO='SCOTT';
5./*+FULL(TABLE)*/
表明對表選擇全局掃描的方法.
例如:
SELECT/*+FULL(A)*/ EMP_NO,EMP_NAM FROM BSEMPMS A WHERE EMP_NO='SCOTT';
6./*+ROWID(TABLE)*/
提示明確表明對指定表根據ROWID進行訪問.
例如:
SELECT/*+ROWID(BSEMPMS)*/ * FROM BSEMPMS WHERE ROWID>='AAAAAAAAAAAAAA' AND EMP_NO='SCOTT';
7./*+CLUSTER(TABLE)*/
提示明確表明對指定表選擇簇掃描的訪問方法,它只對簇物件有效.
例如:
SELECT/*+CLUSTER */ BSEMPMS.EMP_NO,DPT_NO FROM BSEMPMS,BSDPTMS
WHERE DPT_NO='TEC304' AND BSEMPMS.DPT_NO=BSDPTMS.DPT_NO;
8./*+INDEX(TABLE INDEX_NAME)*/
表明對表選擇索引的掃描方法.
例如:
SELECT/*+INDEX(BSEMPMS SEX_INDEX) USE SEX_INDEX BECAUSE THERE ARE FEWMALE BSEMPMS */ FROM BSEMPMS WHERE SEX='M';
9./*+INDEX_ASC(TABLE INDEX_NAME)*/
表明對表選擇索引昇冪的掃描方法.
例如:
SELECT/*+INDEX_ASC(BSEMPMS PK_BSEMPMS) */ FROM BSEMPMS WHERE DPT_NO='SCOTT';
10./*+INDEX_COMBINE*/
為指定表選擇點陣圖訪問路經,如果INDEX_COMBINE中沒有提供作為參數的索引,將選擇出點陣圖索引的布林組合方式.
例如:
SELECT/*+INDEX_COMBINE(BSEMPMS SAL_BMI HIREDATE_BMI)*/ * FROM BSEMPMS
WHERE SAL<5000000 AND HIREDATE<SYSDATE;
11./*+INDEX_JOIN(TABLE INDEX_NAME)*/
提示明確命令優化器使用索引作為訪問路徑.
例如:
SELECT/*+INDEX_JOIN(BSEMPMS SAL_HMI HIREDATE_BMI)*/ SAL,HIREDATE
FROM BSEMPMS WHERE SAL<60000;
12./*+INDEX_DESC(TABLE INDEX_NAME)*/
表明對表選擇索引降冪的掃描方法.
例如:
SELECT/*+INDEX_DESC(BSEMPMS PK_BSEMPMS) */ FROM BSEMPMS WHERE DPT_NO='SCOTT';
13./*+INDEX_FFS(TABLE INDEX_NAME)*/
對指定的表執行快速全索引掃描,而不是全表掃描的辦法.
例如:
SELECT/*+INDEX_FFS(BSEMPMS IN_EMPNAM)*/ * FROM BSEMPMS WHERE DPT_NO='TEC305';
14./*+ADD_EQUAL TABLE INDEX_NAM1,INDEX_NAM2,...*/
提示明確進行執行規劃的選擇,將幾個單列索引的掃描合起來.
例如:
SELECT/*+INDEX_FFS(BSEMPMS IN_DPTNO,IN_EMPNO,IN_SEX)*/ * FROM BSEMPMS WHERE EMP_NO='SCOTT' AND DPT_NO='TDC306';
15./*+USE_CONCAT*/
對查詢中的WHERE後面的OR條件進行轉換為UNION ALL的組合查詢.
例如:
SELECT/*+USE_CONCAT*/ * FROM BSEMPMS WHERE DPT_NO='TDC506' AND SEX='M';
16./*+NO_EXPAND*/
對於WHERE後面的OR 或者IN-LIST的查詢語句,NO_EXPAND將阻止其基於優化器對其進行擴展.
例如:
SELECT/*+NO_EXPAND*/ * FROM BSEMPMS WHERE DPT_NO='TDC506' AND SEX='M';
17./*+NOWRITE*/
禁止對查詢塊的查詢重寫操作.
18./*+REWRITE*/
可以將視圖作為參數.
19./*+MERGE(TABLE)*/
能夠對視圖的各個查詢進行相應的合併.
例如:
SELECT/*+MERGE(V) */ A.EMP_NO,A.EMP_NAM,B.DPT_NO FROM BSEMPMS A (SELET DPT_NO
,AVG(SAL) AS AVG_SAL FROM BSEMPMS B GROUP BY DPT_NO) V WHERE A.DPT_NO=V.DPT_NO
AND A.SAL>V.AVG_SAL;
20./*+NO_MERGE(TABLE)*/
對於有可合併的視圖不再合併.
例如:
SELECT/*+NO_MERGE(V) */ A.EMP_NO,A.EMP_NAM,B.DPT_NO FROM BSEMPMS A
(SELECT DPT_NO,AVG(SAL) AS AVG_SAL FROM BSEMPMS B GROUP BY DPT_NO) V
WHERE A.DPT_NO=V.DPT_NO AND A.SAL>V.AVG_SAL;
21./*+ORDERED*/
根據表出現在FROM中的順序,ORDERED使ORACLE依此順序對其連接.
例如:
SELECT/*+ORDERED*/ A.COL1,B.COL2,C.COL3 FROM TABLE1 A,TABLE2 B,TABLE3 C WHERE A.COL1=B.COL1 AND B.COL1=C.COL1;
22./*+USE_NL(TABLE)*/
將指定表與嵌套的連接的行源進行連接,並把指定表作為內部表.
例如:
SELECT/*+ORDERED USE_NL(BSEMPMS)*/
BSDPTMS.DPT_NO,BSEMPMS.EMP_NO,BSEMPMS.EMP_NAM FROM BSEMPMS,BSDPTMS
WHERE BSEMPMS.DPT_NO=BSDPTMS.DPT_NO;
23./*+USE_MERGE(TABLE)*/
將指定的表與其他行源通過合併排序連接方式連接起來.
例如:
SELECT/*+USE_MERGE(BSEMPMS,BSDPTMS)*/ * FROM BSEMPMS,BSDPTMS WHERE BSEMPMS.DPT_NO=BSDPTMS.DPT_NO;
24./*+USE_HASH(TABLE)*/
將指定的表與其他行源通過哈希連接方式連接起來.
例如:
SELECT/*+USE_HASH(BSEMPMS,BSDPTMS)*/ * FROM BSEMPMS,BSDPTMS WHERE BSEMPMS.DPT_NO=BSDPTMS.DPT_NO;
25./*+DRIVING_SITE(TABLE)*/
強制與ORACLE所選擇的位置不同的表進行查詢執行.
例如:
SELECT/*+DRIVING_SITE(DEPT)*/ * FROM BSEMPMS,DEPT@BSDPTMS WHERE BSEMPMS.DPT_NO=DEPT.DPT_NO;
26./*+LEADING(TABLE)*/
將指定的表作為連接次序中的首表.
27./*+CACHE(TABLE)*/
當進行全表掃描時,CACHE提示能夠將表的檢索塊放置在緩衝區緩存中最近最少列表LRU的最近使用端
例如:
SELECT/*+FULL(BSEMPMS) CAHE(BSEMPMS) */ EMP_NAM FROM BSEMPMS;
28./*+NOCACHE(TABLE)*/
當進行全表掃描時,CACHE提示能夠將表的檢索塊放置在緩衝區緩存中最近最少列表LRU的最近使用端
例如:
SELECT/*+FULL(BSEMPMS) NOCAHE(BSEMPMS) */ EMP_NAM FROM BSEMPMS;
29./*+APPEND*/
直接插入到表的最後,可以提高速度.
insert/*+append*/ into test1 select * from test4 ;
30./*+NOAPPEND*/
通過在插入語句生存期內停止並行模式來啟動常規插入.
insert/*+noappend*/ into test1 select * from test4 ;
作为开发人员,每天同步本地代码是个好习惯,但好习惯不代表人人都愿意做,更不用说天天做。
再细致的人也难免会偶尔忘掉,浪费不必要的时间,因此研究了如下脚本,开机即可启动tortoiseCVS来同步代码,
总算省了自己不少事 :)
TortoiseAct.exe CVSUpdate -l 需要更新的文件夹列表(空格分隔)
将以上脚本置于update.bat文件中,并放到tortoise目录下,然后设置开机运行update.bat即可。
gook luck!
70多家大公司、学术机构和高资历技术专家发起了一项旨在向美国政府机构灌输
开源技术优点的行动。Google、RedHat、Novell、Linux、Mozilla、Sun和电子前沿基金会在
OSCON开源会议上宣布了这一消息,并携手创立了
美国开源联盟。此次共同行动的目标是游说美国联邦政府放弃私权代码软件,转而考虑开源软件。顾问委员会成员包括O’Reilly Media总裁蒂姆·奥莱利(Tim O’Reilly)和Linux基金会执行董事吉姆·泽姆林(Jim Zemlin)。
美国开源联盟的大卫·托马斯(David Thomas)说:“软件自由带来的技术”可以使得政府的IT事务调度更加安全、节省成本、便捷,提供了更强的隐私以及消除供应商锁定(vendor lock-in)的能力。”
然而,游说绝非易事,因为在意见请求阶段,小型开源公
司必须得设法达到和微软一样的地位。而且,政府官员过去曾表达过对开源方式的安全担忧。有评论家认为,暴露在光天化日之下的源代码可能会被黑客盯上,招致
风险。不过,也有观点认为开放代码可以在安全有保障的情况下进行。通过摈弃私权软件模型,允许自由访问系统源代码,政府可不再依赖一小部分软件承包商来构
建安全系统,取而代之的是一个完整的、用于布署防御黑客攻击的编程社区。
美国政府最重要的开源项
目之一是同国家安全局(NSA)的合作。国家安全局已经运用开源技术为使用SELinux系统的政府计算机处理多级安全问题。SELinux最初作为
Linux系统的修改版发布,此后在NSA同开源软件商合作应对安全问题的工作中发挥了自己力量。Linux用户最初曾怀疑NSA用此代码库来窥探他们的
电脑,但并没有人在软件中发现“后门”(back doors)。现在,成千上万名政府雇员和Linux用户都使用SELinux保护自己的机器。
另一个成功的开源政府项目是Sunlight Lab的“美国应用”。该项目显示政府开源对公共责任和集体合作解决方案都具有促进作用,在第一年便大获成功。“美国应用”产生了一系列性能不凡的用户生成项目,包括一个呼叫国会议员的火狐插件以及追踪拖延式演说的网站。Sunlight Labs负责人克雷·约翰逊(Clay Johnson)和其他开源创新者在昨天的Google-O’Reilly开源大奖仪式上得到了如潮的赞美。
蒂姆·奥莱利说:“发明不在于它开始时对世界造成了多大影响,而是最终造成了多少。”随着新安全隐患的形成以及建立新数据集的重要性与日俱增,政府在提升为公民服务的过程中需要考虑开源的解决方案。
如果你想为政府开源项目出一份力,请登陆“美国应用2”和美国开源联盟。
泛型是JDK 5.0后出现新概念,泛型的本质是参数化类型,也就是说所操作的数据类型被指定为一个参数。这种参数类型可以用在类、接口和方法的创建中,分别称为泛型类、泛型接口、泛型方法。
泛型类引入的好处不仅在于减少代码量,还在于一提供了编译时期数据类型的检查功能,可以提前预知错误的发生,增加代码安全性,二是减少了强制类型转换。下面定义一个泛型类
Example GenericFX.java
package org;
public class GenericFX<E>
{
private E fx;
public void setFX(E fx)
{
this.fx = fx;
}
public E getFX()
{
return fx;
}
}
在此例中,GenericFX类就是要定义的泛型类,类型变量E是泛型类的类型参数,我们可以使用GenericFX<E>这样的类型名来声明一个泛型类。如
GenericFX<E> fx = new GenericFX<E>(),其中E就是具体的类型, 下面看一个应用泛型类的例子.
Example GenericFXDemo.java
package org;
public class GenericFXDemo
{
public void main(String[] args)
{
GenericFX<Integer> f1 = new GenericFX<Integer>();
GenericFX<Boolean> f2 = new GenericFX<Boolean>();
f1.setFX(new Integer(10));
System.out.println(f1.getFX);//
f2.setFX(new Boolean(true));
System.out.println(f2.getFX);//
}
}
可以看到,f1,f2都是同一泛型类的对象引用,在取得不同参数类型对象时,已不需要实行类型转换.
如果像这样修改一下代码,
f1.setFX(new Integer(10));
Boolean b = f1.getFX();
System.out.println(b); 这里编译就会报错,告诉你这不安全.
泛型类可以定义多个类型参数,像GenericFX<E,F>就要两个类型参数。但是,无论有多少个类型参数,泛型仍然只需要一个类。如上GenericFX<Integer> ,GenericFX<Boolean>都属于同一个泛型类GenericFX,f1,f2都只是该泛型类的一个应用,当然单一的GenericFX类是不能称为泛型类的,必须要有<>和类型参数才可以,而像fx只是泛型类的成员变量。
昨天打开我心爱的xubuntu,发现上下的panel居然不见了,极其郁闷!虽然可以手工启动,但出现了更恼人的问题,xfce启动明显慢了很多,控制台可以看到错误信息“xfce4-panel is not running”.在xinitrc中,可以看到,如果panel启动失败,会重试很多次,这就是启动变慢的元凶了。
查了很多地方都没有得到答案,最后还是在debian社区找到了线索,ubuntu他爸万岁。。。
Hm, after nuking ~/.cache/sessions/, the problem is gone.
As I didn't make a copy beforehand I can longer reproduce the problem, so feel
free to close the bug.
说得很清楚了,只要删除~/.cache/sessions/ 目录下的所有内容就可以了。
此问题我想与xfce的session保存有关。保存session时可能出现了文件损坏,导致这个问题。
如果想一劳永逸,不保存session就好了,从个人经验来讲,保存session并没有太多实用价值。
原文地址:http://aofengblog.blog.163.com/blog/static/631702120083222117768/
Oracle SQL的优化规则:
- 尽量少用IN操作符,基本上所有的IN操作符都可以用EXISTS代替
用IN写出来的SQL的优点是比较容易写及清晰易懂,但是用IN的SQL性能总是比较低的,从ORACLE执行的步骤来分析用IN的SQL与不用IN的SQL有以下区别:
ORACLE
试图将其转换成多个表的连接,如果转换不成功则先执行IN里面的子查询,再查询外层的表记录,如果转换成功则直接采用多个表的连接方式查询。由此可见用
IN的SQL至少多了一个转换的过程。一般的SQL都可以转换成功,但对于含有分组统计等方面的SQL就不能转换了。
Oracle在执行IN子查询时,首先执行子查询,将查询结果放入临时表再执行主查询。而EXIST则是首先检查主查询,然后运行子查询直到找到第一个匹
配项。NOT EXISTS比NOT IN效率稍高。但具体在选择IN或EXIST操作时,要根据主子表数据量大小来具体考虑。
推荐方案:在业务密集的SQL当中尽量不采用IN操作符。
- 不用NOT IN操作符,可以用NOT EXISTS或者外连接+替代
此操作是强列推荐不使用的,因为它不能应用表的索引。
推荐方案:用NOT EXISTS 或(外连接+判断为空)方案代替
- 不用“<>”或者“!=”操作符。对不等于操作符的处理会造成全表扫描,可以用“<” or “>”代替
不等于操作符是永远不会用到索引的,因此对它的处理只会产生全表扫描。
推荐方案:用其它相同功能的操作运算代替,如:
1)a<>0 改为 a>0 or a<0
2)a<>’’ 改为 a>’’
- Where子句中出现IS NULL或者IS NOT NULL时,Oracle会停止使用索引而执行全表扫描。可以考虑在设计表时,对索引列设置为NOT NULL。这样就可以用其他操作来取代判断NULL的操作
IS NULL 或IS NOT NULL操作(判断字段是否为空)
判断字段是否为空一般是不会应用索引的,因为B树索引是不索引空值的。
推荐方案:
用其它相同功能的操作运算代替,如:
1)a is not null 改为 a>0 或a>’’等。
2)不允许字段为空,而用一个缺省值代替空值,如业扩申请中状态字段不允许为空,缺省为申请。
3) 建立位图索引(有分区的表不能建,位图索引比较难控制,如字段值太多索引会使性能下降,多人更新操作会增加数据块锁的现象)
- 当通配符“%”或者“_”作为查询字符串的第一个字符时,索引不会被使用
- 对于有连接的列“||”,最后一个连接列索引会无效。尽量避免连接,可以分开连接或者使用不作用在列上的函数替代。
- 如果索引不是基于函数的,那么当在Where子句中对索引列使用函数时,索引不再起作用。
- Where子句中避免在索引列上使用计算,否则将导致索引失效而进行全表扫描。
- 对数据类型不同的列进行比较时,会使索引失效。
- > 及 < 操作符(大于或小于操作符)
大于或小于操作符一般情况下是不用调整的,因为它有索引就会采用索引查找,但有的情况下可以对它进行优化,如一个表有100万记录,一个数值型字段A,
30万记录的A=0,30万记录的A=1,39万记录的A=2,1万记录的A=3。那么执行A>2与A>=3的效果就有很大的区别了,因为
A>2时ORACLE会先找出为2的记录索引再进行比较,而A>=3时ORACLE则直接找到=3的记录索引。
推荐方案:用“>=”替代“>”。
UNION在进行表链接后会筛选掉重复的记录,所以在表链接后会对所产生的结果集进行排序运算,删除重复的记录再返回结果。实际大部分应用中是不会产生重复的记录,最常见的是过程表与历史表UNION。如:
select * from gc_dfys
union
select * from ls_jg_dfys
这个SQL在运行时先取出两个表的结果,再用排序空间进行排序删除重复的记录,最后返回结果集,如果表数据量大的话可能会导致用磁盘进行排序。
推荐方案:采用UNION ALL操作符替代UNION,因为UNION ALL操作只是简单的将两个结果合并后就返回。
select * from gc_dfys
union all
select * from ls_jg_dfys
LIKE 操作符可以应用通配符查询,里面的通配符组合可能达到几乎是任意的查询,但是如果用得不好则会产生性能上的问题,如LIKE
‘%5400%’ 这种查询不会引用索引,而LIKE
‘X5400%’则会引用范围索引。一个实际例子:用YW_YHJBQK表中营业编号后面的户标识号可来查询营业编号 YY_BH LIKE
‘%5400%’ 这个条件会产生全表扫描,如果改成YY_BH LIKE ’X5400%’ OR YY_BH LIKE ’B5400%’
则会利用YY_BH的索引进行两个范围的查询,性能肯定大大提高。
- SQL书写的影响(共享SQL语句可以提高操作效率)
同一功能同一性能不同写法SQL的影响
如一个SQL在A程序员写的为
Select * from zl_yhjbqk
B程序员写的为
Select * from dlyx.zl_yhjbqk(带表所有者的前缀)
C程序员写的为
Select * from DLYX.ZLYHJBQK(大写表名)
D程序员写的为
Select * from DLYX.ZLYHJBQK(中间多了空格)
以上四个SQL在ORACLE分析整理之后产生的结果及执行的时间是一样的,但是从ORACLE共享内存SGA的原理,可以得出ORACLE对每个SQL
都会对其进行一次分析,并且占用共享内存,如果将SQL的字符串及格式写得完全相同则ORACLE只会分析一次,共享内存也只会留下一次的分析结果,这不
仅可以减少分析SQL的时间,而且可以减少共享内存重复的信息,ORACLE也可以准确统计SQL的执行频率。
推荐方案:不同区域出现的相同的Sql语句,要保证查询字符完全相同,以利用SGA共享池,防止相同的Sql语句被多次分析。
Oracle从下到上处理Where子句中多个查询条件,所以表连接语句应写在其他Where条件前,可以过滤掉最大数量记录的条件必须写在Where子句的末尾。
WHERE子句后面的条件顺序对大数据量表的查询会产生直接的影响,如
Select * from zl_yhjbqk where dy_dj = '1KV以下' and xh_bz=1
Select * from zl_yhjbqk where xh_bz=1 and dy_dj = '1KV以下'
以上两个SQL中dy_dj(电压等级)及xh_bz(销户标志)两个字段都没进行索引,所以执行的时候都是全表扫描,第一条SQL的dy_dj =
'1KV以下'条件在记录集内比率为99%,而xh_bz=1的比率只为0.5%,在进行第一条SQL的时候99%条记录都进行dy_dj及xh_bz的
比较,而在进行第二条SQL的时候0.5%条记录都进行dy_dj及xh_bz的比较,以此可以得出第二条SQL的CPU占用率明显比第一条低。
Oracle从右到左处理From子句中的表名,所以在From子句中包含多个表的情况下,将记录最少的表放在最后。(只在采用RBO优化时有效)
在FROM后面的表中的列表顺序会对SQL执行性能影响,在没有索引及ORACLE没有对表进行统计分析的情况下ORACLE会按表出现的顺序进行链接,
由此因为表的顺序不对会产生十分耗服务器资源的数据交叉。(注:如果对表进行了统计分析, ORACLE会自动先进小表的链接,再进行大表的链接)。
- Order By语句中的非索引列会降低性能,可以通过添加索引的方式处理。严格控制在Order By语句中使用表达式
- 当在Sql语句中连接多个表时,使用表的别名,并将之作为每列的前缀。这样可以减少解析时间
- 多利用内部函数提高Sql效率
- SQL语句索引的利用
- 对操作符的优化(见前面)
- 对条件字段的一些优化
如:
substr(hbs_bh,1,4)=’5400’,优化处理:hbs_bh like ‘5400%’
trunc(sk_rq)=trunc(sysdate), 优化处理:sk_rq>=trunc(sysdate) and sk_rq<trunc(sysdate+1)
如:
ss_df+20>50,优化处理:ss_df>30
‘X’||hbs_bh>’X5400021452’,优化处理:hbs_bh>’5400021542’
sk_rq+5=sysdate,优化处理:sk_rq=sysdate-5
hbs_bh=5401002554,优化处理:hbs_bh=’ 5401002554’,注:此条件对hbs_bh 进行隐式的to_number转换,因为hbs_bh字段是字符型。
ys_df>cx_df,无法进行优化
qc_bh||kh_bh=’5400250000’,优化处理:qc_bh=’5400’ and kh_bh=’250000’
可能引起全表扫描的操作
- 在索引列上使用NOT或者“<>”
- 对索引列使用函数或者计算
- NOT IN操作
- 通配符位于查询字符串的第一个字符
- IS NULL或者IS NOT NULL
- 多列索引,但它的第一个列并没有被Where子句引用
ORACLE在SQL执行分析方面已经比较成熟,如果分析执行的路径不对首先应在数据库结构(主要是索引)、服务器当前性能(共享内存、磁盘文件碎片)、数据库对象(表、索引)统计信息是否正确这几方面分析。
原文地址:http://blog.csdn.net/ForWayfarer/archive/2008/09/20/2955812.aspx
1.struts2怎么实现方法签名(类似于struts1的DispatchAction):在strust.xml中配置如下
<action ... method="register" ></action>
<action ... method="login" ></action>
<action ... method="insert" ></action>
(1)struts2怎么实现分发验证:对应于每个方法签名,可以有相应的validate()方法如下
public void validateRegister()
public void validateLogin()
public void validateInsert()
(2)struts2怎么实现分发验证:对应于每个方法签名,可以有相应的验证框架如下
UserAction-register-validation.xml
UserAction-login-validation.xml
UserAction-insert-validation.xml
2.验证顺序
在struts1中,如果在FormBean中覆盖了validate()方法,让其放回null值,则验证框架(xml验证)就会失效。返回
null则代表验证通过or无需验证;覆盖的validate()方法如果返回super.validate(mapping,request)即调用父
类的validate(mapping,request),验证框架才会起作用。也就是说,如果想让验证框架有效,则不要覆盖validate()方法,
如果除了验证框架验证,你还想在validate()方法中添加一些其他验证,可以覆盖validate()方法,但返回值须为
super.validate(mapping,request)。
在struts2中类似。struts2的执行顺序为:
(1). 首先Struts2对客户端传来的数据进行类型转换;
(2). 类型转换完毕后再进行输入校验,顺序:UserAction-validation.xml -->
UserAction-login-validation.xml --> public void validateLogin(){}
--> public void validate(){};
(3). 如果类型转换和输入校验都没有错误发生,那么进入login()方法/execuet()方法(调用商业逻辑).
注意:如果类型转换不成功,也同样要进行输入校验,所以错误信息打印会将二者的错误信息全部打印出来
3.struts2的错误信息
struts2有两种错误级别:addFieldError和addActionError
addActionError(String anErrorMessage) -- Add an Action-level error
message to this
Action:看源代码对于Action级别的错误信息,实际上是放置在ArrayList中的,action级别的error不会在表单中显示。
addFieldError(String fieldName, String errorMessage) -- Add an
error message for a given
field:看源代码真正存放field级别错误信息的对象是LinkedHashMap,该LinkedHashMap的key是String类型
的,value是ArrayList类型的(addFielderror(String,
ArrayList)),实际上是:LinkedHashMap<String, ArrayList<String>>
errors。field级别的error会在表单中显示。
源代码中还有这样一个方法:public boolean hasErrors() { if (hasActionErrors() ||
hasFieldErrors()) ...} 则返回input页面,表示不论是field级别的还是action级别的,只要错误容器不为空,就返回
到input页面。
另外:struts2验证框架(xml验证)是将错误保存在fielderror中的,而不是actionerror。所以在jsp显示错误时要注意。
4.struts2支持客户端验证,s标签依然支持js事件,所以客户端验证推荐自己写js,不过基本不用struts做客户端验证。此外,s标签即使你不给其设置id属性,s标签也会自动生成一个id(通过查看页面源代码(获得s标签的html代码)可获得),如果你自定义了id,则会将s标签自动生成的id覆盖。