Monday, October 4, 2010

route table

平时在了解系统的route表和配置系统的route表时, 都是比较喜欢使用route这个命令的. 但是, 本人感觉到此命令输出的結果与一些讲解系统网络的资料对不上号的, 例如:

在Routing Decision处就和route命令的输出結果对不上号:
jessinio@jessinio-laptop:~$ route -n
Kernel IP routing table
Destination     Gateway         Genmask         Flags Metric Ref    Use Iface
192.168.0.0     0.0.0.0         255.255.255.0   U     2      0        0 wlan0
169.254.0.0     0.0.0.0         255.255.0.0     U     1000   0        0 wlan0
0.0.0.0         192.168.0.1     0.0.0.0         UG    0      0        0 wlan0

結果很明显, route输出的結果只是系统发向外面的数据包的routing, 没有陈述数据包进入系统的routing.

这种感觉待续了很久一段时间. 今天看到这样一段话:
Linux has a different approach for routing than other UNIX. The way things are implemented on Linux is more flexible and powerful than traditional ways. Legacy utilities such as ifconfig and route are still valid, but incomplete. This is because they do not give access to the advanced routing layer present on Linux. The utility ip (part of iproute2) is the current tool for networking related stuff under Linux. This tool will be the focus of this section.

文字中提到了,route命令得到的其实是不完全的数据. 在Linux下, 最本地化的应该是ip命令.
google找到一份很老的文档: http://linux-ip.net/html/routing-tables.html 上面写得很清楚了, 如下一小段:
The routing table manipulated by the conventional route command is the main routing table. Additionally, the use of both ip address and ifconfig will cause the kernel to alter the local routing table (and usually the main routing table). For further documentation on how to manipulate the other routing tables, see the command description of ip route.
route命令得到和设置的仅仅是冰山一角。

iproute2工具集手册:http://www.policyrouting.org/iproute2.doc.html

Saturday, September 11, 2010

block size

多处都存在block size, 概念同名但是意义不同, 相当迷惑
这哥们就是一个被迷惑的人: http://www.linuxforums.org/forum/misc/5654-linux-disk-block-size-help-please.html

上面的URL列出了如下几种block:
1. Hardware block size, "sector size"
2. Filesystem block size, "block size"
3. Kernel buffer cache block size, "block size"
4. Partition table block size, "cylinder size"

我对fdisk打印的blocks一栏很不解. 需要dig一下.
先来看看fdisk打印出来的block:

jessinio@jessinio-laptop:/ $ sudo fdisk -l
Disk /dev/sda: 250.1 GB, 250059350016 bytes
255 heads, 63 sectors/track, 30401 cylinders
Units = cylinders of 16065 * 512 = 8225280 bytes
Disk identifier: 0x00038329

   Device Boot      Start         End      Blocks   Id  System
/dev/sda1   *           1         100      803218+  83  Linux
/dev/sda2             101       30401   243392782+  8e  Linux LVM

上面的内容和下面的是一致的:
jessinio@jessinio-laptop:/media/82d236f2-3592-4040-801c-3c2049ddfb95$ sudo fdisk -b 512 -l
Warning: the -b (set sector size) option should be used with one specified device
Disk /dev/sda: 250.1 GB, 250059350016 bytes
255 heads, 63 sectors/track, 30401 cylinders
Units = cylinders of 16065 * 512 = 8225280 bytes
Disk identifier: 0x00038329

   Device Boot      Start         End      Blocks   Id  System
/dev/sda1   *           1         100      803218+  83  Linux
/dev/sda2             101       30401   243392782+  8e  Linux LVM

但是下面的就比较奇怪了:
jessinio@jessinio-laptop:/media/82d236f2-3592-4040-801c-3c2049ddfb95$ sudo fdisk -b 1024 -l
Warning: the -b (set sector size) option should be used with one specified device
Disk /dev/sda: 250.1 GB, 250059350016 bytes
255 heads, 63 sectors/track, 15200 cylinders
Units = cylinders of 16065 * 1024 = 16450560 bytes
Disk identifier: 0x00038329

   Device Boot      Start         End      Blocks   Id  System
/dev/sda1   *           1         100     1606437   83  Linux
/dev/sda2             101       30401   486785565   8e  Linux LVM

