提起运算符的优先级,很多了解C++的过来人都会想:这有什么难的?不就是谁的优先级高就算谁么。确实如此,运算符的优先级不是一个大问题,但对于一个初学者来说,却经常轻易在上面迷糊与犯错。而对于一个了解C++的人来说,我相信也会偶然在上面摔倒,不信就继续往下读。
“优先级高的先运算”带来的困惑
C++中运算符的优先级有一张表,表里把运算符进行了分类,这张表是不需要死记硬背的,只要有个大致的轮廓就OK了。例如应该记住最低优先级是逗号运算符,其次是赋值运算符,再其次是三目运算符。而关系运算符的优先级高于逻辑运算符(不包括逻辑非运算),算术运算符的优先级高于关系运算符,象++和﹣﹣的优先级比前面几个都高,但最高的要属()了。知道这些后,你的脑海里一定有一条准则了:优先级高的先运算。那么下面看一个例子:
int x=1,y=0;
!x&&x+y&++y;
上面的语句中出现了!、&& 、+、++这四个运算符,那么问题来了,到底先算谁呢?
有一个姓蔡的同学站起来说,++运算符在这里面优先级最高,理所应当最先算++,既先计算++y,再算!x,再算x+y,最后把它们&&起来。按照蔡同学的思路,第二步的结果是0&&x+y&&1,由于&&是严格运算,有一个为0结果既为0,所以不需要计算x+y了,整个语句的结果是:假。按照上面蔡同学的说法,执行完后y的值应该是1了,这对不对呢?
一位姓高的同学站起来反驳道,我觉得应该先计算!x,假如值为假,则不需要计算下去,最后结果为假。假如值为真,再计算x+y,同理假如其值为真,再去计算++y,否则最后结果也为假。
蔡同学不服起来说,高同学你觉得++和!谁的优先级高呢?高同学答道,那当然是++高。蔡同学接着问,那为什么还要先计算!呢?高同学答不出来了。
是呀,为什么要先算呢? 三层交换技术 交换机与路由器密码恢复 交换机的选购 路由器设置专题 路由故障处理手册 数字化校园网解决方案 加括号确定优先级的方法
高同学说的是正确的,为什么呢?下面我给大家解释一下。当多个优先级不同的运算符在一起时,为了不混淆,可以先加上括号,这样就分出层次了,相同层次的考虑结合性问题,当确定下来先算那块时,再往这块里面深入。例如上面的例子,我们可以这样加上括号:从左向右看,由于!比&&优先级高,所以有(!x),又由于&&比+优先级低,所以有(x+y),而++优先级高于&&,所以(++y)。这样整个式子就变成了:(!x)&&(x+y)&&(++y),最外层的是两个&&运算,由于&&的结合性是从左至右,所以上式可看成:A&&B&&C,先计算A,再计算B,最后算C。由于x=1,则!x就为假,后面的就不需要再算了,整个语句的值为假。执行完后,y的值没变,还是0。
所以碰到不清楚先算谁后算谁时,先加个括号看看,就明白了先后次序。下面做一个加括号的练习:给语句c=a>b?a:b;加括号。此语句有三个运算符:=、>、? :,应该怎样加括号呢?
第一种方案:c=((a>b)?a:b);
第二种方案:c=(a>(b?a:b));
第三种方案:(c=a)>(b?a:b);
应该是那一种呢?按照运算符优先级的高低顺序,>优先级高于=,所以不可能把(c=a)括起来。而>优先级高于? :运算符。所以也不可能把(b?a:b)括起来。因此,第一种答案正确。
下面再看一个类似的例子:
int i=8,j=4,k;
k=i
猛然一看,有些人上来可能就要计算++i和++j了。这里不妨先加括号看看。从左至右看,<的优先级高于=而且又高于? :,所以有k=(i
所以运算符的优先级千万要小心,既不是想象的那么难,也不是想象的那么轻易。
“优先级高的先运算”带来的困惑
C++中运算符的优先级有一张表,表里把运算符进行了分类,这张表是不需要死记硬背的,只要有个大致的轮廓就OK了。例如应该记住最低优先级是逗号运算符,其次是赋值运算符,再其次是三目运算符。而关系运算符的优先级高于逻辑运算符(不包括逻辑非运算),算术运算符的优先级高于关系运算符,象++和﹣﹣的优先级比前面几个都高,但最高的要属()了。知道这些后,你的脑海里一定有一条准则了:优先级高的先运算。那么下面看一个例子:
int x=1,y=0;
!x&&x+y&++y;
上面的语句中出现了!、&& 、+、++这四个运算符,那么问题来了,到底先算谁呢?
有一个姓蔡的同学站起来说,++运算符在这里面优先级最高,理所应当最先算++,既先计算++y,再算!x,再算x+y,最后把它们&&起来。按照蔡同学的思路,第二步的结果是0&&x+y&&1,由于&&是严格运算,有一个为0结果既为0,所以不需要计算x+y了,整个语句的结果是:假。按照上面蔡同学的说法,执行完后y的值应该是1了,这对不对呢?
一位姓高的同学站起来反驳道,我觉得应该先计算!x,假如值为假,则不需要计算下去,最后结果为假。假如值为真,再计算x+y,同理假如其值为真,再去计算++y,否则最后结果也为假。
蔡同学不服起来说,高同学你觉得++和!谁的优先级高呢?高同学答道,那当然是++高。蔡同学接着问,那为什么还要先计算!呢?高同学答不出来了。
是呀,为什么要先算呢? 三层交换技术 交换机与路由器密码恢复 交换机的选购 路由器设置专题 路由故障处理手册 数字化校园网解决方案 加括号确定优先级的方法
高同学说的是正确的,为什么呢?下面我给大家解释一下。当多个优先级不同的运算符在一起时,为了不混淆,可以先加上括号,这样就分出层次了,相同层次的考虑结合性问题,当确定下来先算那块时,再往这块里面深入。例如上面的例子,我们可以这样加上括号:从左向右看,由于!比&&优先级高,所以有(!x),又由于&&比+优先级低,所以有(x+y),而++优先级高于&&,所以(++y)。这样整个式子就变成了:(!x)&&(x+y)&&(++y),最外层的是两个&&运算,由于&&的结合性是从左至右,所以上式可看成:A&&B&&C,先计算A,再计算B,最后算C。由于x=1,则!x就为假,后面的就不需要再算了,整个语句的值为假。执行完后,y的值没变,还是0。
所以碰到不清楚先算谁后算谁时,先加个括号看看,就明白了先后次序。下面做一个加括号的练习:给语句c=a>b?a:b;加括号。此语句有三个运算符:=、>、? :,应该怎样加括号呢?
第一种方案:c=((a>b)?a:b);
第二种方案:c=(a>(b?a:b));
第三种方案:(c=a)>(b?a:b);
应该是那一种呢?按照运算符优先级的高低顺序,>优先级高于=,所以不可能把(c=a)括起来。而>优先级高于? :运算符。所以也不可能把(b?a:b)括起来。因此,第一种答案正确。
下面再看一个类似的例子:
int i=8,j=4,k;
k=i
猛然一看,有些人上来可能就要计算++i和++j了。这里不妨先加括号看看。从左至右看,<的优先级高于=而且又高于? :,所以有k=(i
所以运算符的优先级千万要小心,既不是想象的那么难,也不是想象的那么轻易。
快下班了,写点鉴赏的东西:
歌曲名称:千年之恋
歌手名称:信乐团
谁在悬崖沏一壶茶 我个人觉得应该修改为心在悬崖沏一壶茶,谁不能代表谁,而心更加能说明人的主观性和浪漫的情感。
温热前世的牵挂 谁又愿意不相信前世,更加主主观,人们往往喜欢,就像我宁愿相信主一样,尽管我是学物理的:)
而我在调整千年的时差 为何要调整时差呢?是恨和爱的铰接,还是对离得太远的恋人们的一种对互相的期盼吗?
爱恨全喝下 这一步说明还很年轻,爱河的水还浅,喝光了看似自己看透的爱和恨。
岁月在岩石上敲打 时间在恋人们的慢慢的走过。
我又留长了头发 又字,体现了一个人对一种状态的回归,长:说明了痴情不改、
耐心等待海岸线的变化 已经死了心的他或她已经高度成熟?应该仅仅还是一种境界的提升罢了。
大雨就要下 对心境的描述,但为何用大雨来寄托。
风 狠狠的刮 风,本来无情,为何用了人类的狠狠来表态呢?更进一步。
谁 在害怕 这句话,却反倒道出了人的狠心。
海风一直眷恋着沙 说明本来我们该在一起,很是眷恋的一种唯美情感表达。
你却错过我的年华 错过了,路过了,就像死了心还在的感觉。
错过我新长的枝丫 为何会新长呢?说的是有些时候当时没有抓住哦,而自己一直在改变,而来不及了的一种深刻的埋怨,极度的嫉狠。
和我的白发
蝴蝶依旧狂恋着花 自然现象,赋 比 兴 中兴的手法,Next(蝶恋花)
你却错过我的年华
错过我转世的脸颊 任然相信,但人已经各有主了吧,估计!
你还爱我吗 呵呵,心软了
我等你一句话 这句话,是爱恨的焦点,全局。
一生行走望断天崖 泪水已经已经盲目了。。。。。下句全是。。。
最远不过是晚霞
而你今生又在哪户人家
欲语泪先下
沙滩上消失的浪花
让我慢慢想起家
曾经许下的永远又在哪
总是放不下
啊 轮回的记忆在风化
我将它牢牢记下
海风一直眷恋着沙
你却错过我的年华
错过我新长的枝丫
和我的白发
蝴蝶依旧狂恋着花
你却错过我的年华
错过我转世的脸颊
你还爱我吗
我等你一句话
蝴蝶依旧狂恋着花
你却错过我的年华
错过我转世的脸颊
你还爱我吗
我等你一句话
歌曲名称:千年之恋
歌手名称:信乐团
谁在悬崖沏一壶茶 我个人觉得应该修改为心在悬崖沏一壶茶,谁不能代表谁,而心更加能说明人的主观性和浪漫的情感。
温热前世的牵挂 谁又愿意不相信前世,更加主主观,人们往往喜欢,就像我宁愿相信主一样,尽管我是学物理的:)
而我在调整千年的时差 为何要调整时差呢?是恨和爱的铰接,还是对离得太远的恋人们的一种对互相的期盼吗?
爱恨全喝下 这一步说明还很年轻,爱河的水还浅,喝光了看似自己看透的爱和恨。
岁月在岩石上敲打 时间在恋人们的慢慢的走过。
我又留长了头发 又字,体现了一个人对一种状态的回归,长:说明了痴情不改、
耐心等待海岸线的变化 已经死了心的他或她已经高度成熟?应该仅仅还是一种境界的提升罢了。
大雨就要下 对心境的描述,但为何用大雨来寄托。
风 狠狠的刮 风,本来无情,为何用了人类的狠狠来表态呢?更进一步。
谁 在害怕 这句话,却反倒道出了人的狠心。
海风一直眷恋着沙 说明本来我们该在一起,很是眷恋的一种唯美情感表达。
你却错过我的年华 错过了,路过了,就像死了心还在的感觉。
错过我新长的枝丫 为何会新长呢?说的是有些时候当时没有抓住哦,而自己一直在改变,而来不及了的一种深刻的埋怨,极度的嫉狠。
和我的白发
蝴蝶依旧狂恋着花 自然现象,赋 比 兴 中兴的手法,Next(蝶恋花)
你却错过我的年华
错过我转世的脸颊 任然相信,但人已经各有主了吧,估计!
你还爱我吗 呵呵,心软了
我等你一句话 这句话,是爱恨的焦点,全局。
一生行走望断天崖 泪水已经已经盲目了。。。。。下句全是。。。
最远不过是晚霞
而你今生又在哪户人家
欲语泪先下
沙滩上消失的浪花
让我慢慢想起家
曾经许下的永远又在哪
总是放不下
啊 轮回的记忆在风化
我将它牢牢记下
海风一直眷恋着沙
你却错过我的年华
错过我新长的枝丫
和我的白发
蝴蝶依旧狂恋着花
你却错过我的年华
错过我转世的脸颊
你还爱我吗
我等你一句话
蝴蝶依旧狂恋着花
你却错过我的年华
错过我转世的脸颊
你还爱我吗
我等你一句话
body{overflow-x:hidden;width:98%;}
程序如:
number_format.php :
用正则的方法进行分割:
---------- 调试PHP ----------
1,234.56
输出完成 (耗时 0 秒) - 正常终止
要把SQL查询出来的数值以下面的方法显示
1122222.21 显示为1,122,222.21
用number_format函数这么写啊?!
各位最好给个代码,谢谢
$number=1122222.21;
echo number_format($number, 2, ".", ",");
我需要:
$result=array("day_key"=>$rs->fields['day_key'],"week"=>$rs->fields['week'],"pv"=>number_format($rs->fields['pv'], 0, "", ","),"uip"=>number_format($rs->fields['uip'], 0, "", ","),"reg_member"=>number_format($rs->fields['reg_member'], 0, "", ","),"upload_ct"=>number_format($rs->fields['upload_ct'], 0, "", ","),"play_ct"=>number_format($rs->fields['play_ct'], 0, "", ","),"video_total"=>number_format($rs->fields['video_total'], 0, "", ","),"play_time"=>number_format($rs->fields['play_time'], 0, "", ","));
参考:http://www.phpe.net/manual/function.number-format.php
首个字母大写:
执行后,如下:
再参考:http://www.jb51.net/article/30478.htm
number_format.php :
用正则的方法进行分割:
---------- 调试PHP ----------
1,234.56
输出完成 (耗时 0 秒) - 正常终止
要把SQL查询出来的数值以下面的方法显示
1122222.21 显示为1,122,222.21
用number_format函数这么写啊?!
各位最好给个代码,谢谢
$number=1122222.21;
echo number_format($number, 2, ".", ",");
我需要:
$result=array("day_key"=>$rs->fields['day_key'],"week"=>$rs->fields['week'],"pv"=>number_format($rs->fields['pv'], 0, "", ","),"uip"=>number_format($rs->fields['uip'], 0, "", ","),"reg_member"=>number_format($rs->fields['reg_member'], 0, "", ","),"upload_ct"=>number_format($rs->fields['upload_ct'], 0, "", ","),"play_ct"=>number_format($rs->fields['play_ct'], 0, "", ","),"video_total"=>number_format($rs->fields['video_total'], 0, "", ","),"play_time"=>number_format($rs->fields['play_time'], 0, "", ","));
参考:http://www.phpe.net/manual/function.number-format.php
首个字母大写:
执行后,如下:
再参考:http://www.jb51.net/article/30478.htm
很多网页都是框架结构的,在很多的情况下会通过按钮点击事件或链接,跳出框架转到其它界面。
例如说点击“注销登录”返回到登录界面。
一、通过运行脚本跳出框架有以下几种写法:
1. <script language = javascript>window.open('Login.aspx','_top')</script>"
2. <script language = javascript>window.open('Login.aspx','_parent')</script>"
3. <script language = javascript>window.parent.location.href='login.aspx'</script>
4. Response.Write("<script>window.parent.opener=null;window.top.close();</script>")
Response.Write("<script>window.open('index.aspx','');</script>")
这种方法会先关闭原框架窗口,再重新打开一个新的窗口。这在很多功能界面对浏览器进行了改变设置,而回到登陆界面又用缺省设置的情况下适用。
二、链接跳出框架
这种情况就很简单了,加上 target="_top" 属性就可以了。
例如说点击“注销登录”返回到登录界面。
一、通过运行脚本跳出框架有以下几种写法:
1. <script language = javascript>window.open('Login.aspx','_top')</script>"
2. <script language = javascript>window.open('Login.aspx','_parent')</script>"
3. <script language = javascript>window.parent.location.href='login.aspx'</script>
4. Response.Write("<script>window.parent.opener=null;window.top.close();</script>")
Response.Write("<script>window.open('index.aspx','');</script>")
这种方法会先关闭原框架窗口,再重新打开一个新的窗口。这在很多功能界面对浏览器进行了改变设置,而回到登陆界面又用缺省设置的情况下适用。
二、链接跳出框架
这种情况就很简单了,加上 target="_top" 属性就可以了。
得高人(easy)指点,我试过,这个方法真好用!
解决办法是:
UPDATE mysql.user SET Password = OLD_PASSWORD('newpwd')
WHERE User = 'usrname';
各位看官,注意到 OLD_PASSWORD( ) 这个函数么?
easy 高!
不過還得刷新,加以下一句:
mysql> FLUSH PRIVILEGES;
新版Mysql密码算法不同导致“Client does not support authentication protocol requested by server”错误
MySql4.1.7密码算法不同,你可以做如下选择:
1)使用PHP5,用php_mysqli.dll,而不是以前的php_mysql.dll
2)将MySql的密码改用原来的那套算法(本文介绍)
以前一直用Mysql 3.23.44,PHP5正式发布后,喜欢尝鲜的我忍不住了,用了几天PHP5,才感觉我的Mysql太弱,于是乎升级我的Mysql,心一横,最新的Mysql 5.0.1,嘿嘿,不信数据库弱。
安装很顺利,登录到mysql
G:\Mysql\bin>mysql -uroot
mysql>......
然后删除一些不用的帐号,只留下了一个root帐号。
更新root密码:
mysql>update user set password=password('password') where host='localhost' and user='root';
成功!
退出并重新启动Mysql服务。
接下来配置phpMyadmin的Config.inc.php
修改Mysql用户名、密码
打开phpMyadmin,登录:
结果很意外竟然弹出错误信息:
Client does not support authentication protocol requested by server;
意思好像是需要我升级客户端,我彻底失望了,我以为Mysql 5.0.1也和3.23.44一样好安装。
后来,我用Google搜索错误信息中的部分内容,搜索到了Mysql的官方网站,一看,明白了,原来是password算法的问题,因为5.0.1版Mysql的password算法和3.23.44的算法不一样了
(原文地址:http://dev.mysql.com/doc/mysql/en/Old_client.html)
采取官方网站的解决办法,一次搞定,嘿嘿
mysql>SET PASSWORD FOR
>'root'@'localhost' = OLD_PASSWORD('newpassword');
写此文章,仅希望有同样问题的朋友能尽快解决此问题。
今天又用一种方法更改了密码,因为现在才知道以前的命令用错了,old-password要直接输入才对!
今天的方法是,update mysql.user set password = old_password('newpwd')
where host = 'localhost' and user = 'root';
然后flush priviledges;
ok,重新登陆,搞定!
解决办法是:
UPDATE mysql.user SET Password = OLD_PASSWORD('newpwd')
WHERE User = 'usrname';
各位看官,注意到 OLD_PASSWORD( ) 这个函数么?
easy 高!
不過還得刷新,加以下一句:
mysql> FLUSH PRIVILEGES;
新版Mysql密码算法不同导致“Client does not support authentication protocol requested by server”错误
MySql4.1.7密码算法不同,你可以做如下选择:
1)使用PHP5,用php_mysqli.dll,而不是以前的php_mysql.dll
2)将MySql的密码改用原来的那套算法(本文介绍)
以前一直用Mysql 3.23.44,PHP5正式发布后,喜欢尝鲜的我忍不住了,用了几天PHP5,才感觉我的Mysql太弱,于是乎升级我的Mysql,心一横,最新的Mysql 5.0.1,嘿嘿,不信数据库弱。
安装很顺利,登录到mysql
G:\Mysql\bin>mysql -uroot
mysql>......
然后删除一些不用的帐号,只留下了一个root帐号。
更新root密码:
mysql>update user set password=password('password') where host='localhost' and user='root';
成功!
退出并重新启动Mysql服务。
接下来配置phpMyadmin的Config.inc.php
修改Mysql用户名、密码
打开phpMyadmin,登录:
结果很意外竟然弹出错误信息:
Client does not support authentication protocol requested by server;
意思好像是需要我升级客户端,我彻底失望了,我以为Mysql 5.0.1也和3.23.44一样好安装。
后来,我用Google搜索错误信息中的部分内容,搜索到了Mysql的官方网站,一看,明白了,原来是password算法的问题,因为5.0.1版Mysql的password算法和3.23.44的算法不一样了
(原文地址:http://dev.mysql.com/doc/mysql/en/Old_client.html)
采取官方网站的解决办法,一次搞定,嘿嘿
mysql>SET PASSWORD FOR
>'root'@'localhost' = OLD_PASSWORD('newpassword');
写此文章,仅希望有同样问题的朋友能尽快解决此问题。
今天又用一种方法更改了密码,因为现在才知道以前的命令用错了,old-password要直接输入才对!
今天的方法是,update mysql.user set password = old_password('newpwd')
where host = 'localhost' and user = 'root';
然后flush priviledges;
ok,重新登陆,搞定!
http://www.ooso.net/index.php/archives/140
在friendfeed上找到的了这个名为FirePHP的Firefox插件,是基于Firebug的一个扩展,可以用来在Firebug的console中方便的输出php的调试信息又不影响php程序的正常运行。实际上这东东出来的时间也不短了,只是以前没试用过,现在把玩了一会也挺有趣:
Http://www.firephp.org/
FirePHP的php调试信息都是通过在http头里面添加X-FirePHP-Data信息串来标识,不会直接输出到页面上,这样也就避免对php正常输出产生影响。可以输出的调试信息类型如下:
* 正常的调试字符串,类型有LOG,INFO,WARN,ERROR几种
* 数组array
* object
* 异常Exception
* SQL返回数据
* http header
通过使用Firephp你可以在Firebug的Console栏中看到要调试的数据,而不影响php程序的正常执行,所以说,这东西对于Ajax开发是很有帮助的!
如何使用:http://www.blankyao.cn/blog/php-firephp-ajax.html
firebug firecookie ietab httpfox
下面一起来看下Firephp的使用方法。
第一步:安装
1.如果你的FireFox没有Firebug这个插件的话,首先要安装Firebug这个插件,可以到其官方地址去下载:http://www.getfirebug.com/
2.安装Firephp,官方地址:http://www.firephp.org/
3.下载Firephp的php文件。并放在合适的目录。
第二步:
包含fb.php,根据你放置的Firephp文件的地址来包含fb.php,比如:
require(’FirePHPCore/fb.php’)
第三步:
打开输出缓冲(因为Firephp主要用到的是header函数),有如下三种方法:
在程序的前面加上ob_start()
修改php.ini 将output_buffering设为1或者on
修改apache的设置,在配置文件中加上php_flag output_buffering on
第四步:开始调试:
可以调试输出以下数据类型:
字符串,可以分为LOG,INFO,WARN,ERROR四种
Object或者Array
通过sql查询返回的数据
抛出的异常信息
服务器返回的信息(不输出在console中,而是NET中
如果你感觉还不错的话,可以安装后运行以下程序看下结果:
$var = array('a'=>'pizza', 'b'=>'cookies', 'c'=>'celery');
fb($var);
fb($var, "An array");
fb($var, FirePHP::WARN);
fb($var, FirePHP::INFO);
fb($var, 'An array with an Error type', FirePHP::ERROR);
你也可以使用FirePHP来跟踪你程序的执行情况:通过使用FirePHP::TRACE常量,你可以在 fb被调用的地方查看行数、类名和方法名
function hello() {
fb('Hello World!', FirePHP::TRACE);
}
function greet() {
hello();
}
greet();
产生的输出如下:
这个跟踪功能可以完美的调试更复杂的代码,让你精确的知道你的方法是在哪里被调用的。
当然,别忘了你需要在你代码发布之前移除你的调试语句。
参考:http://blog.csdn.net/leijuly/archive/2009/05/31/4227613.aspx
自己测试了下,是Ok的,以后就可以用它来调试PHP变量的哇,测试代码如下:
firephp的Consol输出结果如下:
http://test3.qq.com/firephp_test3.php
2 SQL queries took 0.06 seconds
SQL Statement Time Result
SELECT * FROM Foo 0.02 array('0'=>' row1 ', '1'=>' row2 ')
SELECT * FROM Bar 0.04 array('0'=>' row1 ', '1'=>' row2 ')
自己试了下也是Ok的,如下:
name
jack lixu
echo xxd
当firefox升级到firefox6后,这个firephp和firebug匹配上有些问题,只能开启一次,再刷新后控制台就给屏蔽了,点再开启也不行,最后再次升级了firebug插件才Ok,这点要注意。
以上的输出截图,注意table和log,以及info是很适用的输出模型:
特别注意,如出现:
其中某个包含文件中如,password.php,最后两行,vi能看到一行空输出,91行:
90 ?>
91
这儿有一行空输出,一般是看不见的。
这儿有空格输出,于是出现了报错:
Fatal error: Uncaught exception 'Exception' with message 'Headers already sent in /data/home/jackxiang/public_html/pms_proj/trunk/php/lib/password.php on line 92. Cannot send log data to FirePHP. You must have Output Buffering enabled via ob_start() or output_buffering ini directive.' in /data/home/admin/libs/FirePHPCore/FirePHP.class.php on line 1178
这儿得用到前面的:
第三步:
打开输出缓冲(因为Firephp主要用到的是header函数),有如下三种方法:
在程序的前面加上ob_start()
修改php.ini 将output_buffering设为1或者on
修改apache的设置,在配置文件中加上php_flag output_buffering on
其实就是在password.php中输出前加入,ob_start(); #开启缓冲,然后在打firephp变量前加入:
ob_clean(); #关闭缓冲
分别如下:password.php,
89 ob_start();
90 ?>
91
在打印变量处如,BuildProcess.php中:
于是出现如下结果:
其实这个为何要这样加,或者就是头一个文件里里加的原因请看:http://wyllife.blog.163.com/blog/static/41163901201153113512946/
Http://www.firephp.org/
FirePHP的php调试信息都是通过在http头里面添加X-FirePHP-Data信息串来标识,不会直接输出到页面上,这样也就避免对php正常输出产生影响。可以输出的调试信息类型如下:
* 正常的调试字符串,类型有LOG,INFO,WARN,ERROR几种
* 数组array
* object
* 异常Exception
* SQL返回数据
* http header
通过使用Firephp你可以在Firebug的Console栏中看到要调试的数据,而不影响php程序的正常执行,所以说,这东西对于Ajax开发是很有帮助的!
如何使用:http://www.blankyao.cn/blog/php-firephp-ajax.html
<?php
require('FirePHPCore/fb.php');
//echo 'FirePHP测试';
//echo "hello the world!";
fb('Hello World'); /* Defaults to FirePHP::LOG */
fb('Log message' ,FirePHP::LOG);
fb('Info message' ,FirePHP::INFO);
fb('Warn message' ,FirePHP::WARN);
fb('Error message',FirePHP::ERROR);
fb('Message with label','Label',FirePHP::LOG);
fb(array('key1'=>'val1',
'key2'=>array(array('v1','v2'),'v3')),
'TestArray',FirePHP::LOG);
function test($Arg1) {
throw new Exception('Test Exception');
}
try {
test(array('Hello'=>'World'));
} catch(Exception $e) {
/* Log exception including stack trace & variables */
fb($e);
}
fb(array('2 SQL queries took 0.06 seconds',array(
array('SQL Statement','Time','Result'),
array('SELECT * FROM Foo','0.02',array('row1','row2')),
array('SELECT * FROM Bar','0.04',array('row1','row2'))
)),FirePHP::TABLE);
?>
require('FirePHPCore/fb.php');
//echo 'FirePHP测试';
//echo "hello the world!";
fb('Hello World'); /* Defaults to FirePHP::LOG */
fb('Log message' ,FirePHP::LOG);
fb('Info message' ,FirePHP::INFO);
fb('Warn message' ,FirePHP::WARN);
fb('Error message',FirePHP::ERROR);
fb('Message with label','Label',FirePHP::LOG);
fb(array('key1'=>'val1',
'key2'=>array(array('v1','v2'),'v3')),
'TestArray',FirePHP::LOG);
function test($Arg1) {
throw new Exception('Test Exception');
}
try {
test(array('Hello'=>'World'));
} catch(Exception $e) {
/* Log exception including stack trace & variables */
fb($e);
}
fb(array('2 SQL queries took 0.06 seconds',array(
array('SQL Statement','Time','Result'),
array('SELECT * FROM Foo','0.02',array('row1','row2')),
array('SELECT * FROM Bar','0.04',array('row1','row2'))
)),FirePHP::TABLE);
?>
firebug firecookie ietab httpfox
下面一起来看下Firephp的使用方法。
第一步:安装
1.如果你的FireFox没有Firebug这个插件的话,首先要安装Firebug这个插件,可以到其官方地址去下载:http://www.getfirebug.com/
2.安装Firephp,官方地址:http://www.firephp.org/
3.下载Firephp的php文件。并放在合适的目录。
第二步:
包含fb.php,根据你放置的Firephp文件的地址来包含fb.php,比如:
require(’FirePHPCore/fb.php’)
第三步:
打开输出缓冲(因为Firephp主要用到的是header函数),有如下三种方法:
在程序的前面加上ob_start()
修改php.ini 将output_buffering设为1或者on
修改apache的设置,在配置文件中加上php_flag output_buffering on
第四步:开始调试:
可以调试输出以下数据类型:
字符串,可以分为LOG,INFO,WARN,ERROR四种
Object或者Array
通过sql查询返回的数据
抛出的异常信息
服务器返回的信息(不输出在console中,而是NET中
如果你感觉还不错的话,可以安装后运行以下程序看下结果:
$var = array('a'=>'pizza', 'b'=>'cookies', 'c'=>'celery');
fb($var);
fb($var, "An array");
fb($var, FirePHP::WARN);
fb($var, FirePHP::INFO);
fb($var, 'An array with an Error type', FirePHP::ERROR);
你也可以使用FirePHP来跟踪你程序的执行情况:通过使用FirePHP::TRACE常量,你可以在 fb被调用的地方查看行数、类名和方法名
function hello() {
fb('Hello World!', FirePHP::TRACE);
}
function greet() {
hello();
}
greet();
产生的输出如下:
这个跟踪功能可以完美的调试更复杂的代码,让你精确的知道你的方法是在哪里被调用的。
当然,别忘了你需要在你代码发布之前移除你的调试语句。
参考:http://blog.csdn.net/leijuly/archive/2009/05/31/4227613.aspx
自己测试了下,是Ok的,以后就可以用它来调试PHP变量的哇,测试代码如下:
firephp的Consol输出结果如下:
http://test3.qq.com/firephp_test3.php
2 SQL queries took 0.06 seconds
SQL Statement Time Result
SELECT * FROM Foo 0.02 array('0'=>' row1 ', '1'=>' row2 ')
SELECT * FROM Bar 0.04 array('0'=>' row1 ', '1'=>' row2 ')
自己试了下也是Ok的,如下:
name
jack lixu
echo xxd
当firefox升级到firefox6后,这个firephp和firebug匹配上有些问题,只能开启一次,再刷新后控制台就给屏蔽了,点再开启也不行,最后再次升级了firebug插件才Ok,这点要注意。
以上的输出截图,注意table和log,以及info是很适用的输出模型:
特别注意,如出现:
其中某个包含文件中如,password.php,最后两行,vi能看到一行空输出,91行:
90 ?>
91
这儿有一行空输出,一般是看不见的。
这儿有空格输出,于是出现了报错:
Fatal error: Uncaught exception 'Exception' with message 'Headers already sent in /data/home/jackxiang/public_html/pms_proj/trunk/php/lib/password.php on line 92. Cannot send log data to FirePHP. You must have Output Buffering enabled via ob_start() or output_buffering ini directive.' in /data/home/admin/libs/FirePHPCore/FirePHP.class.php on line 1178
这儿得用到前面的:
第三步:
打开输出缓冲(因为Firephp主要用到的是header函数),有如下三种方法:
在程序的前面加上ob_start()
修改php.ini 将output_buffering设为1或者on
修改apache的设置,在配置文件中加上php_flag output_buffering on
其实就是在password.php中输出前加入,ob_start(); #开启缓冲,然后在打firephp变量前加入:
ob_clean(); #关闭缓冲
分别如下:password.php,
89 ob_start();
90 ?>
91
在打印变量处如,BuildProcess.php中:
于是出现如下结果:
其实这个为何要这样加,或者就是头一个文件里里加的原因请看:http://wyllife.blog.163.com/blog/static/41163901201153113512946/
之前一直是editplus的忠实爱好者,对editplus的使用也是小有心得.自打转向到vim之后,费了很大劲才转换一些习惯,渐渐的脱离了editplus,可见一款趁手的编辑器还是粘性很强的.以前说到的editplus可以和zend studio自带的ZendCodeAnalyzer工具进行整合,对php语法进行检测,相当方便.那么怎样用vim实现类似的功能呢?一番搜索,找到了答案:
map <C-J> :!php -l %<CR>
这样在vim里面就可以直接用ctrl+j,调用php解析器对当前文档进行语法检查了.当然如果php解析器不在你的路径下的话,那么应该写上全路径,象下面这样:
map <C-J> :!c:/php/php -l %<CR>
PS:vim的语法美化功能也很方便,只要在程序的第一行,command模式下输入=:1,$,就可以把当前代码排列的井然有序.而editplus则需要另外的工具配合,也能做到这一点.
参考资料:http://www.vim.org/tips/tip.php?tip_id=692
怎么我在vim中不能用你所说的 =:1,$ 来美化PHP代码呢?
难道要先装zendcodeanalyzer?
volcano 于 2008-05-20 @ 00:24:37 留言 :
不需要装zendcodeanalyzer。
首先你需要跳到代码的第一行,然后再输入=:1,$
Jeffery 于 2008-05-20 @ 10:54:57 留言 :
嗯,可以了,我之前是输了:再输的=:1,$
但是这样搞,好像只做了缩进..不知道还有没有其他的参数..
volcano 于 2008-05-20 @ 11:00:12 留言 :
我是这样理解的,如果你输入了=号,那就表示你要做代码自动缩进的操作,后面输入需要操作的行数范围即可
wen 于 2008-05-30 @ 12:28:10 留言 :
你好!看了你php语法检查,确实很有用谢谢!
但是总觉得没吃都要调用cmd,有点丑,可否用QUICKFIX窗口输出
我在网上看了一篇文章 ,他是用quickfix输出,但是我试了一下,不行。
代码如下,你能否改改。谢谢
“”"”"”"”"”"”"”"”"”"”"”"”"”"”"”"”"”"”"”"”"”"”"”"”"”"”"
” => Check PHP Syntax using makeprg
“”"”"”"”"”"”"”"”"”"”"”"”"”"”"”"”"”"”"”"”"”"”"”"”"”"”"
function! PhpCheckSyntax()
” Check php syntax
setlocal makeprg=\”C:\php5\php.exe\”\ -l\ -n\ -d\ html_errors=off
” Set shellpipe
setlocal shellpipe=>
” Use error format for parsing PHP error output
setlocal errorformat=%m\ in\ %f\ on\ line\ %l
make %
endfunction
” Perform :PhpCheckSyntax()
map :call PhpCheckSyntax()
imap :call PhpCheckSyntax()
volcano 于 2008-05-30 @ 13:39:33 留言 :
你机器上的php.exe在这个位置么?C:\php5\php.exe
map <C-J> :!php -l %<CR>
这样在vim里面就可以直接用ctrl+j,调用php解析器对当前文档进行语法检查了.当然如果php解析器不在你的路径下的话,那么应该写上全路径,象下面这样:
map <C-J> :!c:/php/php -l %<CR>
PS:vim的语法美化功能也很方便,只要在程序的第一行,command模式下输入=:1,$,就可以把当前代码排列的井然有序.而editplus则需要另外的工具配合,也能做到这一点.
参考资料:http://www.vim.org/tips/tip.php?tip_id=692
怎么我在vim中不能用你所说的 =:1,$ 来美化PHP代码呢?
难道要先装zendcodeanalyzer?
volcano 于 2008-05-20 @ 00:24:37 留言 :
不需要装zendcodeanalyzer。
首先你需要跳到代码的第一行,然后再输入=:1,$
Jeffery 于 2008-05-20 @ 10:54:57 留言 :
嗯,可以了,我之前是输了:再输的=:1,$
但是这样搞,好像只做了缩进..不知道还有没有其他的参数..
volcano 于 2008-05-20 @ 11:00:12 留言 :
我是这样理解的,如果你输入了=号,那就表示你要做代码自动缩进的操作,后面输入需要操作的行数范围即可
wen 于 2008-05-30 @ 12:28:10 留言 :
你好!看了你php语法检查,确实很有用谢谢!
但是总觉得没吃都要调用cmd,有点丑,可否用QUICKFIX窗口输出
我在网上看了一篇文章 ,他是用quickfix输出,但是我试了一下,不行。
代码如下,你能否改改。谢谢
“”"”"”"”"”"”"”"”"”"”"”"”"”"”"”"”"”"”"”"”"”"”"”"”"”"”"
” => Check PHP Syntax using makeprg
“”"”"”"”"”"”"”"”"”"”"”"”"”"”"”"”"”"”"”"”"”"”"”"”"”"”"
function! PhpCheckSyntax()
” Check php syntax
setlocal makeprg=\”C:\php5\php.exe\”\ -l\ -n\ -d\ html_errors=off
” Set shellpipe
setlocal shellpipe=>
” Use error format for parsing PHP error output
setlocal errorformat=%m\ in\ %f\ on\ line\ %l
make %
endfunction
” Perform :PhpCheckSyntax()
map :call PhpCheckSyntax()
imap :call PhpCheckSyntax()
volcano 于 2008-05-30 @ 13:39:33 留言 :
你机器上的php.exe在这个位置么?C:\php5\php.exe
刚看了看分页!一直以为那个挺麻烦!也一直懒得看 今天看了看 还成!
现在有个问题:
$page = (isset($_GET['page']))?intval($_GET['page']):0;
怎样判断$page是不是个数字!!不知道用什么函数了
因为前面已经intval了!`不会正则!!谁说下
我也来说两句 查看全部评论 相关评论
fyland (2008-4-17 22:48:59)
is_numeric
CrossMaya (2008-4-17 22:55:13)
QUOTE:
原帖由 fyland 于 2008-4-17 22:48 发表
is_numeric
不行
我连is_int都试了
襣吇揷蔥の犭者 (2008-4-24 17:52:56)
就是用is_numeric没必要用正则
lince343 (2008-4-24 18:20:13)
$_GET的东西好像是以字符串形式存在的,is_numeric没用
不管page是不是数字,用过intval($_GET['page'])后都会成为数字
piaomiao163 (2008-4-24 19:18:48)
xiaojie515 (2008-6-25 20:13:21)
ctype_digit()
pylong (2008-6-25 20:16:29)
先intval,再is_int
浮点数另外处理
beilee80 (2008-6-25 21:13:07)
来个简单的,有点小漏洞,但对于page来说已经足够了,呵呵
if ( 0 + $_GET['page'] <= 0 ) echo 'error';
yingwei13 (2008-6-25 22:01:06)
用正则好了,最快
if (!ereg("^[0-9]*$",$_GET['page'] )) echo "false";
qianziai0912 (2008-6-26 17:04:41)
????????????????????不管page是不是整数,intval()都要转换成整数,还判断什么?
intval()
变量转成整数类型。
语
现在有个问题:
$page = (isset($_GET['page']))?intval($_GET['page']):0;
怎样判断$page是不是个数字!!不知道用什么函数了
因为前面已经intval了!`不会正则!!谁说下
我也来说两句 查看全部评论 相关评论
fyland (2008-4-17 22:48:59)
is_numeric
CrossMaya (2008-4-17 22:55:13)
QUOTE:
原帖由 fyland 于 2008-4-17 22:48 发表
is_numeric
不行
我连is_int都试了
襣吇揷蔥の犭者 (2008-4-24 17:52:56)
就是用is_numeric没必要用正则
lince343 (2008-4-24 18:20:13)
$_GET的东西好像是以字符串形式存在的,is_numeric没用
不管page是不是数字,用过intval($_GET['page'])后都会成为数字
piaomiao163 (2008-4-24 19:18:48)
xiaojie515 (2008-6-25 20:13:21)
ctype_digit()
pylong (2008-6-25 20:16:29)
先intval,再is_int
浮点数另外处理
beilee80 (2008-6-25 21:13:07)
来个简单的,有点小漏洞,但对于page来说已经足够了,呵呵
if ( 0 + $_GET['page'] <= 0 ) echo 'error';
yingwei13 (2008-6-25 22:01:06)
用正则好了,最快
if (!ereg("^[0-9]*$",$_GET['page'] )) echo "false";
qianziai0912 (2008-6-26 17:04:41)
????????????????????不管page是不是整数,intval()都要转换成整数,还判断什么?
intval()
变量转成整数类型。
语
全国老婆一览表
一个男人一夜未归.
(一)
老婆一夜未睡。
第二天来到一家私人侦探社,甩下2000元,委托私家侦探收集花心丈夫出轨的所有证据。
过了一周,老公收到一张法院的传票,老婆起诉要离婚。
最后丈夫被判过错方,房子、家产尽归老婆。
这是个北京老婆。
(二)
老婆一夜未睡。
第二天,老婆上午到美发店做个离子烫,下午做了个面膜,顺便到情趣商店买套性感内衣。晚上在家准备一个烛光晚餐,一共花费四百元。老公晚上回到家后,看到美丽性感的老婆,惊讶得嘴里可以放下一个鸡蛋,深悔自己有眼无珠。并发誓一辈子不会让老婆离开自己。
一周后,老婆写了一篇题为《我怎样留住了我得花心老公?》的文章,并在杂志上发表,还得了五百元稿费。
这是个上海老婆。
(三)
老婆一夜没睡。
第二天,老婆打扮得花枝招展,给初恋情人打了一个电话:喂,还记得我吗?
我很寂寞,我今天晚上有空......
于是老公在外面继续潇洒,老婆在家里私会情人,井水不犯河水,相安无事。
这是个广东老婆。
(四)
老婆一夜未睡。
第二天一起床,老婆把屋里收拾得干干净净,把丈夫的换洗衣服迭的整整齐齐,留了一张纸条,告诉丈夫按时吃药。于是回娘家了。
后来老公良心发现,到岳母家负荆请罪,请回了老婆,并发誓好好过日子。
这是个四川老婆。
(五)
老婆一夜没睡。
第二天,老婆把家里的两把菜刀磨的雪亮,前 . 、后背各掖一把,决定和丈夫摊牌。心里说:哼哼,我跟你不是鱼死就是网破。
后来老公乖乖和老婆回到家里。
这是个湖南老婆。
(六)
老婆一夜没睡。
第二天一起床,老婆摞起袖子下厨房。平时一顿可以吃二两汤面加一张烧饼,今天做一斤汤面外加十张烧饼,并且一顿就消灭掉。
吃完以后,老婆摸着圆滚滚的肚皮,倒在床上放声大哭:这今后的日子可怎么过啊?依尔呦......
老公并没有因为外遇离婚,可是半年后提出离婚,理由是老婆胖的像一头猪......
这是个山西老婆。
(七)
老婆一夜没睡。
第二天,老婆哭着回到娘家,把这件事原原本本的告诉了自己的弟弟。弟弟喊上姑姑家的大哥、舅舅家的老弟。一人手里提着条木棍,在丈夫回家的路上等候......
后来鼻青脸肿的老公到法院提出离婚。经调解无效,法院判双方离婚,财产一认一半。并判老婆负担老公被打的医药费。
这是个东北老婆。
(八)
老婆一夜未睡。
第二天一起床,跑到丈夫单位大哭小叫,当众把丈夫和他那位年轻漂亮的"狐狸精"同事的丑事揭露出来,单位答应一定给予处分。
后来老公和她离婚了,离婚后一周就又和那位年轻妹妹结了婚.
这是个山东老婆。
(九)
老婆一夜未睡。
第二天一起床,老婆把户口本、结婚证、房产证、存折藏了起来。并切断丈夫的一切经济来源,然后洋洋得意的对丈夫说:我看你拿什么来养那个狐狸精......我也不和你离婚,*死你!
这是个......
……
……
……
……
……
……
……
看完不给我跟贴人的老婆!!
一个男人一夜未归.
(一)
老婆一夜未睡。
第二天来到一家私人侦探社,甩下2000元,委托私家侦探收集花心丈夫出轨的所有证据。
过了一周,老公收到一张法院的传票,老婆起诉要离婚。
最后丈夫被判过错方,房子、家产尽归老婆。
这是个北京老婆。
(二)
老婆一夜未睡。
第二天,老婆上午到美发店做个离子烫,下午做了个面膜,顺便到情趣商店买套性感内衣。晚上在家准备一个烛光晚餐,一共花费四百元。老公晚上回到家后,看到美丽性感的老婆,惊讶得嘴里可以放下一个鸡蛋,深悔自己有眼无珠。并发誓一辈子不会让老婆离开自己。
一周后,老婆写了一篇题为《我怎样留住了我得花心老公?》的文章,并在杂志上发表,还得了五百元稿费。
这是个上海老婆。
(三)
老婆一夜没睡。
第二天,老婆打扮得花枝招展,给初恋情人打了一个电话:喂,还记得我吗?
我很寂寞,我今天晚上有空......
于是老公在外面继续潇洒,老婆在家里私会情人,井水不犯河水,相安无事。
这是个广东老婆。
(四)
老婆一夜未睡。
第二天一起床,老婆把屋里收拾得干干净净,把丈夫的换洗衣服迭的整整齐齐,留了一张纸条,告诉丈夫按时吃药。于是回娘家了。
后来老公良心发现,到岳母家负荆请罪,请回了老婆,并发誓好好过日子。
这是个四川老婆。
(五)
老婆一夜没睡。
第二天,老婆把家里的两把菜刀磨的雪亮,前 . 、后背各掖一把,决定和丈夫摊牌。心里说:哼哼,我跟你不是鱼死就是网破。
后来老公乖乖和老婆回到家里。
这是个湖南老婆。
(六)
老婆一夜没睡。
第二天一起床,老婆摞起袖子下厨房。平时一顿可以吃二两汤面加一张烧饼,今天做一斤汤面外加十张烧饼,并且一顿就消灭掉。
吃完以后,老婆摸着圆滚滚的肚皮,倒在床上放声大哭:这今后的日子可怎么过啊?依尔呦......
老公并没有因为外遇离婚,可是半年后提出离婚,理由是老婆胖的像一头猪......
这是个山西老婆。
(七)
老婆一夜没睡。
第二天,老婆哭着回到娘家,把这件事原原本本的告诉了自己的弟弟。弟弟喊上姑姑家的大哥、舅舅家的老弟。一人手里提着条木棍,在丈夫回家的路上等候......
后来鼻青脸肿的老公到法院提出离婚。经调解无效,法院判双方离婚,财产一认一半。并判老婆负担老公被打的医药费。
这是个东北老婆。
(八)
老婆一夜未睡。
第二天一起床,跑到丈夫单位大哭小叫,当众把丈夫和他那位年轻漂亮的"狐狸精"同事的丑事揭露出来,单位答应一定给予处分。
后来老公和她离婚了,离婚后一周就又和那位年轻妹妹结了婚.
这是个山东老婆。
(九)
老婆一夜未睡。
第二天一起床,老婆把户口本、结婚证、房产证、存折藏了起来。并切断丈夫的一切经济来源,然后洋洋得意的对丈夫说:我看你拿什么来养那个狐狸精......我也不和你离婚,*死你!
这是个......
……
……
……
……
……
……
……
看完不给我跟贴人的老婆!!
<?php
/*
常用的ADODB使用方法
整理:飞豹游侠 QQ:8527385 E-mail:liuchengcn # 163.com
如有错误之处,敬请谅解,并QQ或E-mail通知我,谢谢
*/
//定义数据库变量
$DB_TYPE = "mysql";
$DB_HOST = "localhost";
$DB_USER = "root";
$DB_PASS = "";
$DB_DATABASE = "ai-part";
require_once("../adodb/adodb.inc.php");
$db = NewADOConnection("$DB_TYPE");//建立数据库对象
$db->debug = true;//数据库的DEBUG测试,程序开发期,可设置为true,正式版要注释掉这行,(默认值是false)
$ADODB_FETCH_MODE = ADODB_FETCH_ASSOC;
/*
返回的记录集形式
define('ADODB_FETCH_DEFAULT',0);
define('ADODB_FETCH_NUM',1);
define('ADODB_FETCH_ASSOC',2);
define('ADODB_FETCH_BOTH',3);
以上的常量,是在adodb.inc.php里定义的,也就是$ADODB_FETCH_MODE 这个变量可以设置的值
常用的是:ADODB_FETCH_NUM 或 ADODB_FETCH_ASSOC
ADODB_FETCH_NUM 返回的记录集中的索引,是数字形式,即数据库字段的排序顺序值
ADODB_FETCH_ASSOC 返回的记录集中的索引,是原数据库字段名
ADODB_FETCH_BOTH 和 ADODB_FETCH_DEFAULT 是同时返回 ADODB_FETCH_NUM, ADODB_FETCH_ASSOC的值,某些数据库不支持
An example:
$ADODB_FETCH_MODE = ADODB_FETCH_NUM;
$rs1 = $db->Execute('select * from table');
$ADODB_FETCH_MODE = ADODB_FETCH_ASSOC;
$rs2 = $db->Execute('select * from table');
print_r($rs1->fields); # 返回的数组是: array([0]=>'v0',[1] =>'v1')
print_r($rs2->fields); # 返回的数组是: array(['col1']=>'v0',['col2'] =>'v1')
*/
//连接数据库,方法有Connect,PConnect,NConnect,一般使用Connect. NConnect是连接特殊的数据库时才用
if (!@$db->Connect("$DB_HOST", "$DB_USER", "$DB_PASS", "$DB_DATABASE")) {
exit('服务器忙,请稍候再访问');
}
/*
$db-> $rs-> 此类的使用方法
Execute($sql,$inputarr=false),执行参数中的$sql语句,后面的那个$inputarr参数,一般情况下不需要
SelectLimit($sql,$numrows=-1,$offset=-1,$inputarr=false) $numrows:取几条记录,$offset,从第几条开始取,SelectLimit,一般是用于分页,或只取出几条记录的时候用
*/
//Example: 取出多个记录
$sql = "SELECT * FROM table ORDER BY id DESC";
if (!$rs = $db->Execute($sql)) {//执行SQL语句,并把结果返回给$rs变量
echo $db->ErrorMsg();//这个是打印出错信息
$db->Close();//关闭数据库
exit();
}
while (!$rs->EOF) {//遍历记录集
echo $rs->fields['username'] . '
';//print_r($rs->fields)试试,$rs->fields['字段名'],返回的是这个字段里的值
$rs->MoveNext();//将指针指到下一条记录,不用的话,会出现死循环!
}
$rs->Close();//关闭它,以便释放内存,每次操作完都进行一次关闭,养成编程的好习惯
//插入新记录
$sql = "INSERT table (user_type,username) VALUES (3, 'liucheng')";
$db->Execute($sql);
//更新记录
$sql = "UPDATE table SET user_type=3 WHERE id=2";
$db->Execute($sql);
//删除记录
$sql = "DELETE FROM table WHERE id=2";
$db->Execute($sql);
// 取单个记录
//$db->GetRow($sql), 取出SQL中的第一条记录,并返回一个数组,如果出错,则返回false
$sql = "SELECT username,password,user_type FROM table WHERE id=3";
$data_ary = $db->GetRow($sql);
if ($data_ary == false) {//如果用===,可能不是你想要的结果
echo '没有找到此记录';
exit();
} else {
echo $data_ary['username'] . ' ' . $data_ary['password'] . ' ' . $data_ary['user_type'] . '
';
}
//这里没有用到$rs,则不需要$rs->Close();
//另一种方法 (使用上面的方法比较好,又方便)
$sql = "SELECT username,password,user_type FROM table WHERE id=3";
if (!$rs = $db->Execute($sql)) {
echo $db->ErrorMsg();
$db->Close();
exit();
}
if (!$result = $rs->FetchRow()) {
echo '没有找到此记录';
exit();
} else {
echo $result['username'] . ' ' . $result['password'] . ' ' . $result['user_type'] . '
';
}
//$db->GetOne($sql) 取出SQL中的第一条记录的第一个字段的值,如果出错,则返回false
$sql = "SELECT COUNT(id) FROM table";
$record_nums = $db->GetOne($sql);
echo $record_nums;
$sql = "SELECT username,password,user_type FROM table WHERE user_id=1";
$result = $db->GetOne($sql);
echo $result;//此值为记录中的username的值
/*
在进行添加,修改,删除记录操作时,要对字符串型的字段,使用$db->qstr()对用户输入的字符进行处理,对数字型字段,要在之前,进行数据判断
更新记录,注意:这是针对php.ini中,magic_quotes被设置为Off的情况,如果不确定,可以使用
$db->qstr($content,get_magic_quotes_gpc())
注意:content= 等号右边,没有单引号
*/
$sql = "UPDATE table SET content=" . $db->qstr($content) . " WHERE id=2";
$db->Execute($sql);
/*$db->Insert_ID(),无参数,返回刚刚插入的那条记录的ID值,仅支持部分数据库,带auto-increment功能的数据库,如PostgreSQL, MySQL 和 MS SQL
*/
//Example:
$sql = "INSERT table (user_type,username) VALUES (3, 'liucheng')";
$db->Execute($sql);
$data_id = $db->Insert_ID();
echo $data_id;
/*$db->GenID($seqName = 'adodbseq',$startID=1),产生一个ID值.$seqName:用于产生此ID的数据库表名,$startID:起始值,一般不用设置,它会把$seqName中的值自动加1.支持部分数据库,某些数据库不支持
Insert_ID,GenID,一般我用GenID,使用它的目的,是在插入记录后,要马上得到它的ID时,才用
*/
/*Example:
先创建一个列名为user_id_seq的表,里面只有一个字段,id,int(10),NOT NULL,然后插入一条值为0的记录
*/
$user_id = $db->GenID('user_id_seq');
$sql = "INSERT table (id, user_type,username) VALUES (" . $user_id . ", 3, 'liucheng')";
$db->Execute($sql);
/*
$rs->RecordCount(),取出记录集总数,无参数
它好像是把取出的记录集,用count()数组的方法,取得数据的数量
如果取大量数据,效率比较慢,建议使用SQL里的COUNT(*)的方法
$sql = "SELECT COUNT(*) FROM table", 用此方法时,不要在SQL里加ORDER BY,那样会降低执行速度
Example:
*/
$sql = "SELECT * FROM table ORDER BY id DESC";
if (!$rs = $db->Execute($sql)) {
echo $db->ErrorMsg();
$db->Close();
exit();
}
$record_nums = $rs->RecordCount();
/*
如果想对某一结果集,要进行两次同样的循环处理,可以用下面方法
以下,只是一个例子,只为说明$rs->MoveFirst()的使用方法
*/
$sql = "SELECT * FROM table ORDER BY id DESC";
if (!$rs = $db->Execute($sql)) {
echo $db->ErrorMsg();
$db->Close();
exit();
}
$username_ary = array();
while (!$rs->EOF) {
$username_ary[] = $rs->fields['username']
echo $rs->fields['username'] . '
';//print_r($rs->fields)试试,$rs->fields['字段名'],返回的是这个字段里的值
$rs->MoveNext();//将指针指到下一条记录,不用的话,会出现死循环!
}
$username_ary = array_unique($username_ary);
$rs->MoveFirst();//将指针指回第一条记录,无参数
while (!$rs->EOF) {
echo $rs->fields['password'] . '
';//print_r($rs->fields)试试,$rs->fields['字段名'],返回的是这个字段里的值
$rs->MoveNext();//将指针指到下一条记录,不用的话,会出现死循环!
}
$rs->Close();
/*
当本页程序,对数据库的操作完毕后,要$db->Close();
*/
$db->Close();
/*一个不错的方法 */
if (isset($db)) {
$db->Close();
}
?>
/*
常用的ADODB使用方法
整理:飞豹游侠 QQ:8527385 E-mail:liuchengcn # 163.com
如有错误之处,敬请谅解,并QQ或E-mail通知我,谢谢
*/
//定义数据库变量
$DB_TYPE = "mysql";
$DB_HOST = "localhost";
$DB_USER = "root";
$DB_PASS = "";
$DB_DATABASE = "ai-part";
require_once("../adodb/adodb.inc.php");
$db = NewADOConnection("$DB_TYPE");//建立数据库对象
$db->debug = true;//数据库的DEBUG测试,程序开发期,可设置为true,正式版要注释掉这行,(默认值是false)
$ADODB_FETCH_MODE = ADODB_FETCH_ASSOC;
/*
返回的记录集形式
define('ADODB_FETCH_DEFAULT',0);
define('ADODB_FETCH_NUM',1);
define('ADODB_FETCH_ASSOC',2);
define('ADODB_FETCH_BOTH',3);
以上的常量,是在adodb.inc.php里定义的,也就是$ADODB_FETCH_MODE 这个变量可以设置的值
常用的是:ADODB_FETCH_NUM 或 ADODB_FETCH_ASSOC
ADODB_FETCH_NUM 返回的记录集中的索引,是数字形式,即数据库字段的排序顺序值
ADODB_FETCH_ASSOC 返回的记录集中的索引,是原数据库字段名
ADODB_FETCH_BOTH 和 ADODB_FETCH_DEFAULT 是同时返回 ADODB_FETCH_NUM, ADODB_FETCH_ASSOC的值,某些数据库不支持
An example:
$ADODB_FETCH_MODE = ADODB_FETCH_NUM;
$rs1 = $db->Execute('select * from table');
$ADODB_FETCH_MODE = ADODB_FETCH_ASSOC;
$rs2 = $db->Execute('select * from table');
print_r($rs1->fields); # 返回的数组是: array([0]=>'v0',[1] =>'v1')
print_r($rs2->fields); # 返回的数组是: array(['col1']=>'v0',['col2'] =>'v1')
*/
//连接数据库,方法有Connect,PConnect,NConnect,一般使用Connect. NConnect是连接特殊的数据库时才用
if (!@$db->Connect("$DB_HOST", "$DB_USER", "$DB_PASS", "$DB_DATABASE")) {
exit('服务器忙,请稍候再访问');
}
/*
$db-> $rs-> 此类的使用方法
Execute($sql,$inputarr=false),执行参数中的$sql语句,后面的那个$inputarr参数,一般情况下不需要
SelectLimit($sql,$numrows=-1,$offset=-1,$inputarr=false) $numrows:取几条记录,$offset,从第几条开始取,SelectLimit,一般是用于分页,或只取出几条记录的时候用
*/
//Example: 取出多个记录
$sql = "SELECT * FROM table ORDER BY id DESC";
if (!$rs = $db->Execute($sql)) {//执行SQL语句,并把结果返回给$rs变量
echo $db->ErrorMsg();//这个是打印出错信息
$db->Close();//关闭数据库
exit();
}
while (!$rs->EOF) {//遍历记录集
echo $rs->fields['username'] . '
';//print_r($rs->fields)试试,$rs->fields['字段名'],返回的是这个字段里的值
$rs->MoveNext();//将指针指到下一条记录,不用的话,会出现死循环!
}
$rs->Close();//关闭它,以便释放内存,每次操作完都进行一次关闭,养成编程的好习惯
//插入新记录
$sql = "INSERT table (user_type,username) VALUES (3, 'liucheng')";
$db->Execute($sql);
//更新记录
$sql = "UPDATE table SET user_type=3 WHERE id=2";
$db->Execute($sql);
//删除记录
$sql = "DELETE FROM table WHERE id=2";
$db->Execute($sql);
// 取单个记录
//$db->GetRow($sql), 取出SQL中的第一条记录,并返回一个数组,如果出错,则返回false
$sql = "SELECT username,password,user_type FROM table WHERE id=3";
$data_ary = $db->GetRow($sql);
if ($data_ary == false) {//如果用===,可能不是你想要的结果
echo '没有找到此记录';
exit();
} else {
echo $data_ary['username'] . ' ' . $data_ary['password'] . ' ' . $data_ary['user_type'] . '
';
}
//这里没有用到$rs,则不需要$rs->Close();
//另一种方法 (使用上面的方法比较好,又方便)
$sql = "SELECT username,password,user_type FROM table WHERE id=3";
if (!$rs = $db->Execute($sql)) {
echo $db->ErrorMsg();
$db->Close();
exit();
}
if (!$result = $rs->FetchRow()) {
echo '没有找到此记录';
exit();
} else {
echo $result['username'] . ' ' . $result['password'] . ' ' . $result['user_type'] . '
';
}
//$db->GetOne($sql) 取出SQL中的第一条记录的第一个字段的值,如果出错,则返回false
$sql = "SELECT COUNT(id) FROM table";
$record_nums = $db->GetOne($sql);
echo $record_nums;
$sql = "SELECT username,password,user_type FROM table WHERE user_id=1";
$result = $db->GetOne($sql);
echo $result;//此值为记录中的username的值
/*
在进行添加,修改,删除记录操作时,要对字符串型的字段,使用$db->qstr()对用户输入的字符进行处理,对数字型字段,要在之前,进行数据判断
更新记录,注意:这是针对php.ini中,magic_quotes被设置为Off的情况,如果不确定,可以使用
$db->qstr($content,get_magic_quotes_gpc())
注意:content= 等号右边,没有单引号
*/
$sql = "UPDATE table SET content=" . $db->qstr($content) . " WHERE id=2";
$db->Execute($sql);
/*$db->Insert_ID(),无参数,返回刚刚插入的那条记录的ID值,仅支持部分数据库,带auto-increment功能的数据库,如PostgreSQL, MySQL 和 MS SQL
*/
//Example:
$sql = "INSERT table (user_type,username) VALUES (3, 'liucheng')";
$db->Execute($sql);
$data_id = $db->Insert_ID();
echo $data_id;
/*$db->GenID($seqName = 'adodbseq',$startID=1),产生一个ID值.$seqName:用于产生此ID的数据库表名,$startID:起始值,一般不用设置,它会把$seqName中的值自动加1.支持部分数据库,某些数据库不支持
Insert_ID,GenID,一般我用GenID,使用它的目的,是在插入记录后,要马上得到它的ID时,才用
*/
/*Example:
先创建一个列名为user_id_seq的表,里面只有一个字段,id,int(10),NOT NULL,然后插入一条值为0的记录
*/
$user_id = $db->GenID('user_id_seq');
$sql = "INSERT table (id, user_type,username) VALUES (" . $user_id . ", 3, 'liucheng')";
$db->Execute($sql);
/*
$rs->RecordCount(),取出记录集总数,无参数
它好像是把取出的记录集,用count()数组的方法,取得数据的数量
如果取大量数据,效率比较慢,建议使用SQL里的COUNT(*)的方法
$sql = "SELECT COUNT(*) FROM table", 用此方法时,不要在SQL里加ORDER BY,那样会降低执行速度
Example:
*/
$sql = "SELECT * FROM table ORDER BY id DESC";
if (!$rs = $db->Execute($sql)) {
echo $db->ErrorMsg();
$db->Close();
exit();
}
$record_nums = $rs->RecordCount();
/*
如果想对某一结果集,要进行两次同样的循环处理,可以用下面方法
以下,只是一个例子,只为说明$rs->MoveFirst()的使用方法
*/
$sql = "SELECT * FROM table ORDER BY id DESC";
if (!$rs = $db->Execute($sql)) {
echo $db->ErrorMsg();
$db->Close();
exit();
}
$username_ary = array();
while (!$rs->EOF) {
$username_ary[] = $rs->fields['username']
echo $rs->fields['username'] . '
';//print_r($rs->fields)试试,$rs->fields['字段名'],返回的是这个字段里的值
$rs->MoveNext();//将指针指到下一条记录,不用的话,会出现死循环!
}
$username_ary = array_unique($username_ary);
$rs->MoveFirst();//将指针指回第一条记录,无参数
while (!$rs->EOF) {
echo $rs->fields['password'] . '
';//print_r($rs->fields)试试,$rs->fields['字段名'],返回的是这个字段里的值
$rs->MoveNext();//将指针指到下一条记录,不用的话,会出现死循环!
}
$rs->Close();
/*
当本页程序,对数据库的操作完毕后,要$db->Close();
*/
$db->Close();
/*一个不错的方法 */
if (isset($db)) {
$db->Close();
}
?>
PHP,一门最近几年兴起的web设计脚本语言,由于它的强大和可伸缩性,近几年来得到长足的发展,php相比传统的asp网站,在速度上有绝对的优势,想mssql转6万条数据php如需要40秒,asp不下2分钟.但是,由于网站的数据越来越多,我们渴求能更快速的调用数据,不必要每次都从数据库掉,我们可以从其他的地方,比方一个文件,或者某个内存地址,这就是php的缓存技术,也就是Cache技术.
一般来说,缓存的目的是把数据放在一个地方让访问的更快点,毫无疑问,内存是最快的,但是,几百M的数据能往内存放么?这不现实,当然,有的时候临时放如服务器缓存,如ob_start()这个缓存页面开启的话在发送文件头之前页面内容都被缓存在内存中,知道等页面输出自动清楚或者等待ob_get_contents的返回,[或者被ob_end_clean显示的清除,这在静态页面的生成中能很好的利用,在模板中能得到很好的体现,我的这篇文章深入的讨论了:
谈PHP生成静态页面,这是一种方式,但这是临时性的,不是解决我们问题的好方法.
另外,在asp中有一对象application,可以保存公用的参数,这也算点缓存,但在php,我至今没看到开发者产出这种对象,的确,没必要.asp.net的页面缓存技术就用的是viewstate,而cache就是文件关联,(不一定准确),文件被修改,更新缓存,文件没被修改而且不超时(注释1),就读取缓存,返回结果,就是这个思路,看看这个源码:
<?php
classcache{
/*
ClassName:cache
Description:controltocachedata,$cache_out_timeisaarraytosavecachedatetimeout.
Version:1.0
Author:老农cjjer
Lastmodify:2006-2-26
AuthorURL:http://www.cjjer.com
*/
private$cache_dir;
private$expireTime=180;//缓存的时间是60秒
function__construct($cache_dirname){
if(!@is_dir($cache_dirname)){
if(!@mkdir($cache_dirname,0777)){
$this->warn('缓存文件不存在而且不能创建,需要手动创建.');
returnfalse;
}
}
$this->cache_dir=$cache_dirname;
}
function__destruct(){
echo'Cacheclassbye.';
}
functionget_url(){
if(!isset($_SERVER['REQUEST_URI'])){
$url=$_SERVER['REQUEST_URI'];
}else{
$url=$_SERVER['SCRIPT_NAME'];
$url.=(!empty($_SERVER['QUERY_STRING']))?'?'.$_SERVER['QUERY_STRING']:'';
}
return$url;
}
functionwarn($errorstring){
echo"<b><fontcolor='red'>发生错误:<pre>".$errorstring."</pre></font></b>";
}
functioncache_page($pageurl,$pagedata){
if(!$fso=fopen($pageurl,'w')){
$this->warns('无法打开缓存文件.');//trigger_error
returnfalse;
}
if(!flock($fso,LOCK_EX)){//LOCK_NB,排它型锁定
$this->warns('无法锁定缓存文件.');//trigger_error
returnfalse;
}
if(!fwrite($fso,$pagedata)){//写入字节流,serialize写入其他格式
$this->warns('无法写入缓存文件.');//trigger_error
returnfalse;
}
flock($fso,LOCK_UN);//释放锁定
fclose($fso);
returntrue;
}
functiondisplay_cache($cacheFile){
if(!file_exists($cacheFile)){
$this->warn('无法读取缓存文件.');//trigger_error
returnfalse;
}
echo'读取缓存文件:'.$cacheFile;
//returnunserialize(file_get_contents($cacheFile));
$fso=fopen($cacheFile,'r');
$data=fread($fso,filesize($cacheFile));
fclose($fso);
return$data;
}
functionreadData($cacheFile='default_cache.txt'){
$cacheFile=$this->cache_dir."/".$cacheFile;
if(file_exists($cacheFile)&&filemtime($cacheFile)>(time()-$this->expireTime)){
$data=$this->display_cache($cacheFile);
}else{
$data="fromherewocangetitfrommysqldatabase,updatetimeis<b>".date('ldSofFYh:i:sA')."</b>,过期时间是:".date('ldSofFYh:i:sA',time()+$this->expireTime)."----------";
$this->cache_page($cacheFile,$data);
}
return$data;
}
}
?>
下面我打断这个代码逐行解释.
三:程序透析
这个缓存类(类没什么好怕的.请继续看)名称是cache,有2个属性:
private$cache_dir;
private$expireTime=180;
$cache_dir是缓存文件所放的相对网站目录的父目录,$expireTime(注释一)是我们缓存的数据过期的时间,主要是这个思路:
当数据或者文件被加载的时候,先判断缓存文件存在不,返回false,文件最后修改时间和缓存的时间和比当前时间大不,大的话说明缓存还没到期,小的话返回false,当返回false的时候,读取原始数据,写入缓存文件中,返回数据.,
接着看程序:
function__construct($cache_dirname){
if(!@is_dir($cache_dirname)){
if(!@mkdir($cache_dirname,0777)){
$this->warn('缓存文件不存在而且不能创建,需要手动创建.');
returnfalse;
}
}
$this->cache_dir=$cache_dirname;
}
当类第一次被实例的时候构造默认函数带参数缓存文件名称,如文件不存在,创建一个有编辑权限的文件夹,创建失败的时候抛出异常.然后把cache类的$cache_dir属性设置为这个文件夹名称,我们的所有缓存文件都是在这个文件夹下面的.
function__destruct(){
echo'Cacheclassbye.';
}
这是class类的析构函数,为了演示,我们输出一个字符串表示我们释放cache类资源成功.
functionwarn($errorstring){
echo"<b><fontcolor='red'>发生错误:<pre>".$errorstring."</pre></font></b>";
}
这个方法输出错误信息.
functionget_url(){
if(!isset($_SERVER['REQUEST_URI'])){
$url=$_SERVER['REQUEST_URI'];
}else{
$url=$_SERVER['SCRIPT_NAME'];
$url.=(!empty($_SERVER['QUERY_STRING']))?'?'.$_SERVER['QUERY_STRING']:'';
}
return$url;
}
这个方法返回当前url的信息,这是我看国外很多人的cms系统这样做,主要是缓存x.php?page=1,x.php?page=2,等这种文件的,这里列出是为了扩展的这个cache类功能的.
functioncache_page($pageurl,$pagedata){
if(!$fso=fopen($pageurl,'w')){
$this->warns('无法打开缓存文件.');//trigger_error
returnfalse;
}
if(!flock($fso,LOCK_EX)){//LOCK_NB,排它型锁定
$this->warns('无法锁定缓存文件.');//trigger_error
returnfalse;
}
if(!fwrite($fso,$pagedata)){//写入字节流,serialize写入其他格式
$this->warns('无法写入缓存文件.');//trigger_error
returnfalse;
}
flock($fso,LOCK_UN);//释放锁定
fclose($fso);
returntrue;
}
cache_page方法分别传入的是缓存的文件名称和数据,这是把数据写到文件里的方法,先用fopen打开文件,然后调用句柄锁定这个文件,然后用fwrite写入文件,最后释放这个句柄,任何一步发生错误将抛出错误.您可能看到这个注释
写入字节流,serialize写入其他格式
,顺便一提的是如果我们要把一个数组,(可以从MySQL数据库里面select查询除了的结果)用serialize函数写入,用unserialize读取到原来的类型.
functiondisplay_cache($cacheFile){
if(!file_exists($cacheFile)){
$this->warn('无法读取缓存文件.');//trigger_error
returnfalse;
}
echo'读取缓存文件:'.$cacheFile;
//returnunserialize(file_get_contents($cacheFile));
$fso=fopen($cacheFile,'r');
$data=fread($fso,filesize($cacheFile));
fclose($fso);
return$data;
}
这是由文件名称读取缓存的方法,直接打开文件,读取全部,如果文件不存在的或者无法读取的话返回false,当然,你感到不人性的话,可以重新生成缓存.
functionreadData($cacheFile='default_cache.txt'){
$cacheFile=$this->cache_dir."/".$cacheFile;
if(file_exists($cacheFile)&&filemtime($cacheFile)>(time()-$this->expireTime)){
$data=$this->display_cache($cacheFile);
}else{
$data="fromherewocangetitfrommysqldatabase,updatetimeis<b>".date('ldSofFYh:i:sA')."</b>,过期时间是:".date('ldSofFYh:i:sA',time()+$this->expireTime)."----------";
$this->cache_page($cacheFile,$data);
}
return$data;
}
这个函数是我们调用的方法,可以写成接口的方法,由传入参数判断文件存在不,文件最后修改时间+expireTime的时间是不是过了当前时间(大于的话说明没有过期),如果文件不存在或者已经过期,重新加载原始数据,这里,为了简单期间,我们是直接源是字符串,您可以把cache类继承某类,取到数据库的数据.(注释2)
四:补充说明,结语
注释一:这个缓存的时间您可以自己调,可以根据时间情况读取数组,xml,缓存等,请按照您的方便,值得一提的是缓存的时间(也就是缓存的key)也用缓存控制,.这在cms系统中被广泛使用,他们把要更新的key放在缓存中,非常容易控制全战.
注释二:php5开始支持类继承,这是让人兴奋的,把网站全局休息写在一个配置的类里面,再写与数据层交互的类(如与MySQL交互的类),我们的这个cache类继承数据交互的类,可以非常容易的读取数据库,这是外话,此处不再展开,有时间和大家详谈.
特别说明,这个类文件针对的php5以上版本,其他版本的请不要使用类.
一般来说,缓存的目的是把数据放在一个地方让访问的更快点,毫无疑问,内存是最快的,但是,几百M的数据能往内存放么?这不现实,当然,有的时候临时放如服务器缓存,如ob_start()这个缓存页面开启的话在发送文件头之前页面内容都被缓存在内存中,知道等页面输出自动清楚或者等待ob_get_contents的返回,[或者被ob_end_clean显示的清除,这在静态页面的生成中能很好的利用,在模板中能得到很好的体现,我的这篇文章深入的讨论了:
谈PHP生成静态页面,这是一种方式,但这是临时性的,不是解决我们问题的好方法.
另外,在asp中有一对象application,可以保存公用的参数,这也算点缓存,但在php,我至今没看到开发者产出这种对象,的确,没必要.asp.net的页面缓存技术就用的是viewstate,而cache就是文件关联,(不一定准确),文件被修改,更新缓存,文件没被修改而且不超时(注释1),就读取缓存,返回结果,就是这个思路,看看这个源码:
<?php
classcache{
/*
ClassName:cache
Description:controltocachedata,$cache_out_timeisaarraytosavecachedatetimeout.
Version:1.0
Author:老农cjjer
Lastmodify:2006-2-26
AuthorURL:http://www.cjjer.com
*/
private$cache_dir;
private$expireTime=180;//缓存的时间是60秒
function__construct($cache_dirname){
if(!@is_dir($cache_dirname)){
if(!@mkdir($cache_dirname,0777)){
$this->warn('缓存文件不存在而且不能创建,需要手动创建.');
returnfalse;
}
}
$this->cache_dir=$cache_dirname;
}
function__destruct(){
echo'Cacheclassbye.';
}
functionget_url(){
if(!isset($_SERVER['REQUEST_URI'])){
$url=$_SERVER['REQUEST_URI'];
}else{
$url=$_SERVER['SCRIPT_NAME'];
$url.=(!empty($_SERVER['QUERY_STRING']))?'?'.$_SERVER['QUERY_STRING']:'';
}
return$url;
}
functionwarn($errorstring){
echo"<b><fontcolor='red'>发生错误:<pre>".$errorstring."</pre></font></b>";
}
functioncache_page($pageurl,$pagedata){
if(!$fso=fopen($pageurl,'w')){
$this->warns('无法打开缓存文件.');//trigger_error
returnfalse;
}
if(!flock($fso,LOCK_EX)){//LOCK_NB,排它型锁定
$this->warns('无法锁定缓存文件.');//trigger_error
returnfalse;
}
if(!fwrite($fso,$pagedata)){//写入字节流,serialize写入其他格式
$this->warns('无法写入缓存文件.');//trigger_error
returnfalse;
}
flock($fso,LOCK_UN);//释放锁定
fclose($fso);
returntrue;
}
functiondisplay_cache($cacheFile){
if(!file_exists($cacheFile)){
$this->warn('无法读取缓存文件.');//trigger_error
returnfalse;
}
echo'读取缓存文件:'.$cacheFile;
//returnunserialize(file_get_contents($cacheFile));
$fso=fopen($cacheFile,'r');
$data=fread($fso,filesize($cacheFile));
fclose($fso);
return$data;
}
functionreadData($cacheFile='default_cache.txt'){
$cacheFile=$this->cache_dir."/".$cacheFile;
if(file_exists($cacheFile)&&filemtime($cacheFile)>(time()-$this->expireTime)){
$data=$this->display_cache($cacheFile);
}else{
$data="fromherewocangetitfrommysqldatabase,updatetimeis<b>".date('ldSofFYh:i:sA')."</b>,过期时间是:".date('ldSofFYh:i:sA',time()+$this->expireTime)."----------";
$this->cache_page($cacheFile,$data);
}
return$data;
}
}
?>
下面我打断这个代码逐行解释.
三:程序透析
这个缓存类(类没什么好怕的.请继续看)名称是cache,有2个属性:
private$cache_dir;
private$expireTime=180;
$cache_dir是缓存文件所放的相对网站目录的父目录,$expireTime(注释一)是我们缓存的数据过期的时间,主要是这个思路:
当数据或者文件被加载的时候,先判断缓存文件存在不,返回false,文件最后修改时间和缓存的时间和比当前时间大不,大的话说明缓存还没到期,小的话返回false,当返回false的时候,读取原始数据,写入缓存文件中,返回数据.,
接着看程序:
function__construct($cache_dirname){
if(!@is_dir($cache_dirname)){
if(!@mkdir($cache_dirname,0777)){
$this->warn('缓存文件不存在而且不能创建,需要手动创建.');
returnfalse;
}
}
$this->cache_dir=$cache_dirname;
}
当类第一次被实例的时候构造默认函数带参数缓存文件名称,如文件不存在,创建一个有编辑权限的文件夹,创建失败的时候抛出异常.然后把cache类的$cache_dir属性设置为这个文件夹名称,我们的所有缓存文件都是在这个文件夹下面的.
function__destruct(){
echo'Cacheclassbye.';
}
这是class类的析构函数,为了演示,我们输出一个字符串表示我们释放cache类资源成功.
functionwarn($errorstring){
echo"<b><fontcolor='red'>发生错误:<pre>".$errorstring."</pre></font></b>";
}
这个方法输出错误信息.
functionget_url(){
if(!isset($_SERVER['REQUEST_URI'])){
$url=$_SERVER['REQUEST_URI'];
}else{
$url=$_SERVER['SCRIPT_NAME'];
$url.=(!empty($_SERVER['QUERY_STRING']))?'?'.$_SERVER['QUERY_STRING']:'';
}
return$url;
}
这个方法返回当前url的信息,这是我看国外很多人的cms系统这样做,主要是缓存x.php?page=1,x.php?page=2,等这种文件的,这里列出是为了扩展的这个cache类功能的.
functioncache_page($pageurl,$pagedata){
if(!$fso=fopen($pageurl,'w')){
$this->warns('无法打开缓存文件.');//trigger_error
returnfalse;
}
if(!flock($fso,LOCK_EX)){//LOCK_NB,排它型锁定
$this->warns('无法锁定缓存文件.');//trigger_error
returnfalse;
}
if(!fwrite($fso,$pagedata)){//写入字节流,serialize写入其他格式
$this->warns('无法写入缓存文件.');//trigger_error
returnfalse;
}
flock($fso,LOCK_UN);//释放锁定
fclose($fso);
returntrue;
}
cache_page方法分别传入的是缓存的文件名称和数据,这是把数据写到文件里的方法,先用fopen打开文件,然后调用句柄锁定这个文件,然后用fwrite写入文件,最后释放这个句柄,任何一步发生错误将抛出错误.您可能看到这个注释
写入字节流,serialize写入其他格式
,顺便一提的是如果我们要把一个数组,(可以从MySQL数据库里面select查询除了的结果)用serialize函数写入,用unserialize读取到原来的类型.
functiondisplay_cache($cacheFile){
if(!file_exists($cacheFile)){
$this->warn('无法读取缓存文件.');//trigger_error
returnfalse;
}
echo'读取缓存文件:'.$cacheFile;
//returnunserialize(file_get_contents($cacheFile));
$fso=fopen($cacheFile,'r');
$data=fread($fso,filesize($cacheFile));
fclose($fso);
return$data;
}
这是由文件名称读取缓存的方法,直接打开文件,读取全部,如果文件不存在的或者无法读取的话返回false,当然,你感到不人性的话,可以重新生成缓存.
functionreadData($cacheFile='default_cache.txt'){
$cacheFile=$this->cache_dir."/".$cacheFile;
if(file_exists($cacheFile)&&filemtime($cacheFile)>(time()-$this->expireTime)){
$data=$this->display_cache($cacheFile);
}else{
$data="fromherewocangetitfrommysqldatabase,updatetimeis<b>".date('ldSofFYh:i:sA')."</b>,过期时间是:".date('ldSofFYh:i:sA',time()+$this->expireTime)."----------";
$this->cache_page($cacheFile,$data);
}
return$data;
}
这个函数是我们调用的方法,可以写成接口的方法,由传入参数判断文件存在不,文件最后修改时间+expireTime的时间是不是过了当前时间(大于的话说明没有过期),如果文件不存在或者已经过期,重新加载原始数据,这里,为了简单期间,我们是直接源是字符串,您可以把cache类继承某类,取到数据库的数据.(注释2)
四:补充说明,结语
注释一:这个缓存的时间您可以自己调,可以根据时间情况读取数组,xml,缓存等,请按照您的方便,值得一提的是缓存的时间(也就是缓存的key)也用缓存控制,.这在cms系统中被广泛使用,他们把要更新的key放在缓存中,非常容易控制全战.
注释二:php5开始支持类继承,这是让人兴奋的,把网站全局休息写在一个配置的类里面,再写与数据层交互的类(如与MySQL交互的类),我们的这个cache类继承数据交互的类,可以非常容易的读取数据库,这是外话,此处不再展开,有时间和大家详谈.
特别说明,这个类文件针对的php5以上版本,其他版本的请不要使用类.
Linux程序设计入门--线程操作
前言:Linux下线程的创建
介绍在Linux下线程的创建和基本的使用. Linux下的线程是一个非常复杂的问题,由
于我对线程的学习不时很好,我在这里只是简单的介绍线程的创建和基本的使用,关于线
程的高级使用(如线程的属性,线程的互斥,线程的同步等等问题)可以参考我后面给出的
资料. 现在关于线程的资料在网络上可以找到许多英文资料,后面我罗列了许多链接,对
线程的高级属性感兴趣的话可以参考一下. 等到我对线程的了解比较深刻的时候,我回来
完成这篇文章.如果您对线程了解的详尽我也非常高兴能够由您来完善.
先介绍什么是线程.我们编写的程序大多数可以看成是单线程的.就是程序是按照一定的
顺序来执行.如果我们使用线程的话,程序就会在我们创建线成的地方分叉,变成两个"程
序"在执行.粗略的看来好象和子进程差不多的,其实不然.子进程是通过拷贝父进程的地
址空间来执行的.而线程是通过共享程序代码来执行的,讲的通俗一点就是线程的相同的
代码会被执行几次.使用线程的好处是可以节省资源,由于线程是通过共享代码的,所以没
有进程调度那么复杂.
线程的创建和使用
线程的创建是用下面的几个函数来实现的.
view plainprint?
#include <pthread.h>
int pthread_create(pthread_t *thread,pthread_attr_t *attr,
void *(*start_routine)(void *),void *arg);
void pthread_exit(void *retval);
int pthread_join(pthread *thread,void **thread_return);
#include <pthread.h>
int pthread_create(pthread_t *thread,pthread_attr_t *attr,
void *(*start_routine)(void *),void *arg);
void pthread_exit(void *retval);
int pthread_join(pthread *thread,void **thread_return);
pthread_create创建一个线程,thread是用来表明创建线程的ID,attr指出线程创建时候
的属性,我们用NULL来表明使用缺省属性.start_routine函数指针是线程创建成功后开始
执行的函数,arg是这个函数的唯一一个参数.表明传递给start_routine的参数. pthrea
d_exit函数和exit函数类似用来退出线程.这个函数结束线程,释放函数的资源,并在最后
阻塞,直到其他线程使用pthread_join函数等待它.然后将*retval的值传递给**thread_
return.由于这个函数释放所以的函数资源,所以retval不能够指向函数的局部变量. pt
hread_join和wait调用一样用来等待指定的线程. 下面我们使用一个实例来解释一下使
用方法.在实践中,我们经常要备份一些文件.下面这个程序可以实现当前目录下的所有文
件备份.备份后的后缀名为bak
view plainprint?
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <pthread.h>
#include <dirent.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/time.h>
#define BUFFER 512
struct copy_file {
int infile;
int outfile;
};
void *copy(void *arg)
{
int infile,outfile;
int bytes_read,bytes_write,*bytes_copy_p;
char buffer[BUFFER],*buffer_p;
struct copy_file *file=(struct copy_file *)arg;
infile=file->infile;
outfile=file->outfile;
/* 因为线程退出时,所有的变量空间都要被释放,所以我们只好自己分配内存了 */
if((bytes_copy_p=(int *)malloc(sizeof(int)))==NULL) pthread_exit(NULL);
bytes_read=bytes_write=0;
*bytes_copy_p=0;
/* 还记得怎么拷贝文件吗 */
while((bytes_read=read(infile,buffer,BUFFER))!=0)
{
if((bytes_read==-1)&&(errno!=EINTR))break;
else if(bytes_read>0)
{
buffer_p=buffer;
while((bytes_write=write(outfile,buffer_p,bytes_read))!=0)
{
if((bytes_write==-1)&&(errno!=EINTR))break;
else if(bytes_write==bytes_read)break;
else if(bytes_write>0)
{
buffer_p+=bytes_write;
bytes_read-=bytes_write;
}
}
if(bytes_write==-1)break;
*bytes_copy_p+=bytes_read;
}
}
close(infile);
close(outfile);
pthread_exit(bytes_copy_p);
}
int main(int argc,char **argv)
{
pthread_t *thread;
struct copy_file *file;
int byte_copy,*byte_copy_p,num,i,j;
char filename[BUFFER];
struct dirent **namelist;
struct stat filestat;
/* 得到当前路径下面所有的文件(包含目录)的个数 */
if((num=scandir(".",&namelist,0,alphasort))<0)
{
fprintf(stderr,"Get File Num Error:%s\n\a",strerror(errno));
exit(1);
}
/* 给线程分配空间,其实没有必要这么多的 */
if(((thread=(pthread_t *)malloc(sizeof(pthread_t)*num))==NULL)||
((file=(struct copy_file *)malloc(sizeof(struct copy_file)*num))==NULL)
)
{
fprintf(stderr,"Out Of Memory!\n\a");
exit(1);
}
for(i=0,j=0;i<num;i++)
{
memset(filename,'\0',BUFFER);
strcpy(filename,namelist[i]->d_name);
if(stat(filename,&filestat)==-1)
{
fprintf(stderr,"Get File Information:%s\n\a",strerror(errno));
exit(1);
}
/* 我们忽略目录 */
if(!S_ISREG(filestat.st_mode))continue;
if((file[j].infile=open(filename,O_RDONLY))<0)
{
fprintf(stderr,"Open %s Error:%s\n\a",filename,strerror(errno));
continue;
}
strcat(filename,".bak");
if((file[j].outfile=open(filename,O_WRONLY|O_CREAT,S_IRUSR|S_IWUSR))
<0)
{
fprintf(stderr,"Creat %s Error:%s\n\a",filename,strerror(errno
));
continue;
}
/* 创建线程,进行文件拷贝 */
if(pthread_create(&thread[j],NULL,copy,(void *)&file[j])!=0)
fprintf(stderr,"Create Thread[%d] Error:%s\n\a",i,strerror(errno));
j++;
}
byte_copy=0;
for(i=0;i<j;i++)
{
/* 等待线程结束 */
if(pthread_join(thread[i],(void **)&byte_copy_p)!=0)
fprintf(stderr,"Thread[%d] Join Error:%s\n\a",
i,strerror(errno));
else
{
if(bytes_copy_p==NULL)continue;
printf("Thread[%d] Copy %d bytes\n\a",i,*byte_copy_p);
byte_copy+=*byte_copy_p;
/* 释放我们在copy函数里面创建的内存 */
free(byte_copy_p);
}
}
printf("Total Copy Bytes %d\n\a",byte_copy);
free(thread);
free(file);
exit(0);
}
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <pthread.h>
#include <dirent.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/time.h>
#define BUFFER 512
struct copy_file {
int infile;
int outfile;
};
void *copy(void *arg)
{
int infile,outfile;
int bytes_read,bytes_write,*bytes_copy_p;
char buffer[BUFFER],*buffer_p;
struct copy_file *file=(struct copy_file *)arg;
infile=file->infile;
outfile=file->outfile;
/* 因为线程退出时,所有的变量空间都要被释放,所以我们只好自己分配内存了 */
if((bytes_copy_p=(int *)malloc(sizeof(int)))==NULL) pthread_exit(NULL);
bytes_read=bytes_write=0;
*bytes_copy_p=0;
/* 还记得怎么拷贝文件吗 */
while((bytes_read=read(infile,buffer,BUFFER))!=0)
{
if((bytes_read==-1)&&(errno!=EINTR))break;
else if(bytes_read>0)
{
buffer_p=buffer;
while((bytes_write=write(outfile,buffer_p,bytes_read))!=0)
{
if((bytes_write==-1)&&(errno!=EINTR))break;
else if(bytes_write==bytes_read)break;
else if(bytes_write>0)
{
buffer_p+=bytes_write;
bytes_read-=bytes_write;
}
}
if(bytes_write==-1)break;
*bytes_copy_p+=bytes_read;
}
}
close(infile);
close(outfile);
pthread_exit(bytes_copy_p);
}
int main(int argc,char **argv)
{
pthread_t *thread;
struct copy_file *file;
int byte_copy,*byte_copy_p,num,i,j;
char filename[BUFFER];
struct dirent **namelist;
struct stat filestat;
/* 得到当前路径下面所有的文件(包含目录)的个数 */
if((num=scandir(".",&namelist,0,alphasort))<0)
{
fprintf(stderr,"Get File Num Error:%s\n\a",strerror(errno));
exit(1);
}
/* 给线程分配空间,其实没有必要这么多的 */
if(((thread=(pthread_t *)malloc(sizeof(pthread_t)*num))==NULL)||
((file=(struct copy_file *)malloc(sizeof(struct copy_file)*num))==NULL)
)
{
fprintf(stderr,"Out Of Memory!\n\a");
exit(1);
}
for(i=0,j=0;i<num;i++)
{
memset(filename,'\0',BUFFER);
strcpy(filename,namelist[i]->d_name);
if(stat(filename,&filestat)==-1)
{
fprintf(stderr,"Get File Information:%s\n\a",strerror(errno));
exit(1);
}
/* 我们忽略目录 */
if(!S_ISREG(filestat.st_mode))continue;
if((file[j].infile=open(filename,O_RDONLY))<0)
{
fprintf(stderr,"Open %s Error:%s\n\a",filename,strerror(errno));
continue;
}
strcat(filename,".bak");
if((file[j].outfile=open(filename,O_WRONLY|O_CREAT,S_IRUSR|S_IWUSR))
<0)
{
fprintf(stderr,"Creat %s Error:%s\n\a",filename,strerror(errno
));
continue;
}
/* 创建线程,进行文件拷贝 */
if(pthread_create(&thread[j],NULL,copy,(void *)&file[j])!=0)
fprintf(stderr,"Create Thread[%d] Error:%s\n\a",i,strerror(errno));
j++;
}
byte_copy=0;
for(i=0;i<j;i++)
{
/* 等待线程结束 */
if(pthread_join(thread[i],(void **)&byte_copy_p)!=0)
fprintf(stderr,"Thread[%d] Join Error:%s\n\a",
i,strerror(errno));
else
{
if(bytes_copy_p==NULL)continue;
printf("Thread[%d] Copy %d bytes\n\a",i,*byte_copy_p);
byte_copy+=*byte_copy_p;
/* 释放我们在copy函数里面创建的内存 */
free(byte_copy_p);
}
}
printf("Total Copy Bytes %d\n\a",byte_copy);
free(thread);
free(file);
exit(0);
}
线程的介绍就到这里了,关于线程的其他资料可以查看下面这写链接.
Getting Started With POSIX Threads
前言:Linux下线程的创建
介绍在Linux下线程的创建和基本的使用. Linux下的线程是一个非常复杂的问题,由
于我对线程的学习不时很好,我在这里只是简单的介绍线程的创建和基本的使用,关于线
程的高级使用(如线程的属性,线程的互斥,线程的同步等等问题)可以参考我后面给出的
资料. 现在关于线程的资料在网络上可以找到许多英文资料,后面我罗列了许多链接,对
线程的高级属性感兴趣的话可以参考一下. 等到我对线程的了解比较深刻的时候,我回来
完成这篇文章.如果您对线程了解的详尽我也非常高兴能够由您来完善.
先介绍什么是线程.我们编写的程序大多数可以看成是单线程的.就是程序是按照一定的
顺序来执行.如果我们使用线程的话,程序就会在我们创建线成的地方分叉,变成两个"程
序"在执行.粗略的看来好象和子进程差不多的,其实不然.子进程是通过拷贝父进程的地
址空间来执行的.而线程是通过共享程序代码来执行的,讲的通俗一点就是线程的相同的
代码会被执行几次.使用线程的好处是可以节省资源,由于线程是通过共享代码的,所以没
有进程调度那么复杂.
线程的创建和使用
线程的创建是用下面的几个函数来实现的.
view plainprint?
#include <pthread.h>
int pthread_create(pthread_t *thread,pthread_attr_t *attr,
void *(*start_routine)(void *),void *arg);
void pthread_exit(void *retval);
int pthread_join(pthread *thread,void **thread_return);
#include <pthread.h>
int pthread_create(pthread_t *thread,pthread_attr_t *attr,
void *(*start_routine)(void *),void *arg);
void pthread_exit(void *retval);
int pthread_join(pthread *thread,void **thread_return);
pthread_create创建一个线程,thread是用来表明创建线程的ID,attr指出线程创建时候
的属性,我们用NULL来表明使用缺省属性.start_routine函数指针是线程创建成功后开始
执行的函数,arg是这个函数的唯一一个参数.表明传递给start_routine的参数. pthrea
d_exit函数和exit函数类似用来退出线程.这个函数结束线程,释放函数的资源,并在最后
阻塞,直到其他线程使用pthread_join函数等待它.然后将*retval的值传递给**thread_
return.由于这个函数释放所以的函数资源,所以retval不能够指向函数的局部变量. pt
hread_join和wait调用一样用来等待指定的线程. 下面我们使用一个实例来解释一下使
用方法.在实践中,我们经常要备份一些文件.下面这个程序可以实现当前目录下的所有文
件备份.备份后的后缀名为bak
view plainprint?
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <pthread.h>
#include <dirent.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/time.h>
#define BUFFER 512
struct copy_file {
int infile;
int outfile;
};
void *copy(void *arg)
{
int infile,outfile;
int bytes_read,bytes_write,*bytes_copy_p;
char buffer[BUFFER],*buffer_p;
struct copy_file *file=(struct copy_file *)arg;
infile=file->infile;
outfile=file->outfile;
/* 因为线程退出时,所有的变量空间都要被释放,所以我们只好自己分配内存了 */
if((bytes_copy_p=(int *)malloc(sizeof(int)))==NULL) pthread_exit(NULL);
bytes_read=bytes_write=0;
*bytes_copy_p=0;
/* 还记得怎么拷贝文件吗 */
while((bytes_read=read(infile,buffer,BUFFER))!=0)
{
if((bytes_read==-1)&&(errno!=EINTR))break;
else if(bytes_read>0)
{
buffer_p=buffer;
while((bytes_write=write(outfile,buffer_p,bytes_read))!=0)
{
if((bytes_write==-1)&&(errno!=EINTR))break;
else if(bytes_write==bytes_read)break;
else if(bytes_write>0)
{
buffer_p+=bytes_write;
bytes_read-=bytes_write;
}
}
if(bytes_write==-1)break;
*bytes_copy_p+=bytes_read;
}
}
close(infile);
close(outfile);
pthread_exit(bytes_copy_p);
}
int main(int argc,char **argv)
{
pthread_t *thread;
struct copy_file *file;
int byte_copy,*byte_copy_p,num,i,j;
char filename[BUFFER];
struct dirent **namelist;
struct stat filestat;
/* 得到当前路径下面所有的文件(包含目录)的个数 */
if((num=scandir(".",&namelist,0,alphasort))<0)
{
fprintf(stderr,"Get File Num Error:%s\n\a",strerror(errno));
exit(1);
}
/* 给线程分配空间,其实没有必要这么多的 */
if(((thread=(pthread_t *)malloc(sizeof(pthread_t)*num))==NULL)||
((file=(struct copy_file *)malloc(sizeof(struct copy_file)*num))==NULL)
)
{
fprintf(stderr,"Out Of Memory!\n\a");
exit(1);
}
for(i=0,j=0;i<num;i++)
{
memset(filename,'\0',BUFFER);
strcpy(filename,namelist[i]->d_name);
if(stat(filename,&filestat)==-1)
{
fprintf(stderr,"Get File Information:%s\n\a",strerror(errno));
exit(1);
}
/* 我们忽略目录 */
if(!S_ISREG(filestat.st_mode))continue;
if((file[j].infile=open(filename,O_RDONLY))<0)
{
fprintf(stderr,"Open %s Error:%s\n\a",filename,strerror(errno));
continue;
}
strcat(filename,".bak");
if((file[j].outfile=open(filename,O_WRONLY|O_CREAT,S_IRUSR|S_IWUSR))
<0)
{
fprintf(stderr,"Creat %s Error:%s\n\a",filename,strerror(errno
));
continue;
}
/* 创建线程,进行文件拷贝 */
if(pthread_create(&thread[j],NULL,copy,(void *)&file[j])!=0)
fprintf(stderr,"Create Thread[%d] Error:%s\n\a",i,strerror(errno));
j++;
}
byte_copy=0;
for(i=0;i<j;i++)
{
/* 等待线程结束 */
if(pthread_join(thread[i],(void **)&byte_copy_p)!=0)
fprintf(stderr,"Thread[%d] Join Error:%s\n\a",
i,strerror(errno));
else
{
if(bytes_copy_p==NULL)continue;
printf("Thread[%d] Copy %d bytes\n\a",i,*byte_copy_p);
byte_copy+=*byte_copy_p;
/* 释放我们在copy函数里面创建的内存 */
free(byte_copy_p);
}
}
printf("Total Copy Bytes %d\n\a",byte_copy);
free(thread);
free(file);
exit(0);
}
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <pthread.h>
#include <dirent.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/time.h>
#define BUFFER 512
struct copy_file {
int infile;
int outfile;
};
void *copy(void *arg)
{
int infile,outfile;
int bytes_read,bytes_write,*bytes_copy_p;
char buffer[BUFFER],*buffer_p;
struct copy_file *file=(struct copy_file *)arg;
infile=file->infile;
outfile=file->outfile;
/* 因为线程退出时,所有的变量空间都要被释放,所以我们只好自己分配内存了 */
if((bytes_copy_p=(int *)malloc(sizeof(int)))==NULL) pthread_exit(NULL);
bytes_read=bytes_write=0;
*bytes_copy_p=0;
/* 还记得怎么拷贝文件吗 */
while((bytes_read=read(infile,buffer,BUFFER))!=0)
{
if((bytes_read==-1)&&(errno!=EINTR))break;
else if(bytes_read>0)
{
buffer_p=buffer;
while((bytes_write=write(outfile,buffer_p,bytes_read))!=0)
{
if((bytes_write==-1)&&(errno!=EINTR))break;
else if(bytes_write==bytes_read)break;
else if(bytes_write>0)
{
buffer_p+=bytes_write;
bytes_read-=bytes_write;
}
}
if(bytes_write==-1)break;
*bytes_copy_p+=bytes_read;
}
}
close(infile);
close(outfile);
pthread_exit(bytes_copy_p);
}
int main(int argc,char **argv)
{
pthread_t *thread;
struct copy_file *file;
int byte_copy,*byte_copy_p,num,i,j;
char filename[BUFFER];
struct dirent **namelist;
struct stat filestat;
/* 得到当前路径下面所有的文件(包含目录)的个数 */
if((num=scandir(".",&namelist,0,alphasort))<0)
{
fprintf(stderr,"Get File Num Error:%s\n\a",strerror(errno));
exit(1);
}
/* 给线程分配空间,其实没有必要这么多的 */
if(((thread=(pthread_t *)malloc(sizeof(pthread_t)*num))==NULL)||
((file=(struct copy_file *)malloc(sizeof(struct copy_file)*num))==NULL)
)
{
fprintf(stderr,"Out Of Memory!\n\a");
exit(1);
}
for(i=0,j=0;i<num;i++)
{
memset(filename,'\0',BUFFER);
strcpy(filename,namelist[i]->d_name);
if(stat(filename,&filestat)==-1)
{
fprintf(stderr,"Get File Information:%s\n\a",strerror(errno));
exit(1);
}
/* 我们忽略目录 */
if(!S_ISREG(filestat.st_mode))continue;
if((file[j].infile=open(filename,O_RDONLY))<0)
{
fprintf(stderr,"Open %s Error:%s\n\a",filename,strerror(errno));
continue;
}
strcat(filename,".bak");
if((file[j].outfile=open(filename,O_WRONLY|O_CREAT,S_IRUSR|S_IWUSR))
<0)
{
fprintf(stderr,"Creat %s Error:%s\n\a",filename,strerror(errno
));
continue;
}
/* 创建线程,进行文件拷贝 */
if(pthread_create(&thread[j],NULL,copy,(void *)&file[j])!=0)
fprintf(stderr,"Create Thread[%d] Error:%s\n\a",i,strerror(errno));
j++;
}
byte_copy=0;
for(i=0;i<j;i++)
{
/* 等待线程结束 */
if(pthread_join(thread[i],(void **)&byte_copy_p)!=0)
fprintf(stderr,"Thread[%d] Join Error:%s\n\a",
i,strerror(errno));
else
{
if(bytes_copy_p==NULL)continue;
printf("Thread[%d] Copy %d bytes\n\a",i,*byte_copy_p);
byte_copy+=*byte_copy_p;
/* 释放我们在copy函数里面创建的内存 */
free(byte_copy_p);
}
}
printf("Total Copy Bytes %d\n\a",byte_copy);
free(thread);
free(file);
exit(0);
}
线程的介绍就到这里了,关于线程的其他资料可以查看下面这写链接.
Getting Started With POSIX Threads
技术绝对不是最重要的东西,但技术是非常非常重要的东西。有很多东西,人们该忽悠的都已经忽悠到极点了,就差有真正好的产品了。可是实际上产品未必有人们忽悠得那么好,归根结底差的还是技术。杨立伟能上天,嫦娥一号能奔月,靠的是什么?技术!唯有靠技术,实实在在的技术,其他一切的忽悠,离开了技术都是空谈。中国这几年确实得到了发展,可是人民的生活还没有达到很理想的程度,大家的钱袋子普遍都还是瘪的,社会上有那么一些富得流油的人,但那和普通老百姓没有任何关系,普通老百姓能感受到的就是经济的泡沫,就是挣钱越来越难、花钱越来越快、未来越来越不那么乐观。虽然国家的GDP年年在涨,按理说人民应该更富裕、幸福感更强烈,但实际情况不是这样。为什么?就是因为我们的GDP里面,实实在在靠生产创新、拉动内需、出口创汇获得的,比例还不是足够很大。只有靠生产获得的GDP,最终才能实实在在地流入到老百姓的口袋里,因为老百姓首先是劳动人民,劳动人民靠的就是自己的一双手。房地产能够创造大量的GDP,但是这种GDP充其量只不过是一种内耗,是中国人整中国人,也是国外的热钱流入中国的一种方式,这种热钱的流入,只不过是把中国真正的财富卷走,然后把经济过热、通货膨胀留在中国。从某种意义上说,这跟过去列强把鸦片运入中国是一样的,当然也有鸦片商人靠这个发了财,但整个社会却受到了极大的毒害。真正的把社会带动起来、让国家和老百姓都得到发展的,唯有发展生产,不管是生产传统行业里的商品,还是生产高科技领域的软件、电子芯片,这些都是实实在在创造价值的东西,这些东西赚回来的钱,绝对不会导致通货膨胀,绝对不会导致经济过热,绝对不会使得GDP年年涨但老百姓的口袋却年年瘪。当然发展生产不光是靠技术,商业方面的东西也至关重要,市场、贸易,都是需要的,但技术是第一个环节,有了精湛的技术,才有优良的产品,有了优良的产品,你去跟人家谈贸易、谈市场才有底气。如果没有优良的产品,你去跟人家天花乱坠地谈市场,这跟忽悠有什么区别呢?
所以说现在最缺的东西其实还是技术。过去因为缺技术让中国受尽列强欺负,但现在也同样因为缺技术导致中国做了很多赔本买卖。精明的外国人,从中国买走原材料,然后深加工,变成了电子芯片,再卖到中国,赚了大笔的钱,但中国为了出口这些原材料消耗了资源,污染了环境,付出了大量的廉价劳动力。微软、IBM、摩托罗拉、富士通,这些企业在包括中国在内的很多国家开办了形形色色的软件工厂,用廉价的劳动力繁荣了自己本国的经济,但这些付出廉价劳动力的国家只是跟着赚了一口剩饭吃,虽然比没得吃要好一些,但也绝对别指望吃得多丰盛。其实这道理大家都明白,但还是不得不继续这种现实,归根结底还是因为缺技术。因为缺技术,所以受制于人,所以吃人家的剩饭,所以让我们的同胞变成廉价劳动力。技术的发展能够让国家、让人民赚实实在在的钱,而缺乏技术底气的发展,只能带来一阵虚火。如果中国在技术方面能更强一些,更加尊重技术人才,那么,我不敢说GDP是否能有这么大的增长,但是经济绝对不会像现在这么热,通货膨胀绝对不会这么严重,人民生活得肯定比现在幸福,国家也会比现在更强盛。房地产不是洪水猛兽,但这几年中国的这帮房地产商,绝对是历史的罪人。
所以说现在最缺的东西其实还是技术。过去因为缺技术让中国受尽列强欺负,但现在也同样因为缺技术导致中国做了很多赔本买卖。精明的外国人,从中国买走原材料,然后深加工,变成了电子芯片,再卖到中国,赚了大笔的钱,但中国为了出口这些原材料消耗了资源,污染了环境,付出了大量的廉价劳动力。微软、IBM、摩托罗拉、富士通,这些企业在包括中国在内的很多国家开办了形形色色的软件工厂,用廉价的劳动力繁荣了自己本国的经济,但这些付出廉价劳动力的国家只是跟着赚了一口剩饭吃,虽然比没得吃要好一些,但也绝对别指望吃得多丰盛。其实这道理大家都明白,但还是不得不继续这种现实,归根结底还是因为缺技术。因为缺技术,所以受制于人,所以吃人家的剩饭,所以让我们的同胞变成廉价劳动力。技术的发展能够让国家、让人民赚实实在在的钱,而缺乏技术底气的发展,只能带来一阵虚火。如果中国在技术方面能更强一些,更加尊重技术人才,那么,我不敢说GDP是否能有这么大的增长,但是经济绝对不会像现在这么热,通货膨胀绝对不会这么严重,人民生活得肯定比现在幸福,国家也会比现在更强盛。房地产不是洪水猛兽,但这几年中国的这帮房地产商,绝对是历史的罪人。
Free BSD,Linux之比较
1. Linux
优点: 充分发挥 PC 的功能,花样极多,玩起来很有趣,各方面的表现都不错。
缺点: 太过自由,以致於发散掉了,维护方面比 FreeBSD 麻烦(对一般人来说)。
-> 适合喜欢「玩 PC」,更甚於「玩 UNIX(Network)」的人。
2. FreeBSD
优点: 非常 UNIX、非常 Free、非常 BSD -- UNIX 的理想归宿!!
缺点: 太过 UNIX,以致於玩下去很难收手 ^^;;
-> 适合喜欢 UNIX,有心好好经营 service 的人;也是 programmer 的理想 OS。
FreeBSD Core Team 并不是刻意忽略「入门的方便性」,只是人力有限,把主力投注在「UNIX 风味的主题」上。
FreeBSD 对硬体的需求实在也不会太严刻,对刚接触的人,建议使用「最一般化」的 硬体,像是: IDE (BigFoot)、ne2000 compatible 杂牌卡,S3Trito64,最烂的14寸 VGA,(atapi-cdrom)。
想说明的是,希望对 FreeBSD 有兴趣的人,别买些「太高档(或者说奇怪)」的硬体, 到时候装不起来就骂 FreeBSD 怎麽这麽烂 ^^;;
可以想一下,到底想试试自己的PC能跑多少东西,还是真的有心进入 UNIX 的世界。
=== 为什麽要选择 FreeBSD ?! ===
嗯...现在有许多免费的 i386 UNIX (在 386 以上 PC 执行的 UNIX),例如 Linux、NetBSD、FreeBSD、OpenBSD、386BSD 等,究竟你要如何选择属於你的
UNIX ?
玩了三年多的 UNIX (一年半 Linux,两个月 NetBSD,两年 FreeBSD)
笔者只能以非正式的说法说说笔者的个人意见,希望这些意见不要引起争论
各个作业系统优缺点的大战。
Linux 是容易上手而且好玩的作业系统,也是现今最多人玩的,正因 为它太好装了,只要硬体没问题闭著眼睛都装的起来,因此 如果你是 i386 UNIX 的新手,这可说是你入门的最佳试金石。
NetBSD 支援 13 种硬体架构,这也是它的强处,算是 multi-platform
的典范。 也因此,i386 在里面只算是 13 种中的一种,自然无法取得全力的发展,再加上其 core team 比较不活跃,所以在 i386 上的硬体支援并不是很好。
OpenBSD 源自 NetBSD,刚出来半年左右,专门把 NetBSD 跟 FreeBSD 的 新功能跟修正加在一起,算是 NetBSD+FreeBSD 的混血儿,由於 其 core team 人数少,加上程式码很少是自己开发的,因此现在
前景还不明朗。
FreeBSD 跟 NetBSD 一样都是基於 4.4 BSD-lite,但是 FreeBSD 现在只支援 i386,所以在 PC 上来说要比 NetBSD/OpenBSD 好太多了, 在从前NetBSD 跟 FreeBSD 的 core team 是一起的,後来分家了。 FreeBSD 具有一般 BSD 系统的稳定,又从其他作业系统学习了许 多优点,再加上自己开发的各种新功能,时时改进演算法以增加 执行效率,现在已是免费 BSD 系列中效率最好的,最主要是因为core team 活跃又乐於接受使用者的意见并改进。
* 什麽是 core team ?
core team 是一个专门对原始程式码做发展跟维护的组织,Linux 没有 core team,NetBSD/OpenBSD/FreeBSD 有。有 core team 的优点是
原始程式码会有一致性,会有组织的被更新,但是整个 OS 的活力也操在 core team 的手中,这就是 NetBSD 在笔者眼中无法兴盛的原因。而没有 core team(如 Linux),好处是全世界每个人都可以发表自己的修正(patch) 不须经由 core team 的审核,但缺点是 source code 杂乱无章且可能会 不同步。所以 Linux 在更新东东的时候,必须由使用者自己注意 kernel、 gcc、library、net-tool、modules、甚至各种 kernel patch 版本的一致性。
(或许在 RetHat Linux 已经稍微好一点了)
而这些可怜的情形在 FreeBSD 身上都不会发生。
* 要选择怎样的 OS 必须看你自己的需求及能力,还有周遭玩的人多不多, 多装几种,多装几次,自己感觉一下才是真的 !
(其实只要不怕 format 硬碟,吃饱撑著,装什麽东西、装几次都好说嘛)
1. 稳定性
一个作业系统最重要的就是稳定性,比方说能连续开机多久,能忍受 多少系统负荷,网路不稳时会不会当掉,网路负荷太大时网路会不会 死掉,笔者个人觉得 FreeBSD > Linux。
尤其许多研究已经提出,Linux 在高系统负荷下的表现相当不好,而
FreeBSD 却不会。要知道世界上最大的 ftp site - wcarchive.cdrom.com 是一台跑著
FreeBSD 的 Pentium pro 机器 (P6-150,512MB RAM,72GB HDs online
more than 1200 ftp users allowed)
注 : wcarchive.cdrom.com = ftp.cdrom.com
2. 网路
争夺封包(packet)的速度,除了网路卡好坏之外,最重要的还是作业系统跟 驱动程式,使用一样的网路卡 FreeBSD > Linux >>> DOS+NCSA. 而且
FreeBSD 在 RPC 及 NFS 上都比 Linux 来的稳定及快速。毕竟 BSD 在网路
这方面是始祖.
3. 移植软体的难易程度
现今一般的软体大多是为 BSD 写的,所以一般软体在 BSD 上会比在SYSV 上容易编译。而 FreeBSD 是 4.4BSD based,Linux 是 SYSV 加 上BSD-extension,所以在 Linux 上编译东西有时是个梦靥 (不是很 SYSV 也不是很 Posix 也不是很 BSD)。不过现在越来越多的软体会注 意到 Linux,因为 Linux 使用者太多了。
FreeBSD 有收集数百种软体的 ports,只要打个 make 就可以轻松编译,不然也有编译好的 binary 可以直接安装使用。
4. 硬体支援
Linux 支援最多种的硬体,NetBSD 最少,而 FreeBSD 夹在中间正急起
直追中,而且许多 FreeBSD 的 driver 都写的相当棒,反而後来被
移植到 NetBSD 跟 Linux。
5. Merged VM/buffer cache
Linux 的磁碟 I/O 速度是一流的,因为一来 Linux 的 ext2fs 是 async-mount 的,写入资料时不须一直更新 meta-data,最主要还是 Linux 会把目前没用到的记忆体尽量拿来做 I/O buffer。一般传统 BSD(如 SunOS,NetBSD)都只有固定大小的 buffer,而 FreeBSD 自己发展出类似 Linux 的 Merged VM/buffer cache,大大提高了 I/O 时的效率以及记忆体利用率,而且现在 FreeBSD 已支援 async-mount, 使得 FreeBSD 的档案系统已经跟 Linux 不相上下,甚至更胜一筹。
6. tty 限制
现在 Linux 要用超过 64 个 tty 除了必须更改应用程式的原始程式码, 还必须做 kernel patch,而 FreeBSD 内定支援 tty[pqrsPQRS][0-9a-v] 总共 256 个 tty,只要到 /dev 下用 MAKEDEV 把 tty 建出来,在 /etc/ttys 加入新的 tty 设定,再到 kernel config file 中把 pty 的数目打入 256 就好了,要使用超过 256 tty 也相当容易修改。
7. 完整原始程式码取得
一般人使用的 Slackware 版 Linux 是由 Slackware 公司整理,所 以一般人要取得完整原始程式码必须自己东抓西抓,这也是 Linux 在 NCTUCCCA 的 mirror 量这麽大的缘故。但往往 Linux 使用者找不到 自己须要的原始程式码,如果没有那些整理 Linux packages 的公司, 以及帮忙 Linux 发展系统工具及函式库的人,Linux 充其量算是只有 Linus 写的 kernel 而已,不过最大的问题还是各家写出来的东东 一致性的问题。不过新出来的 RedHat 已经提供一个简单的软体同步 与更新的方法 - RPM,也算是稍微抒解这一类问题的严重性。 而 FreeBSD 提供完整的系统原始程式码,从 /bin /sbin /usr/bin
/usr/sbin /usr/lib ... 甚至 /etc /usr/share/FAQ 都在里面, 让你可以很容易的更改自己想要的东东,要更新系统时也可以抓取 最新的 source 打个 make world 就成了 (当然也可以用 core team 做好的 binary),它甚至会自动检查各目录的权限是否正确。 简单一句,就是非常的有组织! 利用 binary 来升级只要不到一小时就可以完成,甚至有写好的 script 可以使用。
8. 目录档案组织化
FreeBSD 根据 4.4BSD 规范,什麽档案应该在那里,应该是什麽权限,
编译时应该连结(link)成 static 或 dynamic,都非常的严谨,该有的
manpages 绝对不会少。不像 Linux,写 kernel 一个人、写 library
另一个,写 manpages 又另一个、整理 utility 又另一个,各自为政
不同步,常常档案到处乱放或是重覆,manpages 不完整,许多目录档案
为了新旧版本的相容性而 link 来 link 去。
9. 系统安全
FreeBSD 使用 shadow password,支援 secure NFS,不像 Linux 要 自己安装 shadow password,将来编译 ftpd,sudo 时又得改来改去。
因为USA 版的 DES 禁止输出到美加以外地区,FreeBSD 为了全世界广大
的使用者,在密码系统上内定使用 MD5 编码,它比 DES 来的安全,如果
你不跟 SunOS 类的 YP server 跑 NIS,那你是不须要安装 DES 的。如果
你要使用 DES,你可以安装可以自由流动的 DES 版本 (非 USA 版),在
/usr/share/FAQ/Text/FreeBSD.FAQ 中有提及那里可以取得,或是到台湾
任何一个 FTP 站取得。
此外,FreeBSD 的使用者登入控制,以及档案安全层级保护都比其他
作业系统来的好 (kernel secure level)。
FreeBSD 的 core team 会注意 source code 跟 security 的同步性,
一有新的问题或 sendmail 漏洞,就会立刻更新程式码,已达到最佳的
系统安全。
8. core team 活跃
FreeBSD 的 core team 非常活跃而且谦虚,带动整个 FreeBSD 迅速
发展,每天都有新的 patch 出来,让使用者以 sup/ctm 来定时自动
更新原始程式码。
9. 4.4BSD-lite based
由於 FreeBSD 是基於 4.4BSD-lite 的,因此带来了许多 BSD 的好处,
像网路速度稳定、容易移植软体、安全快速等。
10. 从 Linux 而来的优点
FreeBSD 正在把 Linux 的 dosemu 移植过来,甚至可以直接执行 linux
的 binary (linux emulator),还有移植 Linux 支援的一些驱动程式。
11. 支援 LKM
FreeBSD 支援 Loadable kernel module,也就是说许多驱动程式
在编译 kernel 时可以不必做进去,一旦你要用到时,kernel 会自动
从 /lkm/*.o 载入该 driver,这样可以提高弹性并减小 kernel 使用的
记忆体空间。未来 FreeBSD 会朝向 LKM device 迈进,就像 Solaris
一样不需编译 kernel。
12. 直接执行 gzip 的程式
FreeBSD 可以直接执行 gzip 的程式,如果你把所有的执行档都 gzip
起来,不就等於用 stacker/doublespace 一样了 ?!
13. 线上监控
kernel 支援 tty snoop,可以监控线上使用者 (不像 linux 那个半调子
ttysnoop,会导致许多问题)。
14. 众多档案系统
支援 MFS (Memory File System),类似 SunOS tmpfs 的东东,还有
许多 4.4BSD 定义的档案系统,如 LFS、NULLFS、PORTALFS、UMAPFS
、UNIONFS。
15. Interleaved swap
当你有一个以上的 swap 装置时,会同时使用以增加速度 (尤其是使用
SCSI 装置时),而不是像 Linux 一个接著一个使用。
16. 新的 slice 观念
新的 slice 观念使得 FreeBSD 对其他 OS 的 partition 相容性比
传统的 BSD 好很多,在安装上也较为容易。
17. Binary 相容性
FreeBSD 可以执行 NetBSD-static,BSDI-static,Linux-a.out/elf,
SCO-static 等等的 binary code,增加不少相容性。
18. ccd (软体 RAID)
Concatenated disk (ccd) 驱动程式能让你拥有 Strip、Mirror,甚至
Parity 等 RAID card 才有的功能。
19. 多国语言的支援
FreeBSD 的 localization 是所有免费作业系统中做的最好的,甚至已经
有了亚洲语系(中文、日文)的安装介面。
20. 有组织的原始程式码
FreeBSD 的程式开发者在撰写程式码的时候,会去参考各种 RFC 规范以及 新的理论文献,因此 FreeBSD 的程式码有条不紊、层次鲜明;反观 Linux
常常为了急就章而走捷径写出来的东西,到最後开发新功能时又必须改来
改去。
不过随著时间的发展,Linux、*BSD 都会进步,对於免费的作业系统能
越来越好自然是乐见其成的。
一般而言,如果你须要一台稳定快速的 Internet Server,FreeBSD 是你绝对 的选择;如果你是个人使用或只是想学习 UNIX,Linux 跟 FreeBSD 都是很好 的试金石。
Linux 浮上台面已经四年了,而 FreeBSD 以短短的两年时间就拥有了众多的 使用者人口 (尤其是伺服器,以及程式开发者),高手的选择必有他的道理。
用过 FreeBSD 才知道,『PC 不只是很便宜的工作站』
但是,Linux 的优点是『好玩』,而且随著 kernel 日渐更新,很多东西也 越来越稳定。我们系上从两年前开始就用 Linux 当 mail, acounts, ftp, gopher, terminal, ppp, slip, BBS servers, 最近又加入 WWW server,服务几百位师生。 目前系上已经有好几台 Linux PC 一起运作,其中包含 NFS,与 WinNT,Win95 的连线与资源共享(by SAMBA packages),我们也在测试用其中一台摹拟 Novell Server.
我们的同时上线人数一般不会超过 100 人,用 Linux 来应付绰绰有馀。如果你想开 的是一次几百人上线的 BBS 大站,那可能 FreeBSD 会比较适合。不过话说 回来,能开这种大站的单位都很有钱,大都拿 SUN 或其他 workstation 级的来 run。
Linux 另一个优点是全球的 Linux users 远超过 FreeBSD,这使得 Linux 上面 新的软体跟硬体 drivers 更新数目及速度远超过 FreeBSD。例如,DOSEMU 可以 摹拟 DOS,WINE 可以摹拟 Windows 3.1,smbfs 可以将 Win95 或 WinNT 上的 partition 拿来用:这些在 FreeBSD 上面都还在发展中,甚至没有。新电脑 硬体 drivers 的更新也是如此,几乎任何新的硬体都会有 Linux 迷很快地帮大家 写好 drivers。你如果用过 FreeBSD 跟 Linux,你就会发现 FreeBSD 目前对 硬体要求仍然比较『严格』(其实是还没有人写 drivers)。我用的 scanner, 还有 voice modem,都已经有 Linux 迷写好程式,让我可以在 Linux 上 scan 以及有语音信箱。
我个人的建议是,如果你是个人使用,或者网路同时上线人数不超过一百人以上, Linux 的确是好玩又实用,而且新的硬体很快地几乎都可以在 Linux 上使用。 如果你要架的是几百人上站的机器,又没钱买 workstation,那 FreeBSD 在 网路壅塞时的 performance 的确不错。如果是个人要『玩』,我并不建议 FreeBSD,那会使你觉得提不起兴致(纯属个人观点)。
在 csie gopher 中有关 Linux 与 FreeBSD 的比较中,有一项是 FreeBSD 上 software porting 比较 easy。但是这个 comment 随著 Linux users 群日渐庞大, 我觉得已经有些改变:现在在 Linux 很多东西根本用不著 porting,因为很多 软体根本就是 Linux fans 专门为 Linux 设计写出来的,反而要用这些东西 需要额外费心去修改以便能在 FreeBSD 上使用。DOSEMU,smbfs 即是其中几个
例子。据最近的 newsgroups,FreeBSD core team 有五十多人,但是 Linux fans 散布在全球各地的 programmers 其数量根本无法计算。有心的话, 比较下 Linux 跟 FreeBSD announce newsgroups 就可知一二。
所以,我并不是很赞同一个 UNIX 的新手去玩 FreeBSD。但是,假如有人已经玩过 Linux ,或者在其他工作站级机器有过简单管理经验,那他们会发现
FreeBSD 极易入手。玩过 FreeBSD 的人一定知道光要新增 partitions 就已经是一件麻烦的事。堂堂一个 FreeBSD 的 fdisk 介面连 M$DOS 的都不如, 可见 FreeBSD core team 之目标不在一般连 ls, cp, tar 都不懂的newbie。 另外一个动机是假如你必须要架一台超稳定的 Internet server,那 FreeBSD 是目前的 best choice。
其实呢,如果有心要玩,大可弄个大点的硬碟,同时装上两个系统,一定
可以如鱼得水。我的 office 中同时有一台 FreeBSD,一台 Linux,各做各的事, 也是很快乐。。。。
就目前使用者能观察到的来看, 一般相信 linux 的 data-path-consumed process 的执行速度, 是众多 x86 作业系统中最快的; 而 high load 下的网路则 令人不能感到非常满意. 虽然 linux 第二版後网路 部分有了很大的改善, 据 Linus 本人的说法, linux 在传 single package 已比 FreeBSD 还优胜, 但作
为 NFS 或 high load netserver 还是显得略有不顺 (所谓 "不顺" 与 "不稳" 无关). 毕竟, 考查 linux 的发展历史, 的确是先在 x86-embeded scheduler, fs, 等核心process 执行部分, 最後才加进网路部分, process 执行最佳而网路稍逊乃是合理 的结果.
一般建议如果机器用来执行程式 (如跑 project) 为主, 跑各式怪模怪样的小程式及 server, 或有非正统硬体者 使用 linux 可能较佳.
1. Linux
优点: 充分发挥 PC 的功能,花样极多,玩起来很有趣,各方面的表现都不错。
缺点: 太过自由,以致於发散掉了,维护方面比 FreeBSD 麻烦(对一般人来说)。
-> 适合喜欢「玩 PC」,更甚於「玩 UNIX(Network)」的人。
2. FreeBSD
优点: 非常 UNIX、非常 Free、非常 BSD -- UNIX 的理想归宿!!
缺点: 太过 UNIX,以致於玩下去很难收手 ^^;;
-> 适合喜欢 UNIX,有心好好经营 service 的人;也是 programmer 的理想 OS。
FreeBSD Core Team 并不是刻意忽略「入门的方便性」,只是人力有限,把主力投注在「UNIX 风味的主题」上。
FreeBSD 对硬体的需求实在也不会太严刻,对刚接触的人,建议使用「最一般化」的 硬体,像是: IDE (BigFoot)、ne2000 compatible 杂牌卡,S3Trito64,最烂的14寸 VGA,(atapi-cdrom)。
想说明的是,希望对 FreeBSD 有兴趣的人,别买些「太高档(或者说奇怪)」的硬体, 到时候装不起来就骂 FreeBSD 怎麽这麽烂 ^^;;
可以想一下,到底想试试自己的PC能跑多少东西,还是真的有心进入 UNIX 的世界。
=== 为什麽要选择 FreeBSD ?! ===
嗯...现在有许多免费的 i386 UNIX (在 386 以上 PC 执行的 UNIX),例如 Linux、NetBSD、FreeBSD、OpenBSD、386BSD 等,究竟你要如何选择属於你的
UNIX ?
玩了三年多的 UNIX (一年半 Linux,两个月 NetBSD,两年 FreeBSD)
笔者只能以非正式的说法说说笔者的个人意见,希望这些意见不要引起争论
各个作业系统优缺点的大战。
Linux 是容易上手而且好玩的作业系统,也是现今最多人玩的,正因 为它太好装了,只要硬体没问题闭著眼睛都装的起来,因此 如果你是 i386 UNIX 的新手,这可说是你入门的最佳试金石。
NetBSD 支援 13 种硬体架构,这也是它的强处,算是 multi-platform
的典范。 也因此,i386 在里面只算是 13 种中的一种,自然无法取得全力的发展,再加上其 core team 比较不活跃,所以在 i386 上的硬体支援并不是很好。
OpenBSD 源自 NetBSD,刚出来半年左右,专门把 NetBSD 跟 FreeBSD 的 新功能跟修正加在一起,算是 NetBSD+FreeBSD 的混血儿,由於 其 core team 人数少,加上程式码很少是自己开发的,因此现在
前景还不明朗。
FreeBSD 跟 NetBSD 一样都是基於 4.4 BSD-lite,但是 FreeBSD 现在只支援 i386,所以在 PC 上来说要比 NetBSD/OpenBSD 好太多了, 在从前NetBSD 跟 FreeBSD 的 core team 是一起的,後来分家了。 FreeBSD 具有一般 BSD 系统的稳定,又从其他作业系统学习了许 多优点,再加上自己开发的各种新功能,时时改进演算法以增加 执行效率,现在已是免费 BSD 系列中效率最好的,最主要是因为core team 活跃又乐於接受使用者的意见并改进。
* 什麽是 core team ?
core team 是一个专门对原始程式码做发展跟维护的组织,Linux 没有 core team,NetBSD/OpenBSD/FreeBSD 有。有 core team 的优点是
原始程式码会有一致性,会有组织的被更新,但是整个 OS 的活力也操在 core team 的手中,这就是 NetBSD 在笔者眼中无法兴盛的原因。而没有 core team(如 Linux),好处是全世界每个人都可以发表自己的修正(patch) 不须经由 core team 的审核,但缺点是 source code 杂乱无章且可能会 不同步。所以 Linux 在更新东东的时候,必须由使用者自己注意 kernel、 gcc、library、net-tool、modules、甚至各种 kernel patch 版本的一致性。
(或许在 RetHat Linux 已经稍微好一点了)
而这些可怜的情形在 FreeBSD 身上都不会发生。
* 要选择怎样的 OS 必须看你自己的需求及能力,还有周遭玩的人多不多, 多装几种,多装几次,自己感觉一下才是真的 !
(其实只要不怕 format 硬碟,吃饱撑著,装什麽东西、装几次都好说嘛)
1. 稳定性
一个作业系统最重要的就是稳定性,比方说能连续开机多久,能忍受 多少系统负荷,网路不稳时会不会当掉,网路负荷太大时网路会不会 死掉,笔者个人觉得 FreeBSD > Linux。
尤其许多研究已经提出,Linux 在高系统负荷下的表现相当不好,而
FreeBSD 却不会。要知道世界上最大的 ftp site - wcarchive.cdrom.com 是一台跑著
FreeBSD 的 Pentium pro 机器 (P6-150,512MB RAM,72GB HDs online
more than 1200 ftp users allowed)
注 : wcarchive.cdrom.com = ftp.cdrom.com
2. 网路
争夺封包(packet)的速度,除了网路卡好坏之外,最重要的还是作业系统跟 驱动程式,使用一样的网路卡 FreeBSD > Linux >>> DOS+NCSA. 而且
FreeBSD 在 RPC 及 NFS 上都比 Linux 来的稳定及快速。毕竟 BSD 在网路
这方面是始祖.
3. 移植软体的难易程度
现今一般的软体大多是为 BSD 写的,所以一般软体在 BSD 上会比在SYSV 上容易编译。而 FreeBSD 是 4.4BSD based,Linux 是 SYSV 加 上BSD-extension,所以在 Linux 上编译东西有时是个梦靥 (不是很 SYSV 也不是很 Posix 也不是很 BSD)。不过现在越来越多的软体会注 意到 Linux,因为 Linux 使用者太多了。
FreeBSD 有收集数百种软体的 ports,只要打个 make 就可以轻松编译,不然也有编译好的 binary 可以直接安装使用。
4. 硬体支援
Linux 支援最多种的硬体,NetBSD 最少,而 FreeBSD 夹在中间正急起
直追中,而且许多 FreeBSD 的 driver 都写的相当棒,反而後来被
移植到 NetBSD 跟 Linux。
5. Merged VM/buffer cache
Linux 的磁碟 I/O 速度是一流的,因为一来 Linux 的 ext2fs 是 async-mount 的,写入资料时不须一直更新 meta-data,最主要还是 Linux 会把目前没用到的记忆体尽量拿来做 I/O buffer。一般传统 BSD(如 SunOS,NetBSD)都只有固定大小的 buffer,而 FreeBSD 自己发展出类似 Linux 的 Merged VM/buffer cache,大大提高了 I/O 时的效率以及记忆体利用率,而且现在 FreeBSD 已支援 async-mount, 使得 FreeBSD 的档案系统已经跟 Linux 不相上下,甚至更胜一筹。
6. tty 限制
现在 Linux 要用超过 64 个 tty 除了必须更改应用程式的原始程式码, 还必须做 kernel patch,而 FreeBSD 内定支援 tty[pqrsPQRS][0-9a-v] 总共 256 个 tty,只要到 /dev 下用 MAKEDEV 把 tty 建出来,在 /etc/ttys 加入新的 tty 设定,再到 kernel config file 中把 pty 的数目打入 256 就好了,要使用超过 256 tty 也相当容易修改。
7. 完整原始程式码取得
一般人使用的 Slackware 版 Linux 是由 Slackware 公司整理,所 以一般人要取得完整原始程式码必须自己东抓西抓,这也是 Linux 在 NCTUCCCA 的 mirror 量这麽大的缘故。但往往 Linux 使用者找不到 自己须要的原始程式码,如果没有那些整理 Linux packages 的公司, 以及帮忙 Linux 发展系统工具及函式库的人,Linux 充其量算是只有 Linus 写的 kernel 而已,不过最大的问题还是各家写出来的东东 一致性的问题。不过新出来的 RedHat 已经提供一个简单的软体同步 与更新的方法 - RPM,也算是稍微抒解这一类问题的严重性。 而 FreeBSD 提供完整的系统原始程式码,从 /bin /sbin /usr/bin
/usr/sbin /usr/lib ... 甚至 /etc /usr/share/FAQ 都在里面, 让你可以很容易的更改自己想要的东东,要更新系统时也可以抓取 最新的 source 打个 make world 就成了 (当然也可以用 core team 做好的 binary),它甚至会自动检查各目录的权限是否正确。 简单一句,就是非常的有组织! 利用 binary 来升级只要不到一小时就可以完成,甚至有写好的 script 可以使用。
8. 目录档案组织化
FreeBSD 根据 4.4BSD 规范,什麽档案应该在那里,应该是什麽权限,
编译时应该连结(link)成 static 或 dynamic,都非常的严谨,该有的
manpages 绝对不会少。不像 Linux,写 kernel 一个人、写 library
另一个,写 manpages 又另一个、整理 utility 又另一个,各自为政
不同步,常常档案到处乱放或是重覆,manpages 不完整,许多目录档案
为了新旧版本的相容性而 link 来 link 去。
9. 系统安全
FreeBSD 使用 shadow password,支援 secure NFS,不像 Linux 要 自己安装 shadow password,将来编译 ftpd,sudo 时又得改来改去。
因为USA 版的 DES 禁止输出到美加以外地区,FreeBSD 为了全世界广大
的使用者,在密码系统上内定使用 MD5 编码,它比 DES 来的安全,如果
你不跟 SunOS 类的 YP server 跑 NIS,那你是不须要安装 DES 的。如果
你要使用 DES,你可以安装可以自由流动的 DES 版本 (非 USA 版),在
/usr/share/FAQ/Text/FreeBSD.FAQ 中有提及那里可以取得,或是到台湾
任何一个 FTP 站取得。
此外,FreeBSD 的使用者登入控制,以及档案安全层级保护都比其他
作业系统来的好 (kernel secure level)。
FreeBSD 的 core team 会注意 source code 跟 security 的同步性,
一有新的问题或 sendmail 漏洞,就会立刻更新程式码,已达到最佳的
系统安全。
8. core team 活跃
FreeBSD 的 core team 非常活跃而且谦虚,带动整个 FreeBSD 迅速
发展,每天都有新的 patch 出来,让使用者以 sup/ctm 来定时自动
更新原始程式码。
9. 4.4BSD-lite based
由於 FreeBSD 是基於 4.4BSD-lite 的,因此带来了许多 BSD 的好处,
像网路速度稳定、容易移植软体、安全快速等。
10. 从 Linux 而来的优点
FreeBSD 正在把 Linux 的 dosemu 移植过来,甚至可以直接执行 linux
的 binary (linux emulator),还有移植 Linux 支援的一些驱动程式。
11. 支援 LKM
FreeBSD 支援 Loadable kernel module,也就是说许多驱动程式
在编译 kernel 时可以不必做进去,一旦你要用到时,kernel 会自动
从 /lkm/*.o 载入该 driver,这样可以提高弹性并减小 kernel 使用的
记忆体空间。未来 FreeBSD 会朝向 LKM device 迈进,就像 Solaris
一样不需编译 kernel。
12. 直接执行 gzip 的程式
FreeBSD 可以直接执行 gzip 的程式,如果你把所有的执行档都 gzip
起来,不就等於用 stacker/doublespace 一样了 ?!
13. 线上监控
kernel 支援 tty snoop,可以监控线上使用者 (不像 linux 那个半调子
ttysnoop,会导致许多问题)。
14. 众多档案系统
支援 MFS (Memory File System),类似 SunOS tmpfs 的东东,还有
许多 4.4BSD 定义的档案系统,如 LFS、NULLFS、PORTALFS、UMAPFS
、UNIONFS。
15. Interleaved swap
当你有一个以上的 swap 装置时,会同时使用以增加速度 (尤其是使用
SCSI 装置时),而不是像 Linux 一个接著一个使用。
16. 新的 slice 观念
新的 slice 观念使得 FreeBSD 对其他 OS 的 partition 相容性比
传统的 BSD 好很多,在安装上也较为容易。
17. Binary 相容性
FreeBSD 可以执行 NetBSD-static,BSDI-static,Linux-a.out/elf,
SCO-static 等等的 binary code,增加不少相容性。
18. ccd (软体 RAID)
Concatenated disk (ccd) 驱动程式能让你拥有 Strip、Mirror,甚至
Parity 等 RAID card 才有的功能。
19. 多国语言的支援
FreeBSD 的 localization 是所有免费作业系统中做的最好的,甚至已经
有了亚洲语系(中文、日文)的安装介面。
20. 有组织的原始程式码
FreeBSD 的程式开发者在撰写程式码的时候,会去参考各种 RFC 规范以及 新的理论文献,因此 FreeBSD 的程式码有条不紊、层次鲜明;反观 Linux
常常为了急就章而走捷径写出来的东西,到最後开发新功能时又必须改来
改去。
不过随著时间的发展,Linux、*BSD 都会进步,对於免费的作业系统能
越来越好自然是乐见其成的。
一般而言,如果你须要一台稳定快速的 Internet Server,FreeBSD 是你绝对 的选择;如果你是个人使用或只是想学习 UNIX,Linux 跟 FreeBSD 都是很好 的试金石。
Linux 浮上台面已经四年了,而 FreeBSD 以短短的两年时间就拥有了众多的 使用者人口 (尤其是伺服器,以及程式开发者),高手的选择必有他的道理。
用过 FreeBSD 才知道,『PC 不只是很便宜的工作站』
但是,Linux 的优点是『好玩』,而且随著 kernel 日渐更新,很多东西也 越来越稳定。我们系上从两年前开始就用 Linux 当 mail, acounts, ftp, gopher, terminal, ppp, slip, BBS servers, 最近又加入 WWW server,服务几百位师生。 目前系上已经有好几台 Linux PC 一起运作,其中包含 NFS,与 WinNT,Win95 的连线与资源共享(by SAMBA packages),我们也在测试用其中一台摹拟 Novell Server.
我们的同时上线人数一般不会超过 100 人,用 Linux 来应付绰绰有馀。如果你想开 的是一次几百人上线的 BBS 大站,那可能 FreeBSD 会比较适合。不过话说 回来,能开这种大站的单位都很有钱,大都拿 SUN 或其他 workstation 级的来 run。
Linux 另一个优点是全球的 Linux users 远超过 FreeBSD,这使得 Linux 上面 新的软体跟硬体 drivers 更新数目及速度远超过 FreeBSD。例如,DOSEMU 可以 摹拟 DOS,WINE 可以摹拟 Windows 3.1,smbfs 可以将 Win95 或 WinNT 上的 partition 拿来用:这些在 FreeBSD 上面都还在发展中,甚至没有。新电脑 硬体 drivers 的更新也是如此,几乎任何新的硬体都会有 Linux 迷很快地帮大家 写好 drivers。你如果用过 FreeBSD 跟 Linux,你就会发现 FreeBSD 目前对 硬体要求仍然比较『严格』(其实是还没有人写 drivers)。我用的 scanner, 还有 voice modem,都已经有 Linux 迷写好程式,让我可以在 Linux 上 scan 以及有语音信箱。
我个人的建议是,如果你是个人使用,或者网路同时上线人数不超过一百人以上, Linux 的确是好玩又实用,而且新的硬体很快地几乎都可以在 Linux 上使用。 如果你要架的是几百人上站的机器,又没钱买 workstation,那 FreeBSD 在 网路壅塞时的 performance 的确不错。如果是个人要『玩』,我并不建议 FreeBSD,那会使你觉得提不起兴致(纯属个人观点)。
在 csie gopher 中有关 Linux 与 FreeBSD 的比较中,有一项是 FreeBSD 上 software porting 比较 easy。但是这个 comment 随著 Linux users 群日渐庞大, 我觉得已经有些改变:现在在 Linux 很多东西根本用不著 porting,因为很多 软体根本就是 Linux fans 专门为 Linux 设计写出来的,反而要用这些东西 需要额外费心去修改以便能在 FreeBSD 上使用。DOSEMU,smbfs 即是其中几个
例子。据最近的 newsgroups,FreeBSD core team 有五十多人,但是 Linux fans 散布在全球各地的 programmers 其数量根本无法计算。有心的话, 比较下 Linux 跟 FreeBSD announce newsgroups 就可知一二。
所以,我并不是很赞同一个 UNIX 的新手去玩 FreeBSD。但是,假如有人已经玩过 Linux ,或者在其他工作站级机器有过简单管理经验,那他们会发现
FreeBSD 极易入手。玩过 FreeBSD 的人一定知道光要新增 partitions 就已经是一件麻烦的事。堂堂一个 FreeBSD 的 fdisk 介面连 M$DOS 的都不如, 可见 FreeBSD core team 之目标不在一般连 ls, cp, tar 都不懂的newbie。 另外一个动机是假如你必须要架一台超稳定的 Internet server,那 FreeBSD 是目前的 best choice。
其实呢,如果有心要玩,大可弄个大点的硬碟,同时装上两个系统,一定
可以如鱼得水。我的 office 中同时有一台 FreeBSD,一台 Linux,各做各的事, 也是很快乐。。。。
就目前使用者能观察到的来看, 一般相信 linux 的 data-path-consumed process 的执行速度, 是众多 x86 作业系统中最快的; 而 high load 下的网路则 令人不能感到非常满意. 虽然 linux 第二版後网路 部分有了很大的改善, 据 Linus 本人的说法, linux 在传 single package 已比 FreeBSD 还优胜, 但作
为 NFS 或 high load netserver 还是显得略有不顺 (所谓 "不顺" 与 "不稳" 无关). 毕竟, 考查 linux 的发展历史, 的确是先在 x86-embeded scheduler, fs, 等核心process 执行部分, 最後才加进网路部分, process 执行最佳而网路稍逊乃是合理 的结果.
一般建议如果机器用来执行程式 (如跑 project) 为主, 跑各式怪模怪样的小程式及 server, 或有非正统硬体者 使用 linux 可能较佳.