显示标签为“%incompleted”的博文。显示所有博文
显示标签为“%incompleted”的博文。显示所有博文

星期二, 一月 22, 2008

C static && dynamic library

首先看如下几个 C 源文件:
roc@ulfs:~/c$ cat main.c
#include
// #include "mod1.h"

void main() {
printf("Hello world: %d\n", func(8));
return;
}

roc@ulfs:~/c$ cat mod1.h
#include
// #include "mod2.c"
#include "mod2.h"

roc@ulfs:~/c$ cat mod2.h
int func(int i);

roc@ulfs:~/c$ cat mod2.c
#include

int func(int i) {
int I = i*i;
printf("i == %d\n", i);
return I;
}
然后进行编译并运行:
roc@ulfs:~/c$ gcc main.c mod2.c
roc@ulfs:~/c$ gcc main.c mod2.c
main.c: In function 'main':
main.c:4: warning: return type of 'main' is not 'int'
roc@ulfs:~/c$ ./a.out
i == 8
Hello world: 64
此时完全不需要头文件,编译器直接链接了。那么究竟何时需要头文件呢?

是不是和动态连接库有关呢?首先了解一下动态链接库:
roc@ulfs:~/c$ gcc -c main.c
roc@ulfs:~/c$ gcc -c mod2.c
roc@ulfs:~/c$ gcc -shared -o libmod2.so mod2.o
roc@ulfs:~/c$ gcc -shared -o libmod2.so.1 mod2.c
roc@ulfs:~/c$ diff libmod2.so libmod2.so.1
// 显示两个文件是完全一样的
roc@ulfs:~/c$ file libmod2.so
libmod2.so: ELF 32-bit LSB shared object, Intel 80386, version 1 (SYSV), not stripped
roc@ulfs:~/c$ file mod2.o
mod2.o: ELF 32-bit LSB relocatable, Intel 80386, version 1 (SYSV), not stripped
// 此时是两种不同的文件
然后尝试用两种方式编译:
roc@ulfs:~/c$ gcc -o dynamic.out main.o libmod2.so
roc@ulfs:~/c$ gcc -o dynamic.out.1 main.c libmod2.so
main.c: In function 'main':
main.c:4: warning: return type of 'main' is not 'int'
roc@ulfs:~/c$ diff dynamic.out dynamic.out.1
// 两个文件也是一样的
然后运行:
roc@ulfs:~/c$ ./dynamic.out
./dynamic.out: error while loading shared libraries: libmod2.so: cannot open shared object file: No such file or directory
roc@ulfs:~/c$ sudo vi /etc/ld.so.conf
...
/home/roc/c
# ADD THIS LINE
# 以确保运行时可以找到这个共享库,应该也可以使用 LD_LIBRARY_PATH 环境变量
roc@ulfs:~/c$ sudo ldconfig
roc@ulfs:~/c$ ./dynamic.out
i == 8
Hello world: 64
// 可以正确运行
// 此时仍然不需要使用头文件