指定更大的硬件sector size反而block增加, 这是为什么呢?。下面是fdisk的相关代码:
sector_size变量的来源:
 759 static void
 760 get_sectorsize(int fd) {
 761 #if defined(BLKSSZGET)
 762     if (!user_set_sector_size && 
 763         linux_version_code() >= MAKE_VERSION(2,3,3)) {
 764         int arg;
 765         if (ioctl(fd, BLKSSZGET, &arg) == 0)
 766             sector_size = arg;
 767         if (sector_size != DEFAULT_SECTOR_SIZE)
 768             printf(_("Note: sector size is %d (not %d)\n"),
 769                    sector_size, DEFAULT_SECTOR_SIZE);
 770     }
 771 #else
 772     /* maybe the user specified it; and otherwise we still
 773        have the DEFAULT_SECTOR_SIZE default */
 774 #endif
 775 }
DEFAULT_SECTOR_SIZE 在fdisk.h中定义是
#define DEFAULT_SECTOR_SIZE     512

或者是用户指定的user_set_sector_size:sector_size = atoi(optarg);

打印时使用的代码是:
1731             unsigned int psects = get_nr_sects(p);
1732             unsigned int pblocks = psects;
1733             unsigned int podd = 0;
1734
1735             if (sector_size < 1024) {
1736                 pblocks /= (1024 / sector_size);
1737                 podd = psects % (1024 / sector_size);
1738             }
1739             if (sector_size > 1024)
1740                 pblocks *= (sector_size / 1024);
1741                         printf(
1742                 "%s  %c %11lu %11lu %11lu%c  %2x  %s\n",
1743             partname(disk_device, i+1, w+2),
1744 /* boot flag */     !p->boot_ind ? ' ' : p->boot_ind == ACTIVE_FLAG
1745             ? '*' : '?',
1746 /* start */     (unsigned long) cround(get_partition_start(pe)),
1747 /* end */       (unsigned long) cround(get_partition_start(pe) + psects
1748                 - (psects ? 1 : 0)),
1749 /* odd flag on end */   (unsigned long) pblocks, podd ? '+' : ' ',
1750 /* type id */       p->sys_ind,
1751 /* type name */     (type = partition_type(p->sys_ind)) ?
1752             type : _("Unknown"));
1753             check_consistency(p, i);

1. 当sector_size刚好等于1024时, 打印出的正好是sector的数目。也是partition的大小(同size概念)
2. 当sector_size不等于1024时,把sector数目和sector_size结合起来換算成大小(同size概念)

可见, fdisk打印的Blocks一栏其实是partition的大小。下面测试一下:

$ sudo mount /dev/sda1 /media/disk/
$ df /media/disk/
Filesystem           1K-blocks      Used Available Use% Mounted on
/dev/sda1               790556     48176    702220   7% /media/disk
790556 是文件系统总大小。
上面的fdisk -b 1024 -l 命令得到的1606437是sector的数目。这样计算:

1606437 * 512 / 1024 =803218 是约等于790556 的。partition的大小是比file system大是因为file system需要存放一些信息.

总

1. 要想得到一个partition占有用多少个sector的话, 可以使用fdisk -b 1024这种方式得到
2. 不加-b参数的fdisk命令打印的Blocks一栏其实是表示partition的大小(以K为单位)
3. 还没有能力找出Kernel buffer cache block size, "block size" 这一条的实际代码

Wednesday, September 8, 2010

pkg-config

感觉centOS上的fuse版本太低, 还是从源代码安装。

简单编译后:
./configure --prefix=/usr/local/fuse

fuse-python-binding就无法安装。问题是fuse-python-binding的setup.py需要使用pkg-config取得编译参数。

 pkg-config --list-all  |grep fuse
无法找到pkg-config需要的*.pc metainfornation文件。

可以手工增加:

$ PKG_CONFIG_PATH=/usr/local/fuse/lib/pkgconfig/ pkg-config --list-all |grep fuse
fuse                        fuse - Filesystem in Userspace

所以, 下面的方法可以安装:
$ sudo PKG_CONFIG_PATH=/usr/local/fuse/lib/pkgconfig/ python setup.py install

[jessinio@niowork site-packages]$ /usr/local/python2.6/bin/python -c "import fuse"
Traceback (most recent call last):
  File "<string>", line 1, in <module>
  File "fuse.py", line 27, in <module>
    from fuseparts._fuse import main, FuseGetContext, FuseInvalidate