# ******
roc@ulfs:~/c$ gcc -static -o static.out main.o libmod2.so
/usr/bin/ld: attempted static link of dynamic object `libmod2.so'
collect2: ld returned 1 exit status

# ****** ???

如果我不使用 libmod2.so 文件名,而使用 mod2.so 文件名会如何呢?
roc@ulfs:~/c$ gcc -shared -o mod2.so mod2.o
roc@ulfs:~/c$ diff mod2.so libmod2.so
roc@ulfs:~/c$ gcc -o dy.out main.o mod2.so
roc@ulfs:~/c$ ./dy.out
./dy.out: error while loading shared libraries: mod2.so: cannot open shared object file: No such file or directory
roc@ulfs:~/c$ LD_LIBRARY_PATH=. ./dy.out
i == 8
Hello world: 64
使用 libmod2.so 这种形式的文件名是为了在 gcc 上使用 -l 参数时更方便,能够直接找到相关的库而不需要指定具体的文件:

roc@ulfs:~/c$ gcc -o dynamic.out main.o -lmod2
/usr/bin/ld: cannot find -lmod2
collect2: ld returned 1 exit status
roc@ulfs:~/c$ gcc -o dynamic.out main.o -L. -lmod2
# 或者:
roc@ulfs:~/c$ LIBRARY_PATH=. gcc -o dynamic.out main.o -lmod2
使用 -L 或 LIBRARY_PATH 是为了保证在编译的时候能够找到需要的共享库文件。

到目前为止,没有看到必须要使用头文件的地方!

另外,可以了解一下 lib*.a 文件,它实际上是(archive)的简写,和 tar 文件非常类似,不过 tar 最初是使用磁带的,而 ar 直接将磁盘上的 *.o 对象文件打包到 *.a 档案文件中,因此可以供静态链接使用。可以用 ar -t 命令查看一个 *.la 文件。

gcc -o dynamic.out main.c libmod2.a ??????

星期二, 一月 08, 2008

一个 iptables 的奇怪需求和 TCP/IP netmask 问题

前几天 xuni 说能不能设置 iptables 控制某个网段,比如 192.168.0.10-100 的 IP 禁止掉,因为如果一个一个去增加,似乎效率太低,无论是对人还是对机器来说。

但似乎不太容易实现,首先是如何划分网段,另外这样划分和实际的网段设置(192.168.0.0/24)不同,是不是能够有效,特别是,中间作为网络地址和广播地址的 IP 能否生效?

首先解决第二个问题。随便找个计算子网及其掩码的工具,可以得到划分子网的网络地址和掩码,例如:192.168.0.96/255.255.255.224,也就是 192.168.0.96/28,则主机 IP 范围是 192.168.0.97-126,广播地址是 192.168.0.127,因为我自己的主机是 192.168.0.125,在这个网段之内。我在 iptables 中禁掉这个网段,发现有效,所以虽然网络配置是 192.168.0.0/24,但 iptables 并不受此影响。

再查看边界,将自己的 IP 改为 192.168.0.96 和 192.168.0.127,发现都可以有效的被禁止,所以不存在空洞的问题。

在 know IP/netmask, ask for net address 中讨论过从 IP/netmask 计算网络地址的问题,但这里第一个问题涉及到划分子网段时能否使用向 192.168.0.96/255.255.255.249 这样的掩码。这比较难于理解,所以考虑一个比较极端的例子,在《TCP/IP 详解 卷一 协议》中关于 TCP/IP 有一个这样的习题,即 255.255.0.255 这样的子网掩码是否合法。

答案是合法的,不过不符合习惯,因为难于理解,最主要的问题是其主机地址被分割在不连续的空间中了。例如:192.168.0.2/255.255.0.255,其结果是主机地址的范围是:192.168.[1-254].2。

所以如果按照习惯用法分配在一个连续地址空间中,则 10-100 这样的需求不可能直接实现,因为子网划分总是固定的,只能间接地通过若干子网和 IP 组合来实现。

但习题上说有 16 位的主机地址空间,但不是只有 8 位吗?

星期五, 十一月 02, 2007

SNMP access 的一个问题

SNMP Agent 的配置文件:
sh$ egrep -v '(^#|^$)' snmpd.conf
syslocation Unknown (edit /etc/snmp/snmpd.conf)
syscontact Root (configure /etc/snmp/snmp.local.conf)
pass .1.3.6.1.4.1.4413.4.1 /usr/bin/ucd5820stat
syscontact monitor@zovatech.com
disk / 36G
com2sec mynet 192.168.0.0/24 public
com2sec mynet 222.66.231.106 public
group mynet v1 mynet
group mynet v2c mynet
view system included .1
access mynet "" any noauth exact system none none
exec .1.3.6.1.4.1.2021.54 hdNum /usr/local/bin/snmpdiskio hdNum
exec .1.3.6.1.4.1.2021.55 hdIndex /usr/local/bin/snmpdiskio hdIndex
exec .1.3.6.1.4.1.2021.56 hdDescr /usr/local/bin/snmpdiskio hdDescr
exec .1.3.6.1.4.1.2021.57 hdInBlocks /usr/local/bin/snmpdiskio hdInBlocks
exec .1.3.6.1.4.1.2021.58 hdOutBlocks /usr/local/bin/snmpdiskio hdOutBlocks
本来没有设置 com2sec mynet 222.66.231.106 的。因为 SNMP client 这台主机有两个 IP,一个是内网的 192.168.0.1,还有一个是外网的 202.66.231.106,而 SNMP Agent 所在主机为 192.168.0.197。则在配置 SNMP access 的候,一个很直接的想法就是只要允许了所有内网的主机就应该可以了呀,但是在这里却不行,对于 192.168.0.1 这台主机,必须要使用外网(eth1)的那个 IP 来指定。

不知道原因是什么?

星期二, 九月 25, 2007

python catch OSError

两种形式都可以:
except OSError, (errno, strerr):
# OR
except OSError, osexc:
print osexc.errno, osexc.message
但为什么两个都可以呢?OSError 的 instance osexc 怎么转换成 tuple 的呢?

星期一, 九月 03, 2007

sed 两个例子

1. 排除最后一行:这是在统计 Apache 的日志时用到的,我需要统计某个站点流量的总和:
zcat $logfile \
| grep $site \
| awk '{print $11}' \
| grep -v '^-' \
| sed '$!s/.*/&+\\/g' \
| bc -l


2. sed 如何表示倒数第 n 行?

星期三, 八月 08, 2007

python socket buffers and blocking

前面 python socket write/read only 讨论了使用服务器端只写、客户端只读的方式来进行 mirrord/fs_mirror 之间的同步,那么服务器端如下:
...
fconn = conn.makefile('w', 0)
...
while True:
try:
try:
self.wmCond.acquire()
while self.manager.serial == serial: self.wmCond.wait()
sn = serial
serial = self.manager.serial
finally:
self.wmCond.release()
while sn < serial:
action, target = self.manager.wmlog[sn]
# #1:
fconn.write("%s:%s\n" % (action, target)) # OR:
# conn.sendall("%s:%s\n" % (action, target))
if debug:
print "after writing"
sn += 1
self.manager.svPool[self] = sn
客户端现在主要用在测试中,是这样的:
def __check_protocol(self, conn, fconn, md, expect, incr, func, *args):
svCurrent = md.Status.svCurrent()
sn = md.manager.serial
if mirrord.debug: print "sn:", sn
func(*args)
nloop = md.Status.nloop()
while md.Status.nloop() <= nloop + 1: time.sleep(1)
# Before the wmlog being read, the server should reserve it for the client
self.failUnlessEqual(md.manager.svPool[svCurrent], sn
result = []
for i in range(incr):
line = fconn.readline()[:-1]
result.append(line)
result.sort()
expect.sort()
self.failUnlessEqual(result, expect)
nloop = md.Status.nloop()
while md.Status.nloop() <= nloop + 1: time.sleep(1)
# After the wmlog being transported, the server should delete the overdue records
self.failUnlessEqual(md.manager.svPool[svCurrent], sn + incr)
运行测试时,会在上面的
self.failUnlessEqual(md.manager.svPool[svCurrent], sn
处报错,实际是 md.manager.svPool[svCurrent] > sn!

从输出来分析原因,可以发现在服务器端代码的 #1 处并没有被阻塞!也就是说,fconn.write() 或 conn.sendall() 并不会等待客户端收到数据才返回。实际上,socket 和文件操作一样,使用缓冲 buffers 方法,只有数据被 write() 或 sendall() 到缓冲区以后就会立即返回。

这里虽然在前面使用了 fconn = conn.makefile('w', 0),也就是不使用缓冲,按道理来说,应该是要等待数据成功发送才返回的,但因为这个 fconn 只是一个文件对象,使用的是自己的缓冲,它在底层调用 socket 对象的 sendall() 方法,而这个 sendall() 方法在 Python 中一定会隐含使用 buffers,所以这里还是会立即返回。


《Python 网络编程基础》

对于很多操作系统来说,有时候在网络上发送的数据的调用会在远程服务器确保已经收到信息之前返回,因此,很有可能一个来自对 sendall() 成功调用返回的数据,实际上永远都没有被收到。


或者不完整或顺序不对?

总之,recv() 在缓冲是空的时候阻塞,而 sendall() 等应该是在 buffers 满的时候阻塞!

因此,上面的测试代码,在 func(*args) 被调用之后(通常是 os.mkdir,open('...', 'w') 或 shutil.move 之类的),导致文件系统变化,这样服务器端 fconn.write() 之后立刻返回,则该服务线程的 serial 必然增加,而不会等到下面 line = fconn.readline() 的时候,所以比较 serial 的测试会抛出错误。

所以除非使用一开始那种客户端发送确认回执的方法,否则在这里不能进行这种比较(或者也没必要进行这种比较?)。


但这是不是也就意味着在服务器和客户端之间实际上缺乏一种校准机制?
例如服务器端有一个 serial 为 11138 的 wmlog 记录,但客户端可能并没有收到,一种情况是客户端丢掉了这个记录,另一种情况是客户端不得不进行一次完全的初始化操作?

那么是否使用回执的方法就是正确的校准机制呢?

或者我应该向客户端传递 serial 号?

python socket write/read only

最初服务器端的代码是这样的:
while True:
try:
......
# When modified, inform the client first:
conn.sendall("NEXT")
data = conn.recv(1024)
if data != "OK":
raise ServerExc("Sync failed")
sending = []
while sn < serial:
action, target = self.manager.wmlog[sn]
sending.append("%s:%s\n" % (action, target))
if debug:
print "after writing"
sn += 1
self.manager.svPool[self] = sn
# Assign must be an atomic operation, but dict[k] and getattr?
# But I think it is not necessary to Lock at here.
fconn.writelines(sending)
fconn.write("EOF\n")
fconn.flush()
客户端是这样的(同样在一个循环中):
data = conn.recv(1024)
self.failUnlessEqual(data, "NEXT")
conn.sendall("OK")
result = []
for i in range(incr):
line = fconn.readline()[:-1]
if line == 'EOF': break
result.append(line)
这时候,相当于每次服务器端要发送数据之前,都会先通知客户端,并等待客户端的一个回执。这时我考虑,因为两边都在循环,所以我只需要在服务器端不断的写入,在客户端不断的读取就可以了,因为从这里直到线程退出都不会在发送别的类型的数据。

那么就写成这样:
while True:
try:
......
while sn < serial:
action, target = self.manager.wmlog[sn]
fconn.write("%s:%s\n" % (action, target)) # OR:
# conn.sendall("%s:%s\n" % (action, target))
if debug:
print "after writing"
sn += 1
self.manager.svPool[self] = sn
# Assign must be an atomic operation, but dict[k] and getattr?
# But I think it is not necessary to Lock at here.
客户端:
result = []
for i in range(incr):
line = fconn.readline()[:-1]
result.append(line)
但这时就有一个问题,就是之前采用回执办法的时候,客户端退出时服务器端的 conn.recv() 总会得到空,因此可以通过判断来决定是否要关闭这个 socket(但如果是异常终止呢?)。

而现在服务器端不再做判断,如果客户端退出或异常终止,服务器端会出现什么情况呢?使用一个原型来看看:
#!/usr/bin/env python
# -*- encoding: utf-8 -*-

"""
Prototype: server write only, client read only,
if client terminated, what about the server?
This is the server side: write only

Author: Roc Zhou
Date: 2007-08-08
Email: chowroc.z@gmail.com
"""

import sys
import time
import socket
import traceback
import threading

def server(conn):
name = threading.currentThread().getName()
print "Client server: %s" % name
# print "Got connection from: ", conn.getpeername()
conn.sendall("START")
data = conn.recv(1024)
if data != "OK": raise "FAILED"
fconn = conn.makefile('w', 0)
i = 0
while True:
# conn.sendall("message %d" % i)
fconn.write("message %d\n" % i)
i += 1
time.sleep(5)
print "Working thread of %s ending ..." % name
conn.close()

sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
sock.bind(('localhost', 2123))
print "Waiting for connections ..."
sock.listen(5)

while True:
try:
conn, addr = sock.accept()

print "Got connection from:", addr
that = threading.Thread(target=server, name='server', args=(conn,))
that.setDaemon(1)
that.start()
except KeyboardInterrupt:
raise
except:
print >> sys.stderr, "CATCH ALL:"
traceback.print_exc()
continue
客户端:
#!/usr/bin/env python
# -*- encoding: utf-8 -*-

"""
Prototype: server write only, client read only,
if client terminated, what about the server?
This is the client side: read only

Author: Roc Zhou
Date: 2007-08-08
Email: chowroc.z@gmail.com
"""

import socket

host = 'localhost'
port = 2123

print "Creating socket ..."
conn = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
print "Connecting ..."
conn.connect((host, port))
print "Done."

data = conn.recv(1024)
if data != "START": raise "FAILED"
conn.sendall("OK")
fconn = conn.makefile('r', 0)

while True:
try:
message = fconn.readline()[:-1]
print message
except KeyboardInterrupt:
raise
运行及其输出

客户端:
sh$ python fs_mirror_10_read_only.py
Creating socket ...
Connecting ...
Done.
message 0
message 1
message 2
message 3
message 4
message 5
message 6
Traceback (most recent call last):
File "fs_mirror_10_read_only.py", line 32, in
message = fconn.readline()[:-1]
File "/usr/lib/python2.5/socket.py", line 331, in readline
data = recv(1)
KeyboardInterrupt


服务器端:
sh# python mirrord_10_write_only.py
Waiting for connections ...
Got connection from: ('127.0.0.1', 38618)
Client server: server
Exception in thread server:
Traceback (most recent call last):
File "/usr/lib/python2.5/threading.py", line 460, in __bootstrap
self.run()
File "/usr/lib/python2.5/threading.py", line 440, in run
self.__target(*self.__args, **self.__kwargs)
File "mirrord_10_write_only.py", line 31, in server
fconn.write("message %d\n" % i)
File "/usr/lib/python2.5/socket.py", line 262, in write
self.flush()
File "/usr/lib/python2.5/socket.py", line 249, in flush
self._sock.sendall(buffer)
error: (32, 'Broken pipe')
或者当不使用 time.sleep(5) 时,服务器端的出错信息如下:
Waiting for connections ...
Got connection from: ('127.0.0.1', 56912)
Client server: server
Exception in thread server:
Traceback (most recent call last):
File "/usr/lib/python2.5/threading.py", line 460, in __bootstrap
self.run()
File "/usr/lib/python2.5/threading.py", line 440, in run
self.__target(*self.__args, **self.__kwargs)
File "mirrord_10_write_only.py", line 30, in server
fconn.write("message %d\n" % i)
File "/usr/lib/python2.5/socket.py", line 262, in write
self.flush()
File "/usr/lib/python2.5/socket.py", line 249, in flush
self._sock.sendall(buffer)
error: (104, 'Connection reset by peer')
总之,会抛出一个 socket.error 的异常,可以通过捕捉这个异常来退出。

但如果使用一边只读,一边只写的情况,就必须要考虑到 socket 对于缓冲(buffers)的使用及其与阻塞(blocking)之间的关系了!

现在的一个问题就是,到底采用那种方式更好呢?理由何在?

星期一, 八月 06, 2007

python socket file object and shutdown()

今天写 mirrord.py 的服务器端和协议的测试代码,在使用 socket 的文件类对象的时候有问题。工作部分的代码如下:
try:
conn = self.conn
fconn = conn.makefile('w', 0)
data = conn.recv(1024)
if data != "INIT SYNC":
raise ServerExc("Invalid init sync command")
try:
self.wmLock.acquire()
# Copy the content from the manager queue at first:
wdirs = self.manager.wdirs.keys()
files = self.manager.files.keys()
serial = self.manager.serial
# And then register itself to the svPool to inform the scheduler
reserving the manager.wmlog for it:
self.manager.svPool[self] = serial
finally:
self.wmLock.release()
conn.sendall("OK")
data = conn.recv(1024)
if data != "INIT SYNC DIRS":
raise ServerExc("Invalid init sync dirs command")
if debug:
print "server wdirs send", wdirs
# #1:
# fconn = conn.makefile('w', 0)
# fconn.writelines("%s\n" % fn for fn in wdirs)
# fconn.close()
# #2:
fconn.writelines("%s\n" % fn for fn in wdirs)
conn.shutdown(1)
# #3:
# fconn = conn.makefile('w', 0)
# for line in wdirs:
# # conn.sendall("%s\n" % line)
# fconn.write("%s\n" % line)
# # fconn.write("^D")
# fconn.write("")
# fconn.close()
# #4:
# for line in wdirs:
# conn.sendall("%s\n" % line)
测试代码实际上就是一个客户端,代码如下:
try:
conn = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
conn.connect(('localhost', 2123))
fconn = conn.makefile('r', 0)
conn.sendall("START")
data = conn.recv(1024)
conn.sendall("INIT SYNC")
data = conn.recv(1024)
svCurrent = md.Status.svCurrent()
conn.sendall("INIT SYNC DIRS")
# #1:
result_wdirs = [S.strip() for S in fconn.readlines()]
# #2:
# result_wdirs = []
# for line in fconn:
# line = line.strip()
# print line
# result_wdirs.append(line)
# #3:
# data = conn.recv(2048)
# print "DEBUG", data
# #4:
# while 1:
# data = fconn.readline()
# print data
# if not data: break
if mirrord.debug:
print "client wdirs", result_wdirs
expect_wdirs = snap_wdirs
result_wdirs.sort()
expect_wdirs.sort()
self.failUnlessEqual(result_wdirs, expect_wdirs)
conn.sendall("INVALID INIT SYNC FILES CMD")
self.failIf(conn.recv(1024))
self.failUnlessEqual(svCurrent.Status.stage(), mirrord.STAGE_ABORTED)
self.failIf(svCurrent.that.isAlive())
nloop = md.Status.nloop()
while md.Status.nloop() < nloop + 1: time.sleep(1)
self.failIf(md.manager.svPool.has_key(svCurrent))
except socket.error, socex:
self.fail("Communication failed: %s" % socex)
一开始的时候,始终出现一个问题,就是测试代码在运行到上面的片段的时候挂起。按 Ctrl-C 退出,抛出的异常信息如下:
Thread SERVER ABORTED
loop conitnue at 1186393237.81
Thread SERVER ABORTED
loop conitnue at 1186393238.82
loop conitnue at 1186393239.82
server wdirs send ['/root/ulfs/cutils/trunk/testdata/mirrord/tin/tdir1', '/root/ulfs/cutils/trunk/testdata/mirrord/tin']
server wdirs sent ['/root/ulfs/cutils/trunk/testdata/mirrord/tin/tdir1', '/root/ulfs/cutils/trunk/testdata/mirrord/tin']
loop conitnue at 1186393240.83
loop conitnue at 1186393241.83
loop conitnue at 1186393242.84
loop conitnue at 1186393243.84
loop conitnue at 1186393244.85
loop conitnue at 1186393245.85
loop conitnue at 1186393246.85
loop conitnue at 1186393247.86
loop conitnue at 1186393248.86
DELETE FILE: /root/ulfs/cutils/trunk/testdata/mirrord/tin/tfile1
Traceback (most recent call last):
File "mirrord_ut.py", line 1142, in
runner.run(suite)
File "/usr/lib/python2.5/unittest.py", line 705, in run
test(result)
File "/usr/lib/python2.5/unittest.py", line 437, in __call__
return self.run(*args, **kwds)
File "/usr/lib/python2.5/unittest.py", line 433, in run
test(result)
File "/usr/lib/python2.5/unittest.py", line 281, in __call__
return self.run(*args, **kwds)
File "/usr/lib/python2.5/unittest.py", line 260, in run
testMethod()
File "mirrord_ut.py", line 1047, in test_protocol
data = fconn.readline()
File "/usr/lib/python2.5/socket.py", line 331, in readline
data = recv(1)
KeyboardInterrupt
每次都是这样,不论在客户端使用怎样的读取方法,例如在上面的 #1 到 #4 等集中方法。实际上是因为被阻塞在 socket 的 recv(1) 上了。使用客户端代码的 #2,#3 或 #4 会打印出读取的数据,基本上是这样的:
Thread SERVER ABORTED
loop conitnue at 1186393237.81
Thread SERVER ABORTED
loop conitnue at 1186393238.82
loop conitnue at 1186393239.82
server wdirs send ['/root/ulfs/cutils/trunk/testdata/mirrord/tin/tdir1', '/root/ulfs/cutils/trunk/testdata/mirrord/tin']
server wdirs sent ['/root/ulfs/cutils/trunk/testdata/mirrord/tin/tdir1', '/root/ulfs/cutils/trunk/testdata/mirrord/tin']
/root/ulfs/cutils/trunk/testdata/mirrord/tin/tdir1

/root/ulfs/cutils/trunk/testdata/mirrord/tin


loop conitnue at 1186393240.83
loop conitnue at 1186393241.83
loop conitnue at 1186393242.84
loop conitnue at 1186393243.84
loop conitnue at 1186393244.85
loop conitnue at 1186393245.85
loop conitnue at 1186393246.85
loop conitnue at 1186393247.86
loop conitnue at 1186393248.86
DELETE FILE: /root/ulfs/cutils/trunk/testdata/mirrord/tin/tfile1
Traceback (most recent call last):
File "mirrord_ut.py", line 1142, in
runner.run(suite)
File "/usr/lib/python2.5/unittest.py", line 705, in run
test(result)
File "/usr/lib/python2.5/unittest.py", line 437, in __call__
return self.run(*args, **kwds)
File "/usr/lib/python2.5/unittest.py", line 433, in run
test(result)
File "/usr/lib/python2.5/unittest.py", line 281, in __call__
return self.run(*args, **kwds)
File "/usr/lib/python2.5/unittest.py", line 260, in run
testMethod()
File "mirrord_ut.py", line 1047, in test_protocol
data = fconn.readline()
File "/usr/lib/python2.5/socket.py", line 331, in readline
data = recv(1)
KeyboardInterrupt
进一步验证了被阻塞在了发送的数据结束的地方!

那么是不是和 EOF 有关了,是不是因为没有发送文件结束标志呢?但问题是我不知道如何向一个文件对象写入 EOF,尝试了 "^D",也尝试了空字符串,都没有用;后来我想是不是必须关闭文件对象才会写一个 EOF,但在上面的尝试中也不行;使用 conn.sendall() 也不行,仍然被阻塞,除非客户端使用 conn.recv(),但读到的数据不完整(参看上面服务器端工作代码 #1, #3 和 #4):
loop conitnue at 1186395856.35
Thread SERVER ABORTED
Thread SERVER ABORTED
loop conitnue at 1186395857.36
server wdirs send ['/root/ulfs/cutils/trunk/testdata/mirrord/tin/tdir1', '/root/ulfs/cutils/trunk/testdata/mirrord/tin']
DEBUG /root/ulfs/cutils/trunk/testdata/mirrord/tin/tdir1

server wdirs sent ['/root/ulfs/cutils/trunk/testdata/mirrord/tin/tdir1', '/root/ulfs/cutils/trunk/testdata/mirrord/tin']
...
最终,在服务器端使用了 #2 处的代码,调用一个 conn.shutdown(1),这样客户端才终于不再阻塞!

这是从《Python 网络编程基础》上来的:
对于很多操作系统来说,有时候在网络上发送的数据的调用会在远程服务器确保已经收到信息之前返回,因此,很有可能一个来自对 sendall() 成功调用返回的数据,实际上永远都没有被收到。

一旦结束写操作,应该立刻调用 shutdown() 函数,这样会强制清除缓存里面的内容,同时如果有任何问题就会产生一个异常。

...使用 shutdown() 确保服务器完全收到请求...

...请牢记,数据只有在您调用了 shudown() 函数之后才能确保被发送...


但是从 Python lib 里面的内容来看,似乎又不是这个意思,似乎后续写操作都会受影响(上面调用的是 shutdown(1))?是不是这样呢?还得再写程序再看。

实际上,使用 shutdown(1) 会导致后续的写操作都无效,当然 shutdown(1) 会在当前写缓冲区里面的数据都成功写入后才返回。在《Python 网络编程基础》中讲到的半开发 socket 就使用这种方法,当在 fork 或多线程时,使用这种方法防止多个进程/线程之间的数据读写紊乱很有用,但在这里,如果 shutdown(1) 之后不能再写数据,后面的操作全都不能进行了,这显然不是我想要的。

验证一下。服务器端的代码如下:
# #1:
fconn.writelines("%s\n" % fn for fn in wdirs)
fconn.write("EOF\n")
# fconn.flush()
conn.shutdown(1)
fconn.write("EOF...\n")
客户端代码:
while 1:
data = fconn.readline()[:-1]
if not data: break
# if data == 'EOF': break
result_wdirs.append(data)
print "client readline:", data
if mirrord.debug:
print "client wdirs", result_wdirs
输出:
 server wdirs send ['/home/roc/ulfs/cutils/trunk/testdata/mirrord/tin', '/home/roc/ulfs/cutils/trunk/testdata/mirrord/tin/tdir1']
client readline: /home/roc/ulfs/cutils/trunk/testdata/mirrord/tin
client readline: /home/roc/ulfs/cutils/trunk/testdata/mirrord/tin/tdir1
client readline: EOF
client wdirs ['/home/roc/ulfs/cutils/trunk/testdata/mirrord/tin', '/home/roc/ulfs/cutils/trunk/testdata/mirrord/tin/tdir1', 'EOF']
F
可以看出,第二个 EOF... 没有被打印出来,因为此时 conn.shutdown(1) 已经执行了,后面再 fconn.write() 已经不起作用──但为什么没有抛出 socket.error 异常???但后面的
if debug:
print "server wdirs sent", wdirs
倒也确实没有执行!似乎也被阻塞了!!!即便在客户端再加上读取的语句:
result_wdirs = []
while 1:
data = fconn.readline()[:-1]
# if not data: break
if data == 'EOF': break
result_wdirs.append(data)
print "client readline:", data
data = conn.recv(1024)
print data
也一样,data 没有被读取,最终的输出结果为:
loop conitnue at 1186411462.36
Thread SERVER ABORTED
Thread SERVER ABORTED
loop conitnue at 1186411463.41
server wdirs send ['/home/roc/ulfs/cutils/trunk/testdata/mirrord/tin', '/home/roc/ulfs/cutils/trunk/testdata/mirrord/tin/tdir1']
client readline: /home/roc/ulfs/cutils/trunk/testdata/mirrord/tin
client readline: /home/roc/ulfs/cutils/trunk/testdata/mirrord/tin/tdir1
after shutdown(1)
client wdirs ['/home/roc/ulfs/cutils/trunk/testdata/mirrord/tin', '/home/roc/ulfs/cutils/trunk/testdata/mirrord/tin/tdir1']
F
======================================================================
FAIL: testing server and the protocol for communication ...
----------------------------------------------------------------------
Traceback (most recent call last):
File "mirrord_ut.py", line 1069, in test_protocol
self.failUnlessEqual(svCurrent.Status.stage(), mirrord.STAGE_ABORTED)
AssertionError: 1 != 127

----------------------------------------------------------------------
Ran 1 test in 2.241s

FAILED (failures=1)
stage == 1 说明在运行状态,只可能是被阻塞。

但究竟为什么被阻塞而不是抛出 socket.error 的异常呢?

先撇开这个问题,分析一下客户端 readline() 被阻塞的原因。对于循环调用 readline(readlines 就是循环调用 readline),通过分析 socket.py 可以看到,它调用 recv(1),除非得到 "\n" 或 EOF,否则不会终止,但对于 socket 来说,实际上除非关闭这个 socket,才会返回一个 EOF,而因为下面还有数据要读写,所以这里不可能关闭 conn,因此客户端的 readline/readlines 以为下面还有数据要读取,故阻塞等待。

调用 shutdown(1) 从上面的分析来看也不可行。那么只能通过对协议的设计来解决这个问题!服务器端可以在 writelines 后再追加一行 “EOF\n"(注意这里是字符串”EOF“而不是前面的 EOF 标识),客户端在读取到 EOF 字符串后知道传输已经结束了,因此退出循环。这里不存在有名位 EOF 的正常串的问题,因为传送的都是绝对路径的文件路径名,因此必然是以”/“开头的,因此决不会有冲突!

注意,如果在前面在 shutdown(1) 之前忘记增加 fconn.write("EOF\n") 这一行,将导致这样的输出结果:
......
client readline:
client readline:
client readline:
client readline:
client readline:
client readline:
client readline:
client readline:
client readline:
client readline:
client readline:
client readline:
client r
Traceback (most recent call last):
File "mirrord_ut.py", line 1155, in ?
runner.run(suite)
File "/usr/lib/python2.4/unittest.py", line 696, in run
test(result)
File "/usr/lib/python2.4/unittest.py", line 428, in __call__
return self.run(*args, **kwds)
File "/usr/lib/python2.4/unittest.py", line 424, in run
test(result)
File "/usr/lib/python2.4/unittest.py", line 281, in __call__
return self.run(*args, **kwds)
File "/usr/lib/python2.4/unittest.py", line 260, in run
testMethod()
File "mirrord_ut.py", line 1058, in test_protocol
print "client readline:", data
KeyboardInterrupt
除非使用的是 if not data: break 作为判断条件,而不是 if data == 'EOF': break。

这意味着在 socket 的使用中,fconn.readlines() 和 for line in fconn 这样的语法都要注意,一般情况下只能用:
while 1:
line = fconn.readline()[:-1]
if line == 'EOF': break
...
这样的方法!

星期三, 八月 01, 2007

python thread setDaemon

运行一个单元测试程序,结果抛出异常:
roc@crablfs:~/ulfs/cutils/trunk/lib$ python mirrord_ut.py

testing boot init ...
.
testing monitoring ...
Add a dir to watch: /home/roc/ulfs/cutils/trunk/testdata/mirrord/tin
Add a file to record: /home/roc/ulfs/cutils/trunk/testdata/mirrord/tin/tfile1
Add a dir to watch: /home/roc/ulfs/cutils/trunk/testdata/mirrord/tin/tdir1
Add a file to record: /home/roc/ulfs/cutils/trunk/testdata/mirrord/tin/tdir1/tfile2
boot init finished
run schedule finished
loop conitnue at 1185979072.2
Staring socket failed: ('', 2123), (98, 'Address already in use')
Exception in thread schedule:
Traceback (most recent call last):
File "/usr/lib/python2.4/threading.py", line 442, in __bootstrap
self.run()
File "/usr/lib/python2.4/threading.py", line 422, in run
self.__target(*self.__args, **self.__kwargs)
File "/home/roc/ulfs/cutils/trunk/lib/mirrord.py", line 426, in _run_schedule
raise MirrordExc(strerr)
MirrordExc:

......

F
======================================================================
FAIL: test_Monitor (__main__.TestMirrord)
----------------------------------------------------------------------
Traceback (most recent call last):
File "mirrord_ut.py", line 181, in test_Monitor
self.failUnless(md.manager.wdirs.has_key(wd))
AssertionError

----------------------------------------------------------------------
Ran 2 tests in 72.394s

FAILED (failures=1)
测试代码是通过产生一个线程去运行工作代码的,这样可以同步监控工作代码中的变量。测试代码是这样的:
    def test_Monitor(self):
print "\ntesting monitoring ..."
try:
mirrord.testing = 1
testdir = self.testdir

# self.setUp()
failed = self.__build_2()
self.failIf(failed)
md = mirrord.Mirrord(identities=['test2'], datadir="/tmp/mirrord")
# Invalid Server should not can boot the run()
self.failUnlessRaises(mirrord.MirrordExc, md.run, address=('localhost', 2123), Server=1)
# The infinite loop should be terminated correctly when request:
mdt = threading.Thread(target=md.run, name="mirrord", args=(('', 2123),))
mdt.setDaemon(1)
mdt.start()
while md.Status.stage() < 2: time.sleep(1)
md.stop()
nloop = md.Status.nloop()
while md.Status.nloop() == nloop: time.sleep(1)
self.failUnlessEqual(md.Terminate, True)
self.failIf(mdt.isAlive())
self.tearDown()

mirrord.debug = 1
# Test inotify functionality:
self.setUp()
self.__build_1()
snap_wdirs = [testdir, "%s/tin/" % testdir, "%s/tin/tdir1" % testdir]
snap_files = ["%s/tin/tfile1" % testdir, "%s/tin/tdir1/tfile2" % testdir]
md = mirrord.Mirrord(identities=['test1'], datadir="/tmp/mirrord")
mdt = threading.Thread(target=md.run, name="mirrord", args=(('', 2123),))
mdt.setDaemon(1)
mdt.start()
while md.Status.stage() < 2: time.sleep(1)
在测试中,前后两次运行了 md.run,而 md.run(Mirrord.run) 会再产生一个线程调用 socket 绑定到 2123 号端口并监听请求,这里报出'Address already in use'的错误,说明前一个调用产生的 socket 线程没有退出,但是 self.failIf(mdt.isAlive()) 的测试已经通过,也就是说 md.run 这个线程已经退出,为什么 socket 线程仍然在运行呢?

这是否和线程调度中的 setDaemon() 有关呢?在工作代码中原来是这样写的:
def _schedule_thread(self, Server, address):
that = threading.Thread(
target=self._run_schedule, name='schedule', args=(Server, address))
that.setDaemon(1)
that.start()
# that.join()

class Mirrord:
...
def _run_schedule(self, Server, address):
"""
This method runs in the child process/thread
to bind to a port for remote clients' requests
"""
try:
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
sock.bind(address)
sock.listen(5)
except socket.error, socex:
strerr = _("Staring socket failed: %s, %s" % (address, socex))
syslog.syslog(syslog.LOG_ERR, strerr)
if debug: print >> sys.stderr, strerr
raise MirrordExc(strerr)

while not self.Terminate:
try:
conn, addr = sock.accept()
# TODO: Add authentication code here:
output = _("Got connection from:", addr)
syslog.syslog(syslog.LOG_INFO, output)
serv = Server(self.manager, self.wmLock, conn, addr)
serv.start()
# After the sync, create a child process or thread to serve the client
except socket.error, socex:
strerr = _("Get connections error: %s" % socex)
syslog.syslog(syslog.LOG_ERR, strerr)
except:
strerr = traceback.format_exc()
syslog.syslog(syslog.LOG_ERR, strerr)

def run(self, address, Server=ServerThread):
...
self._schedule(Server, address)
...

while not self.Terminate:
try:
if debug:
output = _("loop conitnue at %s" % time.time())
print output
syslog.syslog(syslog.LOG_DEBUG, output)
if self.notifier.check_events():
self.notifier.read_events()
try:
self.wmLock.acquire()
self.notifier.process_events()
# Process the dirs/files moved to the to location unmonitored:
# the question is: will IN_MOVED_FROM and IN_MOVED_TO occur in one polling?
self.processor.clean_cookies()
# TODO: Clean up the overdue manager.wmlog here
temp = self.manager.progress.values()
if temp: temp.sort(); min = temp[0]
else: min = self.manager.serial
for sn in self.manager.wmlog.keys():
if sn < min: self.manager.wmlog.pop(sn)
finally:
self.wmLock.release()
# self.Status.nloop += 1
self.Status.nloop = self.Status.nloop() + 1
except:
strerr = traceback.format_exc()
syslog.syslog(syslog.LOG_ERR, strerr)
if debug: print strerr
break
self.notifier.stop()

def stop(self):
self.Terminate = True
# Or threading Event()?
如果按照字面意思来理解,则如果将子线程设置为 Daemonic,那么在产生它的主线程退出后,它仍将继续运行,虽然在这里设置了 self.Terminate,但因为在 conn, addr = sock.accept() 的时候,线程被阻塞了,而测试代码中并没有产生连接请求,所以该子线程无法检测到 self.Terminate 的变化。

但实际上,即使使用 setDaemon(),除非主线程(即整个进程)退出──这里就是测试代码,停止 md.run 也不会导致 schedule 线程退出。如果设置成 daemonic,则当 main thread 退出的时候,schedule thread 反而会继续运行,程序将无法返回到命令提示符!

关于这一点,参见如下的一个原型测试代码:
#!/usr/bin/env python
# -*- encoding: utf-8 -*-

"""
Python treading daemonic:
If the second thread exits, the third thread will not exits,
no matter whether setDaemon(),
Only when the main thread(process) exists, the third one exists!
Author: Roc Zhou
Date: 2007-08-02
Email: chowroc.z@gmail.com
"""

import time
import threading

def run_thread():
while 1:
print "child thread is running ..."
time.sleep(1)

def run_schedule():
this = threading.Thread(target=run_thread, name="thread")
# this.setDaemon(1)
this.start()
print "schedule exiting ..."

that = threading.Thread(target=run_schedule, name="schedule")
# that.setDaemon(1)
that.start()
for i in range(10):
print "main thread is running ..."
time.sleep(1)
print "main thread exiting ..."
按几种不同的 setDaemon() 情况分别运行改程序:

星期二, 七月 31, 2007

python assemble methods at runtime?

在以前曾经讨论过一个例子:python unzip,当时在外部定义了一个函数,然后直接利用 setattr 将其加入到 ZipFile 类对象中,如下:
>>> class test:
... pass
...
>>> def func(self):
... print self.__dict__
...
>>> test.func = func
>>> t = test()
>>> t.func()
{}
>>>
但是,如果在类定义中直接使用,则不行:
>>> def func(self):
... print self.__dict__
...
>>> class test:
... def __init__(self):
... self.func = func
...
>>> t = test()
>>> t.func()
Traceback (most recent call last):
File "", line 1, in
TypeError: func() takes exactly 1 argument (0 given)
使用 setattr 也不行:
>>> def func(self):
... print self.__dict__
...
>>> class test:
... def __init__(self):
... setattr(self, 'func', func)
...
>>> t = test()
>>> t.func()
Traceback (most recent call last):
File "", line 1, in
TypeError: func() takes exactly 1 argument (0 given)
但是,是不是我放在外面定义就没有一点问题了呢?在 mirrord.py 中,我就希望在运行时再装配,因为直接在 __init__ 中定义不行,所以我这样做:
def _schedule_thread(self, Server, address):
that = threading.Thread(
target=self.__run_schedule, name='schedule', args=(Server, address))
that.setDaemon(1)
that.start()
that.join()

def _schedule_process(self, Server, address):
"""
There is a sharing problem has not been solved:
manager(WatchManager) should be shared among processes
"""
pid = os.fork()
if pid == 0: self.__run_schedule(Server, address)

class Mirrord:
...
def __run_schedule(self, Server, address):
...
Mirrord._schedule = _schedule_thread
结果在测试的时候报错:
Exception in thread mirrord:
Traceback (most recent call last):
File "/usr/lib/python2.5/threading.py", line 460, in __bootstrap
self.run()
File "/usr/lib/python2.5/threading.py", line 440, in run
self.__target(*self.__args, **self.__kwargs)
File "mirrord_ut.py", line 56, in __run_thread
self.mrd.run(('localhost', 2123))
File "/root/ulfs/cutils/trunk/lib/mirrord.py", line 497, in run
self._schedule(Server, address)
File "/root/ulfs/cutils/trunk/lib/mirrord.py", line 380, in _schedule_thread
target=self.__run_schedule, name='schedule', args=(Server, address))
AttributeError: Mirrord instance has no attribute '__run_schedule'
,而如果将那两个函数改为:
def _schedule_thread(self, Server, address):
that = threading.Thread(
target=self._Mirrord__run_schedule, name='schedule', args=(Server, address))
that.setDaemon(1)
that.start()
that.join()

def _schedule_process(self, Server, address):
"""
There is a sharing problem has not been solved:
manager(WatchManager) should be shared among processes
"""
pid = os.fork()
if pid == 0: self._Mirrord__run_schedule(Server, address)
则可以通过。但显然使用 _Mirrord__run_schedule 这种方式看上去不是太好!

星期五, 六月 22, 2007

python Tree root.[inexistent].branch ?

如果
root = Tree(value)
root.trunk.branch = value1
而 trunk 不存在,能否自动创建这个 Tree Node Container?

是应该利用 root 的 __getattr__ 还是应该利用 __setattr__ ?因为这时候 trunk 根本就不存在,这时也就根本无从利用其 __setattr__,而对于 root,可以肯定的是在进行 root.trunk.branch = value1 的操作时,肯定是 __getattr__ 被调用!可以看下面的例子:
>>> class test:
... def __init__(self):
... self.x = 1
... def __getattr__(self, attr_name):
... try:
... return self.__dict__[attr_name]
... except KeyError:
... self.__dict__[attr_name] = 'inexistent'
... return self.__dict__[attr_name]
...
>>> t = test()
>>> t.x
1
>>> t.y
'inexistent'
>>> t.x.y = 2
Traceback (most recent call last):
File "", line 1, in ?
AttributeError: 'int' object has no attribute 'y'
>>> t.z.x = 2
Traceback (most recent call last):
File "", line 1, in ?
AttributeError: 'str' object has no attribute 'x'

>>> print t
Traceback (most recent call last):
File "", line 1, in ?
TypeError: 'str' object is not callable
这表明 __repr__ 已经受到了影响,那么原因何在呢?

先来看下面这个例子:
>>> class test:
... def __init__(self):
... self.x = 1
... def __getattr__(self, attr_name):
... print attr_name
... if attr_name == 'y':
... return 2
... else:
... raise AttributeError, attr_name
...
>>> t = test()
>>> t.x
1
>>> t.y
y
2
>>> print t.x
1
>>> print t
__str__
__repr__
<__main__.test>
首先可以看到,在前面的例子中 return self.__dict__[attr_name] 其实不是必须的,因为 python 自己会为我们做这些,并且做的更好,因为它会检查继承树。实际上,只有当一个 attribute 在其继承树中都找不到的时候,__getattr__ 才会被调用。

从 print t 的输出可以看出,self.__str__ 和 self.__repr__ 这两个方法实际上也是通过 __getattr__ 来寻找的,在前面的例子中,没有重载 __str__ 和 __repr__,而是对它们进行了赋值操作,将字符串 'inexistent' 赋值给了它们,当然会导致它们"not callable"。

那么,为了实现上面的 Tree 操作,并且不影响 print 操作,编码如下:
def __repr__(self):
return "" % hex(id(self))
# for k, v in self.__traverse__(): print '%s = %s;' % (k, v)
def __str__(self):
return self.__repr__()
def __getattr__(self, attr_name):
setattr(self, attr_name, Tree(None))
return self.__dict__[attr_name]
def __setattr__(self, attr_name, value):
if attr_name in self.__used_names:
raise TreeExc(_("Attribute name '%s' is reserved" % attr_name))
try:
# If self.attribute exists
existed = self.__dict__[attr_name]
if isinstance(value, Tree):
subtree = value
self.__dict__[attr_name] = subtree
# Replace the node directly
else:
# self.__dict__[attr_name]._Tree__node_value = value
# This will lead to raise TreeExc at #1, because the setattr operation of
# "self.__dict__[attr_name].attribute = value" has been affected by self.__setattr__()
subtree = existed
subtree.__dict__['_Tree__node_value'] = value
# Only replace the node value
except KeyError:
# if self.attribute does not exists, assign it a EMPTY node
if isinstance(value, Tree):
subtree = value
self.__dict__[attr_name] = subtree
else:
self.__dict__[attr_name] = Tree(value)
为了和内置的 print 显示同样的效果,使用了 return "" % hex(id(self)),这里 id(self) 就是得到内存地址。

但是这里还有一个疑问,在前面那个例子中,因为 __str__ 和 __repr__ 的 attr_name 已经被打印出来,并且它又不是 "y",为什么没有抛出 AttributeError 的异常?

星期三, 六月 20, 2007

python __doc__ and gettext

#!/usr/bin/env python
# -*- encoding: utf-8 -*-

import re
from gettext import gettext as _

#1
__doc__ = _("""
Author: Roc Zhou
Date: 2007-06-20
Email: chowroc.z@gmail.com

A new data structrue: Tree
...""")

class Tree:
#2
__doc__ = _("""This class define a builtins like tree type of data structure,
thus now we can manipulate a tree very conveniently, like this:
head = Tree(value)
head = Tree(value, data=value1, extra=value2)
head.branch = value
...""")
......
def __init__(self, value, **kwargs):
_(""""head = Tree(value)" create a Tree instance as the root node with node 'value'.
...""")
......
def Node1(self, pathseq, value):
#3
_("""Construct a Tree node from a path sequence, this path sequence
only represents a single node, for example:
...""")
这里对 #1, #2 都使用 "__doc__ = " 的操作,而不是默认的直接定义。对于#1,可能是因为前面使用了 from gettext import gettext as _,因为需要国际化支持,所以必须先这么做,但导致 __doc__ 默认定义不在 module 顶部,所以可能需要使用 __doc__ 来指定。

而#2可能是因为 gettext 返回的值使用默认定义方法无法正确传递给 __doc__,因为打印的结果为 None。

但是对于#3,无论是否使用 "__doc__ = " 都无效,只能使用默认定义,不知道是什么原因?

星期日, 三月 18, 2007

google interview: 2nd by phone

问:TCP 三次握手协议是怎样的一个过程?
答:SYN, SYN+ACK, ACK

问:中断连接是发送什么类型的报文?
答:RST

问:traceroute 原理
...(上次问过的)

问:当不能到达目标的时候的情况?

问:有 Server1 和 Server2,之间有一个 firewall,firewall 只运行两主机的 22 端口互相连接到对方,同时只允许从 Server1 的 80 端口的访问,那么如何从 Server2 连接到 firewall 的 80 端口?
答:可以利用 ssh forwarding,使用本地转发从 Server1 的 22 端口转到 firewall 的 80 端口。

问:那么具体是什么命令来实现?
答:
sh# ssh -L 2080:firewall:80 server1
sh# ssh localhost 80


问:请问没有浏览器的时候,如何浏览页面?
答:可以通过 telnet host 80 来访问
(wget -m)

问:常用端口及 /etc/service(上次问过)

问:软、硬链接的区别?
答:符号链接即文件的内容是到目标的路径的字符串,而硬链接是在文件系统底层利用 inode 的指向来实现的。

问:kill 中止进程是使用的什么信号?
答:SIGTERM

问:这和 -9 强制中止有什么区别?
答:强制中止不会做清理工作并返回推出状态。

问:如何查看一个 pid 是属于什么进程的?
答:可以利用 pidof 命令,这个命令是 killall 的一个符号链接。

问:如何利用 kill 来中止所有的名为 javascript 的进程?
答:如果不使用 killall,则可以例如脚本:
for pid in `pidof javascript`; do
kill pid
done

问:mv * 这个命令会产生什么结果

问:/var/www/htdocs 下所有的 html 改诚 htm

find /var/www/htdocs | grep '\.html$' | xargs -i mv {} {}.l

for f in `find /var/www/htdocs | grep '\.html$'`; do
basename
awk
foo1.foo2.html
$1~$(NF-1) => name
mv /var/www/htdocs/$name ...
done

rewrite......

O(n^2)

改进算法

星期日, 二月 11, 2007

python flat file db && table 'type'

我前面在"数据结构与配置设想"中设想配置的三种形式,即树配置、表配置和列表配置。那里相对比较详细的讨论过树配置,现在考虑表配置。

表配置实际上是关系型的,但因为配置的可读性很重要,所以文本形式是首选,特别是对于一些简单的应用,如果弄一个复杂的 Server/Client SQL 关系型数据库就太麻烦了。最起码需要放置在本地文件系统中吧。

仅对后一点,在本地文件系统上操作,是可以利用 SQLite3 来实现的,python 的相应接口为 pysqlite
sh$ upm packs
sqlite-3.3.12
pysqlite-2.3.2

sh$ python
>>> import pysqlite2
>>> dir(pysqlite2)
['__builtins__', '__doc__', '__file__', '__name__', '__path__']
>>> from pysqlite2 import dbapi2 as sqlite
>>> dir(pysqlite2)
['__builtins__', '__doc__', '__file__', '__name__', '__path__', '_sqlite', 'dbapi2']
>>> dbconn = sqlite.connect('/tmp/filedb')
>>> dbcurs = dbconn.cursor()
>>> dbcurs.execute('''create table stocks (date text, trans text, symbol text, qty real, price real)''')
pysqlite2.dbapi2.Cursor object at 0xb7c20ce0
>>> dbcurs.execute("""insert into stocks values ('2006-01-05','BUY','RHAT',100,35.14)""")
pysqlite2.dbapi2.Cursor object at 0xb7c20ce0
>>> dbcurs.close()
>>> dbconn.close()

星期日, 一月 21, 2007

一个关于网络基本原理的问题

另外一个问题是,接到同一个物理设备如交换机的两台主机使用不同的 IP 网段,能否 ping 通?

想一想 IP 的基本原理,想想主机是怎么计算路由的。

当系统需要连接到另一台主机时,会首先将目标路由与自己的网络地址进行比较(关于网络地址的计算,参考这里),如果发现不是自己网段的,就会到自己的路由表里寻找路由,一般会是默认网关,

星期五, 一月 12, 2007

recompile kernel remotely

有时侯可能不得不远程编译和升级内核,但问题是,如果在启动管理器的配置中设置了新内核为默认选项,而新内核启动失败,那么通常你就只能跑到机房去实地调试了(我不知道机房的管理人员在帮忙重启机器的时候,除了按一按开关之外还能有什么别的帮助?),那么远程编译还有什么意义?

如果本地有和远端一样的主机,那么不妨先在本地进行编译和配置,确保可以启动之后再将内核拷贝到远端或直接在远端编译。在拷贝时注意确保3样东西:
1. kernel
2. Red Hat Linux(如果是)或自己配置的 initrd
3. 所有的内核模块:/lib/modules/$kernel-version

之后最好再运行一下 /sbin/depmod -ae -F System.map-$kernel-version $kernel-version 以确保内核模块都没有问题。

拷贝之后再更改 /boot/grub/grub.conf,添加新的条目。

对于远程的重启操作,关键是 grub 的 grub-set-default 程序和 savedefault & fallback 选项!但目前都不行,不知道为什么?

星期一, 十二月 25, 2006

reserve proxy

主要考察方面:
1. 缓存基本原理

2. 哪些内容被缓存:image, html & text, dynamic scripts 执行结果
哪些内容应该被缓存?

问题:为什么静态内容(image & text)缓存可以提高性能?
(1) 变磁盘的文件操作为内存操作
tmpfs -> /dev/shm 的使用
squid:
cache_dir ufs /dev/shm/tmp 1024 16 256
这里使用 /dev/shm/tmp 与使用普通目录的具体区别是什么?能提高性能吗?为什么?

(2) cache 服务器分担 web 服务器的部分负载

3. 缓存服务器与 web 服务器之间的负载平衡
资源分配和预留?PAM_limits?

4. 引申问题:性能监控和测试方案

5. 可缓存性:
那些数据不会被缓存:
SSL, Cookies, POST 请求, authenticateion(应该是指 Apache 的 auth 设定), user-specific information in URL, 每次都会计算出不同结果的动态页面。

是不缓存还是缓存不友好?

6. 其他问题:
(1) 手工强制刷新

(2) 统计问题

星期六, 十二月 09, 2006

ALSA

...(未完)...

在安装了 alsa-lib 和 alsa-utils 以及一些 multimedia lib 之后,LFS 仍然没有声音。使用 mplayer 等会提示没有 /dev/dsp 等设备。于原来的主系统进行比较(Fedora Core 6,当然原来的主系统也没有声音),可以看到原来的系统是有 /dev/dsp 和 /dev/audio 的。

查看主系统上的 lspci 输出:
# sed '/^\t/d;/^$/d' lspci.log
00:00.0 Host bridge: Intel Corporation Mobile 915GM/PM/GMS/910GML Express Processor to DRAM Controller (rev 04)
00:02.0 VGA compatible controller: Intel Corporation Mobile 915GM/GMS/910GML Express Graphics Controller (rev 04) (prog-if 00 [VGA])
00:02.1 Display controller: Intel Corporation Mobile 915GM/GMS/910GML Express Graphics Controller (rev 04)
00:1b.0 Audio device: Intel Corporation 82801FB/FBM/FR/FW/FRW (ICH6 Family) High Definition Audio Controller (rev 04)
00:1c.0 PCI bridge: Intel Corporation 82801FB/FBM/FR/FW/FRW (ICH6 Family) PCI Express Port 1 (rev 04) (prog-if 00 [Normal decode])
00:1d.0 USB Controller: Intel Corporation 82801FB/FBM/FR/FW/FRW (ICH6 Family) USB UHCI #1 (rev 04) (prog-if 00 [UHCI])
00:1d.1 USB Controller: Intel Corporation 82801FB/FBM/FR/FW/FRW (ICH6 Family) USB UHCI #2 (rev 04) (prog-if 00 [UHCI])
00:1d.2 USB Controller: Intel Corporation 82801FB/FBM/FR/FW/FRW (ICH6 Family) USB UHCI #3 (rev 04) (prog-if 00 [UHCI])
00:1d.3 USB Controller: Intel Corporation 82801FB/FBM/FR/FW/FRW (ICH6 Family) USB UHCI #4 (rev 04) (prog-if 00 [UHCI])
00:1d.7 USB Controller: Intel Corporation 82801FB/FBM/FR/FW/FRW (ICH6 Family) USB2 EHCI Controller (rev 04) (prog-if 20 [EHCI])
00:1e.0 PCI bridge: Intel Corporation 82801 Mobile PCI Bridge (rev d4) (prog-if 01 [Subtractive decode])
00:1f.0 ISA bridge: Intel Corporation 82801FBM (ICH6M) LPC Interface Bridge (rev 04)
00:1f.2 IDE interface: Intel Corporation 82801FBM (ICH6M) SATA Controller (rev 04) (prog-if 80 [Master])
00:1f.3 SMBus: Intel Corporation 82801FB/FBM/FR/FW/FRW (ICH6 Family) SMBus Controller (rev 04)
01:0c.0 Ethernet controller: Realtek Semiconductor Co., Ltd. RTL-8139/8139C/8139C+ (rev 10)


当前系统的 lspci 输出:
# lspci
00:00.0 Host bridge: Intel Corp.: Unknown device 2590 (rev 04)
00:02.0 VGA compatible controller: Intel Corp.: Unknown device 2592 (rev 04)
00:02.1 Display controller: Intel Corp.: Unknown device 2792 (rev 04)
00:1b.0 Class 0403: Intel Corp.: Unknown device 2668 (rev 04)
00:1c.0 PCI bridge: Intel Corp.: Unknown device 2660 (rev 04)
00:1d.0 USB Controller: Intel Corp.: Unknown device 2658 (rev 04)
00:1d.1 USB Controller: Intel Corp.: Unknown device 2659 (rev 04)
00:1d.2 USB Controller: Intel Corp.: Unknown device 265a (rev 04)
00:1d.3 USB Controller: Intel Corp.: Unknown device 265b (rev 04)
00:1d.7 USB Controller: Intel Corp.: Unknown device 265c (rev 04)
00:1e.0 PCI bridge: Intel Corp. 82801BAM/CAM PCI Bridge (rev d4)
00:1f.0 ISA bridge: Intel Corp.: Unknown device 2641 (rev 04)
00:1f.2 IDE interface: Intel Corp.: Unknown device 2653 (rev 04)
00:1f.3 SMBus: Intel Corp.: Unknown device 266a (rev 04)
01:0c.0 Ethernet controller: Realtek Semiconductor Co., Ltd. RTL-8139/8139C/8139C+ (rev 10)

我开始怀疑是不是 udev 的规则写得不对。差不多把 udev 的资料都翻了一遍,udev 是差不多了解个大概了,声卡的问题还没有解决。考虑一下 sysfs 的知识,查看一下 /sys 的内容,因为硬件设备应该会把自己的信息导出到 /sys 下。

主系统:
# find /sys | grep 'dsp'
/sys/class/sound/dsp
/sys/class/sound/dsp/device
/sys/class/sound/dsp/dev
/sys/class/sound/dsp/uevent
/sys/class/sound/dsp/subsystem
/sys/class/sound/adsp
/sys/class/sound/adsp/device
/sys/class/sound/adsp/dev
/sys/class/sound/adsp/uevent
/sys/class/sound/adsp/subsystem
/sys/devices/pci0000:00/0000:00:1b.0/sound:dsp
/sys/devices/pci0000:00/0000:00:1b.0/sound:adsp

当前系统:
# find /sys | grep 'dsp'
没有输出。

另外,运行:
# alsaconf
which: no dialog in (/usr/local/sbin:/usr/local/bin:/bin:/usr/bin:/sbin:/usr/sbin:/usr/X11R6/bin)
which: no whiptail in (/usr/local/sbin:/usr/local/bin:/bin:/usr/bin:/sbin:/usr/sbin:/usr/X11R6/bin)
Error, dialog or whiptail not found.
alsaconf "Error, dialog or whiptail not found"
每次我运行了 alsaconf 后,所有的模块都不在了,必须重新加载。

但是查看 /proc/asound/cards:
# cat /proc/asound/cards
 0 [Intel          ]: HDA-Intel - HDA Intel
HDA Intel at 0xffe38000 irq 169
而且还有:
# modinfo soundcore
filename:       /lib/modules/2.6.18.1/kernel/sound/soundcore.ko
alias: char-major-14-*
license: GPL
author: Alan Cox
description: Core sound module
srcversion: 69B73D502EF87CE567454E1
depends:
vermagic: 2.6.18.1 SMP mod_unload PENTIUMM REGPARM 4KSTACKS gcc-4.0
这说明声卡驱动是有的,声卡应该已经被识别了呀。

google "No /dev/dsp",可以找到一些资料,说明是 OSS(Open Sound System) compatible 的问题,很多程序都需要使用 oss 的接口。于是安装 alsa-oss,但重启后仍然没有 /dev/dsp 和 /dev/audio。这是因为没有加载相应的 oss 内核模块:
# modprobe snd-mixer-oss
# modprobe snd-pcm-oss
# modprobe snd-seq-oss
不用重启,即可以看到 /dev/dsp 和 /dev/audio 了。

# find /sys | grep 'dsp'
/sys/module/snd_pcm_oss/parameters/dsp_map
/sys/module/snd_pcm_oss/parameters/adsp_map
/sys/class/sound/dsp
/sys/class/sound/dsp/device
/sys/class/sound/dsp/dev
/sys/class/sound/dsp/uevent
/sys/class/sound/dsp/subsystem
/sys/class/sound/adsp
/sys/class/sound/adsp/device
/sys/class/sound/adsp/dev
/sys/class/sound/adsp/uevent
/sys/class/sound/adsp/subsystem
/sys/devices/pci0000:00/0000:00:1b.0/sound:dsp
/sys/devices/pci0000:00/0000:00:1b.0/sound:adsp

http://lists.ibiblio.org/pipermail/sm-discuss/2004-May/006802.html
http://www.mail-archive.com/alsa-user@lists.sourceforge.net/msg17891.html
http://www.linuxsir.org/bbs/showthread.php?t=282216
http://www.xxlinux.com/linux/article/accidence/internet/20061122/5945.html
Alsa-sound-mini-HOWTO

/dev/mixer:访问声卡中内置的mixer,调整音量大小,选择音源。
/dev/dsp, /dev/dspW, /dev/audio:读这个设备就相当于录音,写这个设备就相当于放音。/dev/dsp 与 /dev/audio 之间的区别在于采样的编码不同,/dev/audio 使用μ律编码,/dev/dsp 使用8-bit(无符号)线性编码,/dev/dspW 使用16-bit(有符号)线形编码。/dev/audio 主要是为了与 SunOS 兼容,所以尽量不要使用。


对于非 OSS compatible,其设备为 /dev/snd/*。

但是仍然不能发声!但内核已经编译了 ALSA,从前面的 /proc/asound/cards 也可以看到驱动应该已经识别呀?

我开始考虑是不是驱动的问题。因为前面使用的是系统内建的驱动,这次使用 alsa-driver 看看。因为编译时提示必须使用没有增加 ALSA 支持的内核,所以又重新编译了内核,去除了 ALSA。然后启动到新的内核来编译 alsa-driver -- 这时发生了很奇怪的事 -- 扬声器竟然可以发出"嘟嘟"的蜂鸣声了!

但是当我编译好 alsa-driver,再次重启后,又没有任何声音了!而且似乎每次重启后音量设置就无效了。即使安装了 blfs-bootscripts 的 alsa 脚本!

alsa-driver 的 make install 提示了
cat WARNING

WARNING!!! The mixer channels for the ALSA driver are muted by default!!!
**************************************************************************
You would use some ALSA or OSS mixer to set the appropriate volume.
于是使用 alsamixer/aumix 来调整音量,但毫无用处。

我又参照 ALSA 官方网站的
Intel ICH southbridge HD-audio and modem
一文进行了编译,并设置 /etc/modprobe.conf 如下:
$ cat /etc/modprobe.conf
alias eth0 8139too
alias scsi_hostadapter ata_piix
alias scsi_hostadapter1 ahci

# ALSA portion
alias char-major-116 snd
alias snd-card-0 snd-hda-intel
# module options should go here

# OSS/Free portion
alias char-major-14 soundcore
alias sound-slot-0 snd-card-0

# card #1
alias sound-service-0-0 snd-mixer-oss
alias sound-service-0-1 snd-seq-oss
alias sound-service-0-3 snd-pcm-oss
alias sound-service-0-8 snd-seq-oss
alias sound-service-0-12 snd-pcm-oss

# remove snd-hda-intel { /usr/sbin/alsactl store 0 >/dev/null 2>&1 || : ; }; /sbin/modprobe -r --ignore-remove snd-hda-intel
结果仍然无效。声卡就是不发声!

lspci -v | sed '/^\t/d;/^$/d' && vi && grep

...(未完)...

使用 lspci -v | sed '/^\t/d;/^$/d' 实际得到 lspci 的效果。这样主要是在主系统上运行 lspci -v >lspci.log 后,在当前 LFS 系统中需要得到 lspci 的输出,但当然不想切回原来的主系统,所以用模式匹配文本操作。

也可以用 grep:
$ grep '^\<' lspci.log

但是却不能使用?:
$ grep -v '^\t' lspci.log


另外,sed 默认是全局匹配,而 vi 默认只匹配一个。如何使用非默认操作?