ImportError: libfuse.so.2: cannot open shared object file: No such file or directory


需要增加lib路径:
$ sudo sh -c "echo /usr/local/fuse/lib >> /etc/ld.so.conf.d/fuse.conf "
$ sudo ldconfig

Saturday, September 4, 2010

prefork服务器方式

WEB服务器一般都有两种模式处理动态服务:
1. prefork
2. thread

一种是使用多进程, 另一种是使用多线程.  它们的具体的实现方式自己其实并不清楚.
最近公司的python WEB服务使用较多的内存. 为了清楚其中的问题. 需要学习flup(django需要此库)代码. (  人家写的代码实在好看 )

很多软件都有prefork方式, 下面是prefork模型:

每个nginx子进程都分别调用accept得到用户发来的80端口请求.
之所以每个子进程都可以调用accept得到同一个socket的请求, 是因为fork出的子进程的file descriptor是指向同一个实体.

这样实现了多进程竟争得到socket请求.

每个子进程又可以使用epoll, 线程等方式并发处理众多来自80端口的请求. fast cgi server也是类似的方法.
nginx与fast cgi server之间使用socket通信, 使用fast cgi协议.


每个flup work某一时刻只服务一个页面请求. 完成请求后可以重新接受请求.

使用prefork模型的方式。 所以在PreforkServer类中。

父进程的主体是一个loop:

119         # Main loop.
120         while self._keepGoing:
121             # Maintain minimum number of children.
122             while len(self._children) < self._maxSpare:
123                 if not self._spawnChild(sock): break

可以看出, 父进程是永远期待子进程的数目为一个数值的。这个数值为maxSpare
粗粗看上去, 有些野。但是父进程是有回收子进程的策略的, 如下:

169             # See who and how many children are available.
170             availList = filter(lambda x: x[1]['avail'],
self._children.items())
171             avail = len(availList)

上面的代码可以知道, 父进程会一直收集可用的子进程。 所谓可用是没有在工作
的子进程, 可以从子进程的代码中看出, 如下:
370             # Notify parent we're no longer available.
371             self._notifyParent(parent, '\x00')
372
373             # Do the job.
374             self._jobClass(clientSock, addr, *self._jobArgs).run()

上面的代码是子进程在调用jobClass.run之前, 通知父进程自己是 no longer
available的。


父进程维护着“可用的子进程“数目是为了可以了解负载情况, 判断是否需要产生
更多的子进程, 如下:
172
173             if avail < self._minSpare:
174                 # Need to spawn more children.
175                 while avail < self._minSpare and \
176                       len(self._children) < self._maxChildren:
177                     if not self._spawnChild(sock): break
178                     avail += 1

上面的代码使用了两个数据: minSpare和maxChildren。

minSpare是表示最少“可用子进程“数
maxChildren是表示最大“子进程”数, 是空闲与工作的子进程总数

如果“可用子进程”数还剩一定数据,会被回收, 如下:

179             elif avail > self._maxSpare:
180                 # Too many spares, kill off the extras.
181                 pids = [x[0] for x in availList]
182                 pids.sort()
183                 pids = pids[self._maxSpare:]
184                 for pid in pids:
185                     d = self._children[pid]
186                     d['file'].close()
187                     d['file'] = None
188                     d['avail'] = False

从上面的代码可以看到, fast cgi在启动时的使用到的三个参数的作用应该是:
minspare: 最小“空闲进程数“
maxspare: 常规进程数,即fast cgi启动后, fastcgi进程数是大于等于此值
maxChildren: 最大进程数, 此值主要是用于防止内存被使用完的。

Friday, September 3, 2010

父类方法使用子类的方法与数据

对OO其实是不懂, 只是会一个class( 模式说白了不会. 接口是学了go语言才知道是什么. (-_-)! )

今天在看flup项目的代码时, 有一段发码看不懂. 其实是自己对OO的不懂:
 
  1 #coding:utf-8
  2
  3 class Parent(object):
  4     def __init__(self, name):
  5         self._name = "\t父类: %s" % name
  6     def whoami(self):
  7         print self._name
  8         print "在父类方面中调用子类的方法:"
  9         self._print()
 10         
 11         
 12 class Child(Parent):
 13     def __init__(self, name):
 14         self._nickname = "\t子类:%s" % name
 15         Parent.__init__(self, "大明")
 16         
 17     def _print(self):
 18         print self._nickname
 19         
 20     def run(self):
 21         # 下面的两种调用方法是一样的
 22         print "使用Parent.method(self)的方法调用父类方法"
 23         Parent.whoami(self)
 24         print "使用self.method()的方法调用父类方法"
 25         self.whoami()
 26         
 27         
 28         
 29 if __name__ == "__main__":
 30     c = Child("小明")
 31     c.run()

平时一般都是使用self.whoami(), 自为发父方法无法使用子类的东西. 但是如果使用Parent.whoami(self) 的方式, 就会明白自己的理解其实是错的.

基本问题是自己没有对书中的Parent.__init__(self, "大明") 一句理解到位

嗯.... 自己的水平太差了.

Sunday, August 15, 2010

python2.4与2.5的异常基类变化

工作中, 有一服务需要从freeBSD迁移到centOS上。其中有一python脚本有些鬼异。
在freeBSD上可以正常, 在centOS上不能正常。最后目标定位在脚本最后的代码上, 大概如下:

try:                           
    if 测试条件:           
        do_someting()
        sys.exit(0)       
    else:                     
        sys.exit(1)       
except Exception:  
        sys.exit(1)       

centOS上, python的版本是2.4的。 freeBSD下安装的是2.5.
旧版本的Exception是基类: 因此在2.4上脚本永远是返回1的。
到了新的版本BaseException才是基类: 因此SystemExit和Exception是同一级。脚本可以返回0

这是迁移后的主要问题。 其实, 应该是作者不知道sys.exit的后果。作者把sys.exit当成os._exit了。
如下是APUE里的一幅图:
标准C里的exit函数在调用后, 不是马上退出程序, 而是运行一些收尾工作。比如把buffer里的数据flush到磁盘等等工作。然后才是调用真正的退出函数:_exit

python的sys.exit是抛出一个异常:SystemExit, python会默认会catch这个异常, 大意为:

try:
    我们的python代码
exception SystemExit:
    做一些收尾工作, 如flush之类的



递归, 循环, 迭代

工作中, 需要有历遍目录进行一些操作. python自带的os.walk很强大, 但是没有maxdepth这种层数参数.

os.walk是原本是一个递归. 下面是它的代码:
273     try:
274         # Note that listdir and error are globals in this module due
275         # to earlier import-*.
276         names = listdir(top)
277     except error, err:
278         if onerror is not None:
279             onerror(err)
280         return
281
282     dirs, nondirs = [], []
283     for name in names:
284         if isdir(join(top, name)):
285             dirs.append(name)
286         else:
287             nondirs.append(name)
288
289     if topdown:
290         yield top, dirs, nondirs
291     for name in dirs:
292         path = join(top, name)
293         if followlinks or not islink(path):
294             for x in walk(path, topdown, onerror, followlinks):
295                 yield x
296     if not topdown:
297         yield top, dirs, nondirs

BTW:: 这里需要注意*nux的ENAMETOOLONG 错误.

递归无法按"层"历遍目录. 它只能一条一条路径走尽.

朋友说使用循环也可以实现递归, 这种代码从来没有写过. 也一直在迷惑。
但是受 http://www.devshed.com/c/a/Python/Basic-Threading-in-Python/2/ 的启发.
突然明白循环是怎么做到递归的。 于是自己使用循环自己写了一个历遍的函数:

def loop_walk(top, n):
    stack = Queue.Queue()
    sub_stack = Queue.Queue()
    dirs = []
    files = []
    error = None
    stack.put([top])
    
    while True:
        if stack.empty():
            # stack被取完了, 下一层队列
            stack, sub_stack = sub_stack, stack
            if n <= 1:
                yield [], [], None
                return
            else:
                n -= 1
            continue
        else:
            try:
                top_list = stack.get(False)
            except Queue.Empty:
                yield [], [], None
                return

        for top in top_list:
            try:
                for item in os.listdir(top):
                    item = os.path.join(top, item)
                    if os.path.isdir(item):
                        dirs.append(item)
                    else:
                        files.append(item)
            except:
                # 出错, 比如没有权限
                #traceback.print_exc()
                error = top
            yield dirs, files, error
            if dirs:
                sub_stack.put(dirs)
            dirs = []
            files = []
            error = None

代码长度double了.........