• Python 关键字

    Python 3 的关键字,文档参考2.3.1.Keywords

    False      class      finally    is         return
    None       continue   for        lambda     try
    True       def        from       nonlocal   while
    and        del        global     not        with
    as         elif       if         or         yield
    assert     else       import     pass
    break      except     in         raise
    

    空值
    None 空值是个特殊的值,数字 0 不是空值,这跟 C 语言的 NULL 是不一样的。

    a = None
    

    布尔量
    False #逻辑假
    True #逻辑真

    a = True
    b = False
    

    逻辑运算
    and #逻辑与
    or #逻辑或
    not #逻辑反

    a = 10
    l1 = a > 0 and a < 20  # True
    l2 = a < 0 or a > 20   # False
    l3 = not l1            # False
    

    条件判断语句
    if else elif

    a = 7
    if a > 0 and a < 5 :
        print("A in [0,4]")
    elif a < 10 :
        print("A in [5,9]")
    else:
        print("A in [10,...]")
    

    循环语句及循环控制
    for in #循环迭代
    while
    continue #结束当前循环进入下一次循环
    break #结束整个循环

    ss = ['abc', 'def', 'ghi', 'jkl']
    for tmp in ss:
        print("ss -> " tmp)
    #输出
    ss -> abc
    ss -> def
    ss -> ghi
    ss -> jkl
    #================================================
    n = 5
    while n > 0:
        print(n)
        n -= 1
    #输出
    5
    4
    3
    2
    1
    #================================================
    n = 6
    while n > 0:
        n -= 1
        if n == 3 : continue
        print(n)
    #输出
    5
    4
    2
    1
    0
    #================================================
    n = 6
    while n > 0:
        n -= 1
        if n <= 3 : break
        print(n)
    #输出
    5
    4
    

    函数定义
    def #定义函数
    return #函数返回

    def calcall(*nums):
        c = 0
        for i in nums:
            c += i
        return c
    
    cl = calcall(1, 2, 3, 4, 5)
    print(cl)
    #输出
    15
    

    导入模块
    from import

    import os
    os.getcwd()
    #输出
    '/home/jeff'
    #================================================
    from os import getcwd
    getcwd()
    #输出
    '/home/jeff'
    

    异常处理
    try except finally raise

    def div(a, b):
        a = float(a)    #如果a是个只包含字母的字符串同样会抛出异常
        b = float(b)    #如果b是个只包含字母的字符串同样会抛出异常
        if b == 0:
            raise ValueError("b == 0")  # 抛出异常,相当于 java 的 throw
        return a/b
    
    try:
        print(div(10,3))
        print(div(10,0))
    except ValueError as e:
        print("Error: ", str(e))
    finally:
        print("end")
    
    #输出
    3.3333333333333335
    Error:  b == 0
    end
    

    空语句
    pass
    猜测可能是因为没有{}大括号使用的原因,所以增加了这个关键字吧。
    反正就是以前写C代码时需要大括号的地方都可以加个pass结尾,只是如果是没代码的就一写要加pass

    #定义两个空函数,由于没有{},所以就写成下面哪样吧
    def fun1():
    def fun2():
    #但以上写法是会出错的,正确的写法是
    def fun1():
        pass
    def fun2():
        pass
    def fun3():
        print("fun3")
        pass
    

    类定义
    class

    class MyClass1:
        pass
    class MyClass2(MyClass1):
        def cFun1():
            print("MyClass cFun1")
    class MyClass3(list):
        def __init__(self,att):
            list.__init__([])
            self.att1 = att
    
    a = MyClass1()
    b = MyClass2()
    c = MyClass3("test")
    

    匿名函数
    lambda
    快速生成匿名函数对象,主要用于像map(),reduce()一类以函数对象为参数的函数或类快速传参。

    >>> help(map)
    Help on class map in module builtins:
    
    class map(object)
     |  map(func, *iterables) --> map object
    #以上是map的说明,map需要传递一个函数对象作为参数
    #================================
    #当然你可以这样调用
    def fun1(x):
        return x + x
    map(fun1, [3,4,5,6])
    #也可以这样调用
    map(lambda x: x + x, [3,4,5,6])
    #================================
    #两个参数的写法
    #================================
    >>> help(reduce)
    Help on built-in function reduce in module _functools:
    
    reduce(...)
        reduce(function, sequence[, initial]) -> value
    #以上是 reduce 的说明
    #================================
    #当然你可以这样调用
    from functools import reduce
    def fun1(x, y):
        return x + y
    reduce(fun1, [1,2,3,4])
    #也可以这样调用
    from functools import reduce
    reduce(lambda x, y: x + y, [1,2,3,4])
    #输出
    10
    

    is nonlocal del global with as yield assert in

  • Python 类的特殊变量

    转载自廖雪峰的官方网站

     

    看到类似__slots__这种形如__xxx__的变量或者函数名就要注意,这些在Python中是有特殊用途的。

    __slots__我们已经知道怎么用了,__len__()方法我们也知道是为了能让class作用于len()函数。

    除此之外,Python的class中还有许多这样有特殊用途的函数,可以帮助我们定制类。

    __str__

    我们先定义一个Student类,打印一个实例:

    <code class="python"><span class="prompt">&gt;&gt;&gt; </span><span class="class"><span class="keyword">class</span> <span class="title">Student</span><span class="params">(object)</span>:</span>
    <span class="prompt">... </span>    <span class="function"><span class="keyword">def</span> <span class="title">__init__</span><span class="params">(self, name)</span>:</span>
    <span class="prompt">... </span>        self.name = name
    ...
    <span class="prompt">&gt;&gt;&gt; </span>print(Student(<span class="string">'Michael'</span>))
    &lt;__main__.Student object at <span class="number">0x109afb190</span>&gt;
    </code>

    打印出一堆<__main__.Student object at 0x109afb190>,不好看。

    怎么才能打印得好看呢?只需要定义好__str__()方法,返回一个好看的字符串就可以了:

    <code class="python"><span class="prompt">&gt;&gt;&gt; </span><span class="class"><span class="keyword">class</span> <span class="title">Student</span><span class="params">(object)</span>:</span>
    <span class="prompt">... </span>    <span class="function"><span class="keyword">def</span> <span class="title">__init__</span><span class="params">(self, name)</span>:</span>
    <span class="prompt">... </span>        self.name = name
    <span class="prompt">... </span>    <span class="function"><span class="keyword">def</span> <span class="title">__str__</span><span class="params">(self)</span>:</span>
    <span class="prompt">... </span>        <span class="keyword">return</span> <span class="string">'Student object (name: %s)'</span> % self.name
    ...
    <span class="prompt">&gt;&gt;&gt; </span>print(Student(<span class="string">'Michael'</span>))
    Student object (name: Michael)
    </code>

    这样打印出来的实例,不但好看,而且容易看出实例内部重要的数据。

    但是细心的朋友会发现直接敲变量不用print,打印出来的实例还是不好看:

    <code class="xml">&gt;&gt;&gt; s = Student('Michael')
    &gt;&gt;&gt; s
    <span class="tag">&lt;<span class="title">__main__.Student</span> <span class="attribute">object</span> <span class="attribute">at</span> <span class="attribute">0x109afb310</span>&gt;</span>
    </code>

    这是因为直接显示变量调用的不是__str__(),而是__repr__(),两者的区别是__str__()返回用户看到的字符串,而__repr__()返回程序开发者看到的字符串,也就是说,__repr__()是为调试服务的。

    解决办法是再定义一个__repr__()。但是通常__str__()__repr__()代码都是一样的,所以,有个偷懒的写法:

    <code class="python"><span class="class"><span class="keyword">class</span> <span class="title">Student</span><span class="params">(object)</span>:</span>
        <span class="function"><span class="keyword">def</span> <span class="title">__init__</span><span class="params">(self, name)</span>:</span>
            self.name = name
        <span class="function"><span class="keyword">def</span> <span class="title">__str__</span><span class="params">(self)</span>:</span>
            <span class="keyword">return</span> <span class="string">'Student object (name=%s)'</span> % self.name
        __repr__ = __str__
    </code>

    __iter__

    如果一个类想被用于for ... in循环,类似list或tuple那样,就必须实现一个__iter__()方法,该方法返回一个迭代对象,然后,Python的for循环就会不断调用该迭代对象的__next__()方法拿到循环的下一个值,直到遇到StopIteration错误时退出循环。

    我们以斐波那契数列为例,写一个Fib类,可以作用于for循环:

    <code class="python"><span class="class"><span class="keyword">class</span> <span class="title">Fib</span><span class="params">(object)</span>:</span>
        <span class="function"><span class="keyword">def</span> <span class="title">__init__</span><span class="params">(self)</span>:</span>
            self.a, self.b = <span class="number">0</span>, <span class="number">1</span> <span class="comment"># 初始化两个计数器a,b</span>
    
        <span class="function"><span class="keyword">def</span> <span class="title">__iter__</span><span class="params">(self)</span>:</span>
            <span class="keyword">return</span> self <span class="comment"># 实例本身就是迭代对象,故返回自己</span>
    
        <span class="function"><span class="keyword">def</span> <span class="title">__next__</span><span class="params">(self)</span>:</span>
            self.a, self.b = self.b, self.a + self.b <span class="comment"># 计算下一个值</span>
            <span class="keyword">if</span> self.a &gt; <span class="number">100000</span>: <span class="comment"># 退出循环的条件</span>
                <span class="keyword">raise</span> StopIteration();
            <span class="keyword">return</span> self.a <span class="comment"># 返回下一个值</span>
    </code>

    现在,试试把Fib实例作用于for循环:

    <code class="python"><span class="prompt">&gt;&gt;&gt; </span><span class="keyword">for</span> n <span class="keyword">in</span> Fib():
    <span class="prompt">... </span>    print(n)
    ...
    <span class="number">1</span>
    <span class="number">1</span>
    <span class="number">2</span>
    <span class="number">3</span>
    <span class="number">5</span>
    ...
    <span class="number">46368</span>
    <span class="number">75025</span>
    </code>

    __getitem__

    Fib实例虽然能作用于for循环,看起来和list有点像,但是,把它当成list来使用还是不行,比如,取第5个元素:

    <code class="xml">&gt;&gt;&gt; Fib()[5]
    Traceback (most recent call last):
      File "<span class="tag">&lt;<span class="title">stdin</span>&gt;</span>", line 1, in <span class="tag">&lt;<span class="title">module</span>&gt;</span>
    TypeError: 'Fib' object does not support indexing
    </code>

    要表现得像list那样按照下标取出元素,需要实现__getitem__()方法:

    <code class="python"><span class="class"><span class="keyword">class</span> <span class="title">Fib</span><span class="params">(object)</span>:</span>
        <span class="function"><span class="keyword">def</span> <span class="title">__getitem__</span><span class="params">(self, n)</span>:</span>
            a, b = <span class="number">1</span>, <span class="number">1</span>
            <span class="keyword">for</span> x <span class="keyword">in</span> range(n):
                a, b = b, a + b
            <span class="keyword">return</span> a
    </code>

    现在,就可以按下标访问数列的任意一项了:

    <code class="python"><span class="prompt">&gt;&gt;&gt; </span>f = Fib()
    <span class="prompt">&gt;&gt;&gt; </span>f[<span class="number">0</span>]
    <span class="number">1</span>
    <span class="prompt">&gt;&gt;&gt; </span>f[<span class="number">1</span>]
    <span class="number">1</span>
    <span class="prompt">&gt;&gt;&gt; </span>f[<span class="number">2</span>]
    <span class="number">2</span>
    <span class="prompt">&gt;&gt;&gt; </span>f[<span class="number">3</span>]
    <span class="number">3</span>
    <span class="prompt">&gt;&gt;&gt; </span>f[<span class="number">10</span>]
    <span class="number">89</span>
    <span class="prompt">&gt;&gt;&gt; </span>f[<span class="number">100</span>]
    <span class="number">573147844013817084101</span>
    </code>

    但是list有个神奇的切片方法:

    <code class="css">&gt;&gt;&gt; <span class="tag">list</span>(<span class="tag">range</span>(100))<span class="attr_selector">[5:10]</span>
    <span class="attr_selector">[5, 6, 7, 8, 9]</span>
    </code>

    对于Fib却报错。原因是__getitem__()传入的参数可能是一个int,也可能是一个切片对象slice,所以要做判断:

    <code class="python"><span class="class"><span class="keyword">class</span> <span class="title">Fib</span><span class="params">(object)</span>:</span>
        <span class="function"><span class="keyword">def</span> <span class="title">__getitem__</span><span class="params">(self, n)</span>:</span>
            <span class="keyword">if</span> isinstance(n, int): <span class="comment"># n是索引</span>
                a, b = <span class="number">1</span>, <span class="number">1</span>
                <span class="keyword">for</span> x <span class="keyword">in</span> range(n):
                    a, b = b, a + b
                <span class="keyword">return</span> a
            <span class="keyword">if</span> isinstance(n, slice): <span class="comment"># n是切片</span>
                start = n.start
                stop = n.stop
                <span class="keyword">if</span> start <span class="keyword">is</span> <span class="built_in">None</span>:
                    start = <span class="number">0</span>
                a, b = <span class="number">1</span>, <span class="number">1</span>
                L = []
                <span class="keyword">for</span> x <span class="keyword">in</span> range(stop):
                    <span class="keyword">if</span> x &gt;= start:
                        L.append(a)
                    a, b = b, a + b
                <span class="keyword">return</span> L
    </code>

    现在试试Fib的切片:

    <code class="python"><span class="prompt">&gt;&gt;&gt; </span>f = Fib()
    <span class="prompt">&gt;&gt;&gt; </span>f[<span class="number">0</span>:<span class="number">5</span>]
    [<span class="number">1</span>, <span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>, <span class="number">5</span>]
    <span class="prompt">&gt;&gt;&gt; </span>f[:<span class="number">10</span>]
    [<span class="number">1</span>, <span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>, <span class="number">5</span>, <span class="number">8</span>, <span class="number">13</span>, <span class="number">21</span>, <span class="number">34</span>, <span class="number">55</span>]
    </code>

    但是没有对step参数作处理:

    <code class="css">&gt;&gt;&gt; <span class="tag">f</span><span class="attr_selector">[:10:2]</span>
    <span class="attr_selector">[1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89]</span>
    </code>

    也没有对负数作处理,所以,要正确实现一个__getitem__()还是有很多工作要做的。

    此外,如果把对象看成dict__getitem__()的参数也可能是一个可以作key的object,例如str

    与之对应的是__setitem__()方法,把对象视作list或dict来对集合赋值。最后,还有一个__delitem__()方法,用于删除某个元素。

    总之,通过上面的方法,我们自己定义的类表现得和Python自带的list、tuple、dict没什么区别,这完全归功于动态语言的“鸭子类型”,不需要强制继承某个接口。

    __getattr__

    正常情况下,当我们调用类的方法或属性时,如果不存在,就会报错。比如定义Student类:

    <code class="python"><span class="class"><span class="keyword">class</span> <span class="title">Student</span><span class="params">(object)</span>:</span>
    
        <span class="function"><span class="keyword">def</span> <span class="title">__init__</span><span class="params">(self)</span>:</span>
            self.name = <span class="string">'Michael'</span>
    </code>

    调用name属性,没问题,但是,调用不存在的score属性,就有问题了:

    <code class="python"><span class="prompt">&gt;&gt;&gt; </span>s = Student()
    <span class="prompt">&gt;&gt;&gt; </span>print(s.name)
    Michael
    <span class="prompt">&gt;&gt;&gt; </span>print(s.score)
    Traceback (most recent call last):
      ...
    AttributeError: <span class="string">'Student'</span> object has no attribute <span class="string">'score'</span>
    </code>

    错误信息很清楚地告诉我们,没有找到score这个attribute。

    要避免这个错误,除了可以加上一个score属性外,Python还有另一个机制,那就是写一个__getattr__()方法,动态返回一个属性。修改如下:

    <code class="python"><span class="class"><span class="keyword">class</span> <span class="title">Student</span><span class="params">(object)</span>:</span>
    
        <span class="function"><span class="keyword">def</span> <span class="title">__init__</span><span class="params">(self)</span>:</span>
            self.name = <span class="string">'Michael'</span>
    
        <span class="function"><span class="keyword">def</span> <span class="title">__getattr__</span><span class="params">(self, attr)</span>:</span>
            <span class="keyword">if</span> attr==<span class="string">'score'</span>:
                <span class="keyword">return</span> <span class="number">99</span>
    </code>

    当调用不存在的属性时,比如score,Python解释器会试图调用__getattr__(self, 'score')来尝试获得属性,这样,我们就有机会返回score的值:

    <code class="python"><span class="prompt">&gt;&gt;&gt; </span>s = Student()
    <span class="prompt">&gt;&gt;&gt; </span>s.name
    <span class="string">'Michael'</span>
    <span class="prompt">&gt;&gt;&gt; </span>s.score
    <span class="number">99</span>
    </code>

    返回函数也是完全可以的:

    <code class="python"><span class="class"><span class="keyword">class</span> <span class="title">Student</span><span class="params">(object)</span>:</span>
    
        <span class="function"><span class="keyword">def</span> <span class="title">__getattr__</span><span class="params">(self, attr)</span>:</span>
            <span class="keyword">if</span> attr==<span class="string">'age'</span>:
                <span class="keyword">return</span> <span class="keyword">lambda</span>: <span class="number">25</span>
    </code>

    只是调用方式要变为:

    <code class="python"><span class="prompt">&gt;&gt;&gt; </span>s.age()
    <span class="number">25</span>
    </code>

    注意,只有在没有找到属性的情况下,才调用__getattr__,已有的属性,比如name,不会在__getattr__中查找。

    此外,注意到任意调用如s.abc都会返回None,这是因为我们定义的__getattr__默认返回就是None。要让class只响应特定的几个属性,我们就要按照约定,抛出AttributeError的错误:

    <code class="python"><span class="class"><span class="keyword">class</span> <span class="title">Student</span><span class="params">(object)</span>:</span>
    
        <span class="function"><span class="keyword">def</span> <span class="title">__getattr__</span><span class="params">(self, attr)</span>:</span>
            <span class="keyword">if</span> attr==<span class="string">'age'</span>:
                <span class="keyword">return</span> <span class="keyword">lambda</span>: <span class="number">25</span>
            <span class="keyword">raise</span> AttributeError(<span class="string">'\'Student\' object has no attribute \'%s\''</span> % attr)
    </code>

    这实际上可以把一个类的所有属性和方法调用全部动态化处理了,不需要任何特殊手段。

    这种完全动态调用的特性有什么实际作用呢?作用就是,可以针对完全动态的情况作调用。

    举个例子:

    现在很多网站都搞REST API,比如新浪微博、豆瓣啥的,调用API的URL类似:

    如果要写SDK,给每个URL对应的API都写一个方法,那得累死,而且,API一旦改动,SDK也要改。

    利用完全动态的__getattr__,我们可以写出一个链式调用:

    <code class="python"><span class="class"><span class="keyword">class</span> <span class="title">Chain</span><span class="params">(object)</span>:</span>
    
        <span class="function"><span class="keyword">def</span> <span class="title">__init__</span><span class="params">(self, path=<span class="string">''</span>)</span>:</span>
            self._path = path
    
        <span class="function"><span class="keyword">def</span> <span class="title">__getattr__</span><span class="params">(self, path)</span>:</span>
            <span class="keyword">return</span> Chain(<span class="string">'%s/%s'</span> % (self._path, path))
    
        <span class="function"><span class="keyword">def</span> <span class="title">__str__</span><span class="params">(self)</span>:</span>
            <span class="keyword">return</span> self._path
    
        __repr__ = __str__
    </code>

    试试:

    <code class="php">&gt;&gt;&gt; Chain().status.user.timeline.<span class="keyword">list</span>
    <span class="string">'/status/user/timeline/list'</span>
    </code>

    这样,无论API怎么变,SDK都可以根据URL实现完全动态的调用,而且,不随API的增加而改变!

    还有些REST API会把参数放到URL中,比如GitHub的API:

    <code class="ruby"><span class="constant">GET</span> /users/<span class="symbol">:user/repos</span>
    </code>

    调用时,需要把:user替换为实际用户名。如果我们能写出这样的链式调用:

    <code class="undefined">Chain().users('michael').repos
    </code>

    就可以非常方便地调用API了。有兴趣的童鞋可以试试写出来。

    __call__

    一个对象实例可以有自己的属性和方法,当我们调用实例方法时,我们用instance.method()来调用。能不能直接在实例本身上调用呢?在Python中,答案是肯定的。

    任何类,只需要定义一个__call__()方法,就可以直接对实例进行调用。请看示例:

    <code class="python"><span class="class"><span class="keyword">class</span> <span class="title">Student</span><span class="params">(object)</span>:</span>
        <span class="function"><span class="keyword">def</span> <span class="title">__init__</span><span class="params">(self, name)</span>:</span>
            self.name = name
    
        <span class="function"><span class="keyword">def</span> <span class="title">__call__</span><span class="params">(self)</span>:</span>
            print(<span class="string">'My name is %s.'</span> % self.name)
    </code>

    调用方式如下:

    <code class="python"><span class="prompt">&gt;&gt;&gt; </span>s = Student(<span class="string">'Michael'</span>)
    <span class="prompt">&gt;&gt;&gt; </span>s() <span class="comment"># self参数不要传入</span>
    My name <span class="keyword">is</span> Michael.
    </code>

    __call__()还可以定义参数。对实例进行直接调用就好比对一个函数进行调用一样,所以你完全可以把对象看成函数,把函数看成对象,因为这两者之间本来就没啥根本的区别。

    如果你把对象看成函数,那么函数本身其实也可以在运行期动态创建出来,因为类的实例都是运行期创建出来的,这么一来,我们就模糊了对象和函数的界限。

    那么,怎么判断一个变量是对象还是函数呢?其实,更多的时候,我们需要判断一个对象是否能被调用,能被调用的对象就是一个Callable对象,比如函数和我们上面定义的带有__call__()的类实例:

    <code class="python"><span class="prompt">&gt;&gt;&gt; </span>callable(Student())
    <span class="built_in">True</span>
    <span class="prompt">&gt;&gt;&gt; </span>callable(max)
    <span class="built_in">True</span>
    <span class="prompt">&gt;&gt;&gt; </span>callable([<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>])
    <span class="built_in">False</span>
    <span class="prompt">&gt;&gt;&gt; </span>callable(<span class="built_in">None</span>)
    <span class="built_in">False</span>
    <span class="prompt">&gt;&gt;&gt; </span>callable(<span class="string">'str'</span>)
    <span class="built_in">False</span>
    </code>

    通过callable()函数,我们就可以判断一个对象是否是“可调用”对象。

    小结

    Python的class允许定义许多定制方法,可以让我们非常方便地生成特定的类。

    本节介绍的是最常用的几个定制方法,还有很多可定制的方法,请参考Python的官方文档

  • 小内存服务器Nginx+php-fpm recv() failed (104: Connection reset by peer) 错误解决一例

    因为近期捣鼓上了 python web,所以把原来的 apache+wordpress 换成了 nginx+php-fpm+wordpress(目的是php和python一起跑),然后问题就来了,服务器跑个几天就会打不开页面,经常出现 502 Error,SSH连接也是很卡,prtree查看有30*php-fpm,一共30个php-fpm进程,感觉上应该是php-fpm进程太多把服务器拖死掉了,重启 php-fpm 服务器,pstree 查看 6*php-fpm,一切正常,页面也能正常访问。

    参考了文章《PHP配置文件php-fpm的优化》,我把它转载到我的blog来了,修改php-fpm最大进程数为10,我的阿里ECS只有512M内存,重启php-fpm,世界就清静了。

     

    ; Start a new pool named 'www'.
    [www]
    
    ; The address on which to accept FastCGI requests.
    ; Valid syntaxes are:
    ;   'ip.add.re.ss:port'    - to listen on a TCP socket to a specific address on
    ;                            a specific port;
    ;   'port'                 - to listen on a TCP socket to all addresses on a
    ;                            specific port;
    ;   '/path/to/unix/socket' - to listen on a unix socket.
    ; Note: This value is mandatory.
    listen = 127.0.0.1:9000
    
    ; Set listen(2) backlog. A value of '-1' means unlimited.
    ; Default Value: -1
    ;listen.backlog = -1
     
    ; List of ipv4 addresses of FastCGI clients which are allowed to connect.
    ; Equivalent to the FCGI_WEB_SERVER_ADDRS environment variable in the original
    ; PHP FCGI (5.2.2+). Makes sense only with a tcp listening socket. Each address
    ; must be separated by a comma. If this value is left blank, connections will be
    ; accepted from any ip address.
    ; Default Value: any
    listen.allowed_clients = 127.0.0.1
    
    ; Set permissions for unix socket, if one is used. In Linux, read/write
    ; permissions must be set in order to allow connections from a web server. Many
    ; BSD-derived systems allow connections regardless of permissions. 
    ; Default Values: user and group are set as the running user
    ;                 mode is set to 0666
    ;listen.owner = nobody
    ;listen.group = nobody
    ;listen.mode = 0666
    
    ; Unix user/group of processes
    ; Note: The user is mandatory. If the group is not set, the default user's group
    ;       will be used.
    ; RPM: apache Choosed to be able to access some dir as httpd
    user = nginx
    ; RPM: Keep a group allowed to write in log dir.
    group = nginx
    
    ; Choose how the process manager will control the number of child processes.
    ; Possible Values:
    ;   static  - a fixed number (pm.max_children) of child processes;
    ;   dynamic - the number of child processes are set dynamically based on the
    ;             following directives:
    ;             pm.max_children      - the maximum number of children that can
    ;                                    be alive at the same time.
    ;             pm.start_servers     - the number of children created on startup.
    ;             pm.min_spare_servers - the minimum number of children in 'idle'
    ;                                    state (waiting to process). If the number
    ;                                    of 'idle' processes is less than this
    ;                                    number then some children will be created.
    ;             pm.max_spare_servers - the maximum number of children in 'idle'
    ;                                    state (waiting to process). If the number
    ;                                    of 'idle' processes is greater than this
    ;                                    number then some children will be killed.
    ; Note: This value is mandatory.
    pm = dynamic
    
    ; The number of child processes to be created when pm is set to 'static' and the
    ; maximum number of child processes to be created when pm is set to 'dynamic'.
    ; This value sets the limit on the number of simultaneous requests that will be
    ; served. Equivalent to the ApacheMaxClients directive with mpm_prefork.
    ; Equivalent to the PHP_FCGI_CHILDREN environment variable in the original PHP
    ; CGI.
    ; Note: Used when pm is set to either 'static' or 'dynamic'
    ; Note: This value is mandatory.
    pm.max_children = 50
    
    ; The number of child processes created on startup.
    ; Note: Used only when pm is set to 'dynamic'
    ; Default Value: min_spare_servers + (max_spare_servers - min_spare_servers) / 2
    pm.start_servers = 5
    
    ; The desired minimum number of idle server processes.
    ; Note: Used only when pm is set to 'dynamic'
    ; Note: Mandatory when pm is set to 'dynamic'
    pm.min_spare_servers = 5
    
    ; The desired maximum number of idle server processes.
    ; Note: Used only when pm is set to 'dynamic'
    ; Note: Mandatory when pm is set to 'dynamic'
    ;pm.max_spare_servers = 35
    ; 内存有限把最大进程数改成 10
    pm.max_spare_servers = 10
     
    ; The number of requests each child process should execute before respawning.
    ; This can be useful to work around memory leaks in 3rd party libraries. For
    ; endless request processing specify '0'. Equivalent to PHP_FCGI_MAX_REQUESTS.
    ; Default Value: 0
    ;pm.max_requests = 500
    
    ; The URI to view the FPM status page. If this value is not set, no URI will be
    ; recognized as a status page. By default, the status page shows the following
    ; information:
    ;   accepted conn    - the number of request accepted by the pool;
    ;   pool             - the name of the pool;
    ;   process manager  - static or dynamic;
    ;   idle processes   - the number of idle processes;
    ;   active processes - the number of active processes;
    ;   total processes  - the number of idle + active processes.
    ; The values of 'idle processes', 'active processes' and 'total processes' are
    ; updated each second. The value of 'accepted conn' is updated in real time.
    ; Example output:
    ;   accepted conn:   12073
    ;   pool:             www
    ;   process manager:  static
    ;   idle processes:   35
    ;   active processes: 65
    ;   total processes:  100
    ; By default the status page output is formatted as text/plain. Passing either
    ; 'html' or 'json' as a query string will return the corresponding output
    ; syntax. Example:
    ;   http://www.foo.bar/status
    ;   http://www.foo.bar/status?json
    ;   http://www.foo.bar/status?html
    ; Note: The value must start with a leading slash (/). The value can be
    ;       anything, but it may not be a good idea to use the .php extension or it
    ;       may conflict with a real PHP file.
    ; Default Value: not set 
    ;pm.status_path = /status
     
    ; The ping URI to call the monitoring page of FPM. If this value is not set, no
    ; URI will be recognized as a ping page. This could be used to test from outside
    ; that FPM is alive and responding, or to
    ; - create a graph of FPM availability (rrd or such);
    ; - remove a server from a group if it is not responding (load balancing);
    ; - trigger alerts for the operating team (24/7).
    ; Note: The value must start with a leading slash (/). The value can be
    ;       anything, but it may not be a good idea to use the .php extension or it
    ;       may conflict with a real PHP file.
    ; Default Value: not set
    ;ping.path = /ping
    
    ; This directive may be used to customize the response of a ping request. The
    ; response is formatted as text/plain with a 200 response code.
    ; Default Value: pong
    ;ping.response = pong
     
    ; The timeout for serving a single request after which the worker process will
    ; be killed. This option should be used when the 'max_execution_time' ini option
    ; does not stop script execution for some reason. A value of '0' means 'off'.
    ; Available units: s(econds)(default), m(inutes), h(ours), or d(ays)
    ; Default Value: 0
    ;request_terminate_timeout = 0
     
    ; The timeout for serving a single request after which a PHP backtrace will be
    ; dumped to the 'slowlog' file. A value of '0s' means 'off'.
    ; Available units: s(econds)(default), m(inutes), h(ours), or d(ays)
    ; Default Value: 0
    ;request_slowlog_timeout = 0
     
    ; The log file for slow requests
    ; Default Value: not set
    ; Note: slowlog is mandatory if request_slowlog_timeout is set
    slowlog = /var/log/php-fpm/www-slow.log
     
    ; Set open file descriptor rlimit.
    ; Default Value: system defined value
    ;rlimit_files = 1024
     
    ; Set max core size rlimit.
    ; Possible Values: 'unlimited' or an integer greater or equal to 0
    ; Default Value: system defined value
    ;rlimit_core = 0
     
    ; Chroot to this directory at the start. This value must be defined as an
    ; absolute path. When this value is not set, chroot is not used.
    ; Note: chrooting is a great security feature and should be used whenever 
    ;       possible. However, all PHP paths will be relative to the chroot
    ;       (error_log, sessions.save_path, ...).
    ; Default Value: not set
    ;chroot = 
     
    ; Chdir to this directory at the start. This value must be an absolute path.
    ; Default Value: current directory or / when chroot
    ;chdir = /var/www
     
    ; Redirect worker stdout and stderr into main error log. If not set, stdout and
    ; stderr will be redirected to /dev/null according to FastCGI specs.
    ; Default Value: no
    ;catch_workers_output = yes
     
    ; Limits the extensions of the main script FPM will allow to parse. This can
    ; prevent configuration mistakes on the web server side. You should only limit
    ; FPM to .php extensions to prevent malicious users to use other extensions to
    ; exectute php code.
    ; Note: set an empty value to allow all extensions.
    ; Default Value: .php
    ;security.limit_extensions = .php .php3 .php4 .php5
    
    ; Pass environment variables like LD_LIBRARY_PATH. All $VARIABLEs are taken from
    ; the current environment.
    ; Default Value: clean env
    ;env[HOSTNAME] = $HOSTNAME
    ;env[PATH] = /usr/local/bin:/usr/bin:/bin
    ;env[TMP] = /tmp
    ;env[TMPDIR] = /tmp
    ;env[TEMP] = /tmp
    
    ; Additional php.ini defines, specific to this pool of workers. These settings
    ; overwrite the values previously defined in the php.ini. The directives are the
    ; same as the PHP SAPI:
    ;   php_value/php_flag             - you can set classic ini defines which can
    ;                                    be overwritten from PHP call 'ini_set'. 
    ;   php_admin_value/php_admin_flag - these directives won't be overwritten by
    ;                                     PHP call 'ini_set'
    ; For php_*flag, valid values are on, off, 1, 0, true, false, yes or no.
    
    ; Defining 'extension' will load the corresponding shared extension from
    ; extension_dir. Defining 'disable_functions' or 'disable_classes' will not
    ; overwrite previously defined php.ini values, but will append the new value
    ; instead.
    
    ; Default Value: nothing is defined by default except the values in php.ini and
    ;                specified at startup with the -d argument
    ;php_admin_value[sendmail_path] = /usr/sbin/sendmail -t -i -f www@my.domain.com
    ;php_flag[display_errors] = off
    php_admin_value[error_log] = /var/log/php-fpm/www-error.log
    php_admin_flag[log_errors] = on
    ;php_admin_value[memory_limit] = 128M
    
    ; Set session path to a directory owned by process user
    php_value[session.save_handler] = files
    php_value[session.save_path] = /var/lib/php/session
    
    
  • PHP配置文件php-fpm的优化

    本文转自www.linuxde.net

    本文所涉及的配置文件名为PHP-fpm.conf,里面比较重要的配置项有如下一些。

    request_terminate_timeout = 30
    

    #表示等待30秒后,结束那些没有自动结束的php脚本,以释放占用的资源。

    pm表示使用那种方式,就是static(静态)或者dynamic(动态)。在更老一些的版本中,dynamic被称作apache-like。

    pm.max_children:静态方式下开启的php-fpm进程数量。
    pm.start_servers:动态方式下的起始php-fpm进程数量。
    pm.min_spare_servers:动态方式下的最小php-fpm进程数量。
    pm.max_spare_servers:动态方式下的最大php-fpm进程数量。

    如果dm设置为static,那么其实只有pm.max_children这个参数生效,系统会开启设置数量的php-fpm进程。

    如果dm设置为dynamic,那么pm.max_children参数失效,后面3个参数生效。

    系统会在php-fpm运行开始 的时候启动pm.start_servers个php-fpm进程,然后根据系统的需求动态在pm.min_spare_servers和pm.max_spare_servers之间调整php-fpm进程数。

    那么,对于我们的服务器,选择哪种执行方式比较好呢?事实上,跟Apache一样,运行的PHP程序在执行完成后,或多或少会有内存泄露的问题。这也是为什么开始的时候一个php-fpm进程只占用3M左右内存,运行一段时间后就会上升到20-30M的原因了。

    对于内存大的服务器(比如8G以上)来说,指定静态的max_children实际上更为妥当,因为这样不需要进行额外的进程数目控制,会提高效率。

    因为频繁开关php-fpm进程也会有时滞,所以内存够大的情况下开静态效果会更好。数量也可以根据 内存/30M 得到,比如8GB内存可以设置为100,那么php-fpm耗费的内存就能控制在 2G-3G的样子。如果内存稍微小点,比如1G,那么指定静态的进程数量更加有利于服务器的稳定。这样可以保证php-fpm只获取够用的内存,将不多的 内存分配给其他应用去使用,会使系统的运行更加畅通。

    对于小内存的服务器来说,比如256M内存的VPS,即使按照一个20M的内存量来算,10个php-cgi进程就将耗掉200M内存,那系统的崩溃就应该很正常了。

    因此应该尽量地控制php-fpm进程的数量,大体明确其他应用占用的内存后,给它指定一个静态的小数量,会让系统更加平稳一些。或者使用动态方式,因为动态方式会结束掉多余的进程,可以回收释放一些内存,所以推荐在内存较少的服务器或VPS上使用。具体最大数量根据 内存/20M 得到。比如512M的VPS,建议pm.max_spare_servers设置为20。至于pm.min_spare_servers,则建议根据服务器的负载情况来设置,比较合适的值在5~10之间。

  • beyond compare 4 for linux 注册

    打开之后直接输入下面的 key(也可以使用KeyGen.exe然后generate生成的 key)

    --- BEGIN LICENSE KEY ---
    GXN1eh9FbDiX1ACdd7XKMV7hL7x0ClBJLUJ-zFfKofjaj2yxE53xauIfkqZ8FoLpcZ0Ux6McTyNmODDSvSIHLYhg1QkTxjCeSCk6ARz0ABJcnUmd3dZYJNWFyJun14rmGByRnVPL49QH+Rs0kjRGKCB-cb8IT4Gf0Ue9WMQ1A6t31MO9jmjoYUeoUmbeAQSofvuK8GN1rLRv7WXfUJ0uyvYlGLqzq1ZoJAJDyo0Kdr4ThF-IXcv2cxVyWVW1SaMq8GFosDEGThnY7C-SgNXW30jqAOgiRjKKRX9RuNeDMFqgP2cuf0NMvyMrMScnM1ZyiAaJJtzbxqN5hZOMClUTE+++
    --- END LICENSE KEY -----
    

    复制代码
    Windows 版本的可以直接下载 keygen.exe 文件,打开keygen.exe点击patch选择BCompare.exe,会自动备份原文件,然后点击generate,生成 key,点击 copy
    最后运行Beyond Compare 4输入前面 copy 的key 就可以了

    简单说一下思路:感谢 zwt团队提供的 win版keygen 文件,根据win 这个文件对 Mac 和 Ubuntu 的文件进行了 hex,然后使用 zwt keygen 生成的 key 进行注册即可
    keygen 主要是将BCompare内的 key 进行修改,让其可以使用generate生成的 key
    如果想自己动手的,可以 hex BCompare,将下面的文件替换
    原文件自带 key

    keexjEP3t4Mue23hrnuPtY4TdcsqNiJL-5174TsUdLmJSIXKfG2NGPwBL6vnRPddT7tH29qpkneX63DO9ECSPE9rzY1zhThHERg8lHM9IBFT+rVuiY823aQJuqzxCKIE1bcDqM4wgW01FH6oCBP1G4ub01xmb4BGSUG6ZrjxWHJyNLyIlGvOhoY2HAYzEtzYGwxFZn2JZ66o4RONkXjX0DF9EzsdUef3UAS+JQ+fCYReLawdjEe6tXCv88GKaaPKWxCeaUL9PejICQgRQOLGOZtZQkLgAelrOtehxz5ANOOqCaJgy2mJLQVLM5SJ9Dli909c5ybvEhVmIC0dc9dWH+
    

    复制代码
    替换为下面的 key

    N9KmiLVlKMU7RJqnE+WXEEPI1SgglmfmLc1yVH7dqBb9ehOoKG9UE+HAE1YvH1XX2XVGeEqYUY-Tsk7YBTz0WpSpoYyPgx6Iki5KLtQ5G-aKP9eysnkuOAkrvHU8bLbGtZteGwJarev03PhfCioJL4OSqsmQGEvDbHFEbNl1qJtdwEriR+VNZts9vNNLk7UGfeNwIiqpxjk4Mn09nmSd8FhM4ifvcaIbNCRoMPGl6KU12iseSe+w+1kFsLhX+OhQM8WXcWV10cGqBzQE9OqOLUcg9n0krrR3KrohstS9smTwEx9olyLYppvC0p5i7dAx2deWvM1ZxKNs0BvcXGukR+
    

    复制代码
    把文件 hex 之后就可以使用 ZWT keygen 生成的 key 了

  • Tux Commander 双面板文件管理器快捷键

    Key shortcuts
    The following key shortcuts can be used in panels:

    Letter Redirect typing to command line
    Enter Change directory / run program / run associated program / execute command line if not empty
    Ctrl+Enter, Alt+Enter Insert selected file/directory name to the command line
    Tab Switch to opposite panel
    Num + Expand selection
    Num – Shrink selection
    Num * Invert selection
    Backspace Change to the parent directory
    Insert Include or exclude file/directory to or from the selection
    Ctrl+R Refresh current directory
    Ctrl+O, Alt+O Change the path in the opposite panel to the focused directory
    Ctrl+S, Alt+S, “/” key Start quick search and redirect typing to the search box
    Ctrl+A, Alt+A Insert current path to the command line
    Ctrl+P, Alt+P Select previous command in the command line history
    Ctrl+N, Alt+N Select next command in the command line history
    Shift+F4 Create new text file and open it in the editor
    Shift+F5 Copy files (with rename) in the same directory
    Shift+F6 Perform quick-rename (inplace editing)
    Space Count the directory size under the cursor
    Alt+Shift+Enter Count sizes of all directories in the list
    Ctrl+Home Go to the home directory in the current panel
    Ctrl+/ Go to the root directory (“/”) in the current panel
    Ctrl+LeftArrow/RightArrow Switch the panel on the left/right to the selected directory (or actual if file is selected)
    Alt+F1, Alt+F2 Display bookmark menu for left (right) panel
    Ctrl+D Display bookmark menu for active panel
    Alt+1..9,0 Quick jump to the bookmark (the numbering starts from 1 = first bookmark)
    Ctrl+Down Activate commandline
    Ctrl+Up Get back to panel from commandline
    Ctrl+T Duplicate current panel tab
    Ctrl+W Close current panel tab
    Ctrl+Tab, Ctrl+PageDown Switch to the next panel tab
    Ctrl+Shift+Tab, Ctrl+PageUp Switch to the previous panel tab
    Ctrl+. Show/hide dot files
    Ctrl+F Open the Connection Manager
    Shift+Ctrl+F Disconnect active connection
    Shift+F2, Ctrl+F2 Copy selected file names to the clipboard (Ctrl+F2 with full pathnames)
    Alt+R Open terminal from the current location (shortcut depends on localization)
  • pacman 一些基本用法

    一些基本用法示例:

    • pacman -U xx.pkg.tar.xz //安装本地包,.pkg.tar.xz 为 archlinux 的本地包扩展名
    • pacman -Syy //更新软件源
    • pacman -Syu //升级系统

    删除软件包

    • pacman -R package_name //删除单个软件包,保留其全部已经安装的依赖关系
    • pacman -Rs package_name //删除指定软件包,及其所有没有被其他已安装软件包使用的依赖关系
    • pacman -Rsc package_name //要删除软件包和所有依赖这个软件包的程序
    • pacman -Rdd package_name //要删除软件包,但是不删除依赖这个软件包的其他程序
    • pacman -Rn package_name //pacman 删除某些程序时会备份重要配置文件,-n 选项可以删除这些文件
    • pacman -Rs $(pacman -Qtdq) //删除孤立软件包(递归的,要小心)

    查询包数据库

    • pacman 使用 -Q 参数查询本地软件包数据库。参见:
    • pacman -Q –help
    • 使用 -S 参数来查询远程同步的数据库。参见:
    • pacman -S –help
    • pacman -Sg gnome //查看哪些包属于 gnome 组
    • pacman -Ss package //查询软件包
    • pacman -Qs package //查询已安装的软件包
    • pacman -Si package //显示软件包的详尽的信息
    • pacman -Qi package //查询本地安装包的详细信息
    • pacman -Qii package_name //使用两个 -i 将同时显示备份文件和修改状态
    • pacman -Ql package //获取已安装软件包所包含文件的列表
    • pacman -Qdt //罗列所有不再作为依赖的软件包
    • pactree package_name //显示软件包的依赖树
  • Arch Linux 的靈魂:PKGBUILD、AUR 和 ABS

    在下先前在 LinuxTOY 上看到了該文,覺得是非常很好的 Arch Linux 傳道文,特此重新大幅度優化排版並更新了部分過時內容,以饗讀者。此外難免會有疏漏之處,歡迎斧正。

    版權特定聲明:

    1. 原載:https://linuxtoy.org
    2. 作者:Ning Bao
    3. 原文網址:https://linuxtoy.org/archives/archlinux-pkgbuild-aur-and-abs.html

    Updated 1: 感謝 [twitter|lilydjwg] 進一步大力斧正過時內容!
    Updated 2: 感谢 [twitter|felixonmars] 提供了种种宝贵的意见!

    注意:在嘗試本文提供的種種練習前,確保已經安裝了 base-devel

    PKGBUILD 和 makepkg

    其实,我使用 Arch Linux 的时间并不是很长。可是,就是在这半年的时间内,我感觉我学到了很多的东西,对 GNU/Linux 有了更多的了解,也在这个过程中深深地爱上了 Arch Linux 这个发行版。

    首先声明一下,我不是一个程序员,从来没有写过真正的 Code,顶多是写一点 scripts,或者做一些简单的网页什么的。和很多人一样,我对计算机的接触是从 DOS/Windows 开始的。Windows 的图形界面很容易学。可是时间长了,Windows 就会运行的越来越慢,我的硬盘上也就会有很多我根本不知道是什么的东西。而且,在 Windows 上很多东西都是设定好的,改变起来非常不容易。不要跟我提注册表,那个东西实在是让我一头雾水。还有,大部分 Windows 上的软件都不是自由软件。这意味着人们很难了解这些软件内部的情況,你可能在毫无察觉的情况下就中了病毒或木马。

    所以,为了能够完全了解和控制我每天工作生活要用的计算机,我开始学着使用 GNU/Linux。我一开始是用 Mandriva(那个时候还叫 Mandrake Linux)。后来是 Ubuntu(也包括其他一些 Debian 为基础的发行版)。Wow!我一用上 Ubuntu 就有了完全不同的感觉。特别是非常好用的 apt-get,加上庞大的自由软件库,让我大开眼界。可是在使用 Ubuntu 一段时间以后,我发现这个平台实在和 Windows 非常相似。Gnome 和 KDE 的界面都是在模仿 Windows。更糟糕的是,Ubuntu 会在一开始安装一些乱七八糟的东西,大大影响了我的电脑的运行速度。我需要一点一点把我不用的东西去掉,这个过程真的很不爽。我开始问自己为什么要放弃 Windows 呢?

    Ubuntu 有一个很大很好的用户社区,很多问题都可以在讨论区得到解决。然而,Ubuntu 的用户完全要依赖 Maintainer。我就有过这样的经历,在发现一个 Bug 后得到很多其他人的确定,可是 Maintainer 迟迟不作修改。还有,也许有很多人都很想用一个比较新的软件,但是大家都要等到有人能够而且愿意作 Maintainer 之后,这个软件才会在 Repository 里出现。我或许能够在调试后自己从源代码编译,可是我要如何和别人分享我的成果呢?

    其实,各种 Linux 发行版在本质上没有什么不一样。大家使用的软件都是要从源代码编译生成可以运行的二进制码。如果没有 rpm、apt-get 或者 pacman,我们也是可以快乐生活的。只不过,我们的生活会变得麻烦一些。如果要从源码安装一个软件,我们通常是要做如下的步骤:

    wget http://somewhere.org/source/package.tar.gz # 下载源代码
    tar xvzf package.tar.gz # 解压缩
    cd package # 进入源代码目录
    ./configure # 设定
    make # 编译
    make install # 安装

    如果我们要像这样安装一个两个软件是没有什么问题的。但是如果我们要对付成百上千的软件/类库的话,这样的土办法是行不通的。于是出现了不同的 Linux 发行版,他们之间的区别只是在于如何管理成百上千的软件,特别是不同软件/类库之间互相依存的关系,也就是 dependency 的问题。

    大多数 Linux 发行版都是以二进制包为基础的,这其中又分 Redhat(还有 SUSE、Fedora 等)、Debian(还有 Ubuntu、PCLinux 等)和 Slackware 阵营。为了解决管理大量软件包的问题,这些发行版采取了这样一个办法。他们找了一群大牛程序员来作 Maintainer,这些 Maintainer 负责把源代码编译成二进制码,加上一些控制信息(比如如何安装、dependency 等),然后一起打包放在服务器上。所以,最终用户根本不用接触源代码。如果你有兴趣的话,你可以抓一个 Debian 的 DEB 文件下来研究一下:

    wget http://somewhare.org/package.deb
    tar vx package.deb

    你会发现你多了三个文件: debian-binary,control.tar.gz,data.tar.gz。

    然后再用 tar tzvf 命令看一看 control.tar.gz 和 data.tar.gz 里面有什么东东,你就明白神奇的 dpkg/apt-get 是怎么一回事情了。

    二进制包固然是很方便,但是这种办法有一个很大的问题。那就是最终用户受到 Maintainer 很大的控制。比如说,我们并不知道 Maintainer 在编译的过程中是如何设定的(./configure)。如果我们要用不同的设定,就要自己从源代码从头开始。另外,如果某一个 Maintainer 心术不正,在二进制包里面加了木马程序,我们这些最终用户是很难察觉的。还有,设想一下如果某一个 Maintainer 外出休假了,那么你的软件也就不能及时更新了。

    所以,也有一些发行版采取了完全不同的办法,这些发行版是以源代码为基础的。Gentoo 就是其中的代表。如果你用过 Gentoo 你就会知道 ebuild 文件。你如果有兴趣,可以从 http://packages.gentoo.org/ 抓一个 ebuild 文件研究一下。你会明白 Gentoo 的用户其实从 Gentoo 得到的只有这些 ebuild 文件,在每一个 ebuild 文件里包含了安装使用一个软件需要的所有信息(从哪里下载源代码、如何编译、如何安装还有 dependency 的问题等)。之后,Gentoo 的用户用 emerge 命令按照 ebuild 文件的指示编译、安装一个软件。Gentoo 用户可以使用 USE 等变量来选择启用或者禁用软件的一些功能。这样做的好处是,Gentoo 的用户可以一目了然地了解每一个软件的编译、安装的过程。如果需要的话,Gentoo 的用户可以修改 ebuild,按照自己的需要编译一个软件。

    我也用过 Gentoo。不过对于我这样的初学者,Gentoo 实在是太复杂了,有太多的参数要设定,ebuild 的编写也不是那么简单。还有,Gentoo 几乎不提供任何二进制包,所以绝大部分的软件都要从源代码编译,这是一个非常慢的过程。其实在大部分情况下,用户对一些软件的设定都是差不多的,没有必要让每一个 Gentoo 的用户都从头编译。所以,我需要找到一个发行版,既有 Debian 的易用性,又有 Gentoo 的灵活性。

    我因此找到了 Arch Linux。那么 Arch Linux 又是如何解决从源代码到二进制码的问题呢?Arch Linux 使用了 makepkg 这样一个工具。makepkg 会按照 PKGBUILD 文件生成一个二进制包。有些时候,makepkg 还需要 install 文件(主要用来显示提示信息、备份用户设置等)和其他的配置文件以及补丁等。那么 PKGBUILD 是什么呢?PKGBUILD 和 Gentoo 的 ebuild 一样,包含了安装使用一个软件需要的所有信息。下面是 dwm(一个非常非常简捷、高效的窗口管理器)的 PKGBUILD 文件:

    # $Id$
    # Maintainer: Sergej Pupykin &lt;pupykin.s+arch@gmail.com&gt;
    # Contributor: Dag Odenhall &lt;dag.odenhall@gmail.com&gt;
    # Contributor: Grigorios Bouzakis &lt;grbzks@gmail.com&gt;
    
    pkgname=dwm
    pkgver=6.0 # 软件版本
    pkgrel=1 # 打包的版本
    pkgdesc="A dynamic window manager for X" # 软件说明
    url="http://dwm.suckless.org" # 官方网站
    arch=('i686' 'x86_64') # 适用平台
    license=('MIT') # 版权声明
    options=(zipman) 
    depends=('libx11' 'libxinerama') # 依赖
    install=dwm.install
    source=(http://dl.suckless.org/dwm/dwm-$pkgver.tar.gz
    	config.h
    	dwm.desktop) # 需要的源文件
    md5sums=('8bb00d4142259beb11e13473b81c0857'
             '2453e037f46449774ec8afab49b4f1a2'
             '939f403a71b6e85261d09fc3412269ee') # MD5 校验码
    
    build() {
      cd $srcdir/$pkgname-$pkgver
      cp $srcdir/config.h config.h
      sed -i 's/CPPFLAGS =/CPPFLAGS +=/g' config.mk
      sed -i 's/^CFLAGS = -g/#CFLAGS += -g/g' config.mk
      sed -i 's/^#CFLAGS = -std/CFLAGS += -std/g' config.mk
      sed -i 's/^LDFLAGS = -g/#LDFLAGS += -g/g' config.mk
      sed -i 's/^#LDFLAGS = -s/LDFLAGS += -s/g' config.mk
      make X11INC=/usr/include/X11 X11LIB=/usr/lib/X11
    } # 编译过程
    
    package() {
      cd $srcdir/$pkgname-$pkgver
      make PREFIX=/usr DESTDIR=$pkgdir install
      install -m644 -D LICENSE $pkgdir/usr/share/licenses/$pkgname/LICENSE
      install -m644 -D README $pkgdir/usr/share/doc/$pkgname/README
      install -m644 -D $srcdir/dwm.desktop $pkgdir/usr/share/xsessions/dwm.desktop
    }
    

    我们可以注意到在“编译过程”这个部分,很多代码都和我们在 shell 里编译的命令一样。对!Arch Linux 不要求用户学习太多新的东西,PKGBUILD 很容易理解,因为里面都是基本的 shell 命令。

    好,我们把 PKGBUILDdwm.installdwm.desktopconfig.h(dwm 比较特殊,config.h 包含所有的配置信息,所以要在编译之前提供。其他的软件大多依靠外部的配置文件,像是 .bashrc 等)放在一个新的目录里之后。我们执行:

    makepkg
    

    之后,你会发现这个目录里出现了一些新的东西,包括:dwm-6.0-1-x86_64.pkg.tar.xz,dwm-6.0.tar.gz 两个文件,还有两个目录:src,pkg.

    通过比较这些文件、目录里的内容和 PKGBUILD,再留意下makepkg的输出,你就会明白 makepkg 到底做了些什么:

    1. 根据 source 里的内容下载了源代码文件 dwm-6.0.tar.gz
    2. 通过 md5 校验码确定下载的源代码文件和 PKGBUILD 的作者使用的一致
    3. 把源代码文件解压缩到 ./src,并把本地提供的文件软链接到此处。dwm-6.0 正是压缩包中已有的路径
    4. 按照 build() 里的内容编译源代码,并把编译好的内容放在./pkg/$pkgname 里(旧版本的 makepkg 不会创建 $pkgname 这个目录层级)
    5. 在 ./pkg/$pkgname 里加上其它的一些信息,包括 .PKGINFO、.MTREE和 .INSTALL,也就是 dwm.install 的拷贝;
    6. 把 ./pkg/$pkgname 里面的内容打包形成 dwm-6.0-1-x86_64.pkg.tar.xz。

    那么,我们有了一个 .pkg.tar.xz 这样一个二进制包之后,我们要如何安装呢?我们要使用这样一个命令:

    pacman -U dwm-6.0-1-x86_64.pkg.tar.xz
    

    这个命令又完成了那些事情呢?

    1. 首先,二进制包被解压缩
    2. 按照 .INSTALL 的内容执行安装(或者是升级)前的命令
    3. 二进制包里面的内容会被拷贝到相应的目录(你注意到二进制包内的目录结构了吗?)
    4. 在 /var/lib/pacman/local 这个目录中建立 dwm-6.0-1 这样一个目录
    5. 这个目录里包含了四个文件 desc、files、install 和 mtree
    6. desc 记录了该软件包的基本信息,大部分由 PKGBUILD 中提取,如描述和依赖关系等,files 记录了每一个安装到系统上的文件的路径,install 就是 .INSTALL 的拷贝,mtree 是 .MTREE 的拷贝,记录了各文件的权限、时间、类型和校验码等信息,使用 gzip 压缩过
    7. 按照 .INSTALL 的内容执行安装(或者是升级)后的命令

    从这以后,pacman 正是通过检查 /var/lib/pacman/local 里的内容来管理软件包的。比如说,在执行 pacman -R dwm 的过程中,pacman 首先在/var/lib/pacman/local 找到了 dwm-6.0-1 这个目录,然后根据 files的内容删除已安装的内容。Dependency 也是通过 desc 计算的。

    OK!我已经解释了 PKGBUILD 的基本结构和 makepkg 的过程。基本上是两步:从 PKGBUILD 到 .pkg.tar.xz 包,再从二进制包安装到系统。这样一种办法有很多好处。首先,PKGBUILD 非常方便用户交流。我的一个PKGBUILD 如果编译成功了,就可以给别人用。PKGBUILD 的内容一目了然,不但有助于学习,也再不用担心木马的问题了。

    另外,我通过一个小例子展现 Arch Linux 的灵活性在哪里。比如,我要对 dwm 有自己的设置,也就是自己的 config.h,那我应该怎么做呢?我会做如下的事情:

    1. 编辑 config.h,另存为 myconfig.h;
    2. 编辑 PKGBUILD,把所有的 config.h 替换为 myconfig.h;
    3. 把 pkgname 改成 dwm-my;
    4. 添加 provides=(dwm=$pkgname) 和 conflicts=(dwm) 告诉 pacman 这个包与 dwm 是冲突的,并且它可以用来代替 dwm。

    之后通过 makepkg,我会得到一个文件 dwm-my-6.0-1-x86_64.pkg.tar.xz,这个和原来的 dwm-6.0-1-x86_64.pkg.tar.xz 可以区别开。我可以安装 dwm-my-6.0-1-x86_64.pkg.tar.xz,如果有问题我还可以通过 pacman -U dwm-6.0-1-x86_64.pkg.tar.xz 来安装原来的二进制包。pacman 会知道这两个包之间的关系,并提示用户需要先卸载其中之一才能安装另一个。我还可以用同样的办法生成一系列的 .pkg.tar.xz 包,这在软件的安装调试过程中非常有用。

    AUR 和 ABS

    我非常高兴看到我关于 PKGBUILD 和 makepkg 的文章在 LinuxTOY 受到了欢迎。我想先针对一些读者的回复谈一点题外话。

    我先声明我一点也没有要诋毁 Debian 或 Gentoo 的意思,它们都是非常伟大的发行版,都有自己的特色。其实大多数的发行版都可以自己去定制,从而达到类似的目的。比如说,有的人提到 Gentoo 也有二进制包,比如像 Openoffice 这样的怪物。然而,我个人以为比较不同的发行版关键是要看它最核心的设计思想。比如说,Gentoo 根本就不是为了使用二进制包设计的。你要是都想用二进制包,就别费劲用 Gentoo 了。关于 Debian 阵营的发行版,我也想讲几句。正如一些朋友的回复所讲,DEB/apt-get 是非常好的管理工具,软件库也非常的大。我的笔记本现在还在用 elive,也是 Debian 的分支。我不喜欢 Debian 系列的发行版的原因不是它不能定制,而是他们非常依靠 Maintainer。我们可以自己做 DEB 包,然后呢?你的 DEB 包什么时候才能进入软件库呢?还有,只有你自己知道你的 DEB 是怎么做的,别人不能了解你编译打包的过程。Debian 本身打包的过程没有 Arch Linux 的 PKGBUILD 来的简单明了。只要比较 Debian 的 Maintainer 手册和 Arch Linux 的 Wiki 就可以看出这一点。

    选择什么样的发行版完全是要看个人需要。我选择 Arch Linux,是因为它当初设计的时候就是要满足 “KISS Rule”,也就是 “Keep It Simple, Stupid”。或者说像爱因斯坦讲得:“Everything should be made as simple as possible, but no simpler”。Arch Linux 的所有配置基本都是非常相似的脚本,加上简单灵活的 PKGBUILD 和 pacman,其实关于 Arch Linux 本身真的没有太多新的东西要学习。大家有兴趣可以看看 “Arch Compared To Other Distros (简体中文)” 。Arch Linux 实际上是强迫用户从零开始自己定制自己的系统,在这个过程中也就真正了解了 Linux 本身。

    好了,现在言归正传谈一谈 AUR 和 ABS。

    AUR 是指 Arch Linux User Repository,也就是 Arch Linux 用户社区的软件库。我们现在回忆一下在 Arch Linux 中我们把源代码变成可以运行的二进制码需要哪些文件。我们需要:PKGBUILD,可能还有 .install 文件,加上一些补丁和必要的配置文件(像 dwm 的 config.h)。这样就足够了!当你成功使用 PKGBUILD 编译安装了一个新软件之后就可以通过 AUR 和其他的人分享你的成果了。具体的步骤是:

    1. 在 package-dir 中执行 makepkg –source 把 package-dir 中所有所需的文件打包(包括 PKGBUILD,.install,patch 和其他的config 等)
    2. 前往 http://aur.archlinux.org 选择 “Submit”,并把你的 package-1.0-1.tar.gz 这样的文件上传。如果你希望从命令行上直接完成上传,可以试试这个脚本

    AUR SubmitAUR Submit

    AUR 会自动根据你的 PKGBUILD 内容把你的 Package 加到 AUR 里来。就是这么简单!那么有人会问:“别的用户要如何使用 AUR 呢?”

    这个就更简单了,我们还是用一张截图来解释:

    AUR YaourtAUR Yaourt

    1. 首先下载“Tarball”(红色的圈圈),这个 Tarball 和你上传的内容是一样的,无非是 PKGBUILD 什么的
    2. tar xvzf package.tar.gz 然后解压缩
    3. 然后的步骤你应该知道了,那就是 makepkg 还有 pacman -U

    不过需要提醒的是,为了对自己负责,你应该在编译之前读一下 PKGBUILD和 .install 的内容,确定里面没有恶意的代码。另外,我建议你一般不要以 root 的身份进行 makepkg。其实,makepkg 本身也做了这样的限制,你不加 asroot 的选项是不能 makepkg 的。这是因为,makepkg 会执行 PKGBUILD 里 build() 部分的代码。想一想,如果有人在 build() 这部分加了这么一句 rm -r /home,你就死定了!

    为了防止在打包过程意外删掉外边的文件,可以使用 Gentoo 使用的 sandbox 程序进行一定程序的防范。它当然也在 AUR 里

    有些性急的朋友可能要问这个 AUR 和 pacman 取得的二进制包有什么联系?你应该记住,只要是 Arch Linux,所有的东西一定是从 PKGBUILD 开始的。你通过 pacman 得到的二进制包也是从 PKGBUILD 编译而成的。在你的/etc/pacman.conf 有很多 Repository 的设置,其中的 [core] 和[extra] 是由 Arch Linux 的核心成员维护的,这些软件库里的软件由于特别重要,每个人都要用,所以 Arch Linux 的开发人员把二进制包提前做好,你就可以通过 pacman 取得了。然而,和 Debian 的 Maintainer 机制不一样,最终的用户可以很容易的了解这些软件的编译过程。如果需要的话,最终用户可以改变设置,重新编译这些软件。最典型的情形就是自己编译 kernel 的时候。这要通过 ABS 来解释清楚。ABS,也就是 Arch Linux Build System。首先我们要安装 ABS:

    1. pacman -S abs
    2. vi /etc/abs/abs.conf # 编辑 ABS 的配置文件
    3. 你会看到这样一行 SUPFILES=(core extra !unstable community !testing),把你需要的 Repo 之前的 ! 去掉
    4. 然后以 root 身份执行 abs

    之后又要如何使用 ABS 呢?ABS 所作的事情无非是把所有 Repo 里的软件的 PKGBUILD 下载到你本地的硬盘中。这些 PKGBUILD 都放在了 /var/abs中。你能通过 pacman 直接安装的二进制包其实也都是按照 ABS 的内容编译的。下面我还是用 dwm 的例子解释 ABS 的使用:

    1. su
    2. cd /var/abs # 你可以看到这个目录里有core,extra,community 三个子目录,正如 abs.conf 中的设定
    3. mkdir local # 建立一个 local 目录,用来放你自己需要的软件的 PKGBUILD
    4. chown username:usergroup ./local makepkg # 要以非 root 身份进行
    5. exit # 退出
    6. cd local
    7. cp -r ../community/x11/dwm ./ # 从 ABS 中拷贝 dwm 的内容
    8. cd dwm

    下面不用我说了,你在这个目录里可以看到三个文件PKGBUILD、dwm.install、config.h。你于是可以用 makepkg 和 pacman -U 来按照自己喜欢的方式安装 dwm。

    OK,你实际已经清楚了解了 ABS 和 pacman 的关系,那么 AUR 又和 ABS 还有 pacman 有什么联系呢?说的直白一点,你上传到 AUR 的 PKGBUILD 要足够“有品”才能直接通过 pacman 使用。对于“有品”,我是这样定义的。你的 PKGBUILD 要有很多人用(很多人投票),没有恶意代码,没有太多的 Bug……而判定你的 PKGBUILD 够不够“有品”的人是一些叫作 TU(Trusted User)的人。这些人的工作是检查 AUR,关注那些特别受欢迎的 PKGBUILD。之后,他会仔细检查,确定这些 PKGBUILD 是不是安全。然后,他们会给这些 PKGBUILD 打上安全的标签,并且把这些 PKGBUILD 从 unsupported(我们上传的 PKGBUILD 一开始都是在 unsupported 中)移到 Arch Linux 就是这样,非常灵活。既有 pacman 这样好的二进制包管理工具,又有 ABS 和 AUR 这样方便的源代码服务。通过 ABS,你可以完全控制你自己的系统到底是如何建立的。如果在 pacman -Ss 的时候找不到一个软件,你可以到 AUR 去找,如果还是找不到,为什么不自己试着从源代码开始,写一个 PKGBUILD 然后放到 AUR 中和别人分享呢?

    说到这里,我希望我已经把 Arch Linux 最核心的东西讲明白了。有些人说我的文章写得比 wiki 里的文章清楚。其实,我写的东西只是在順序上不一样。我是从 PKGBUILD 开始讲到 AUR 和 ABS,再到 pacman。这个順序和 Arch Linux 实际的开发过程是一致的,所以逻辑上容易理解很多。如果你从 pacman 入手反过来读,你可能就完全错过了理解 Arch Linux 核心概念的机会。

    Yaourt

    就一般情况而言,当 Arch Linux 用户需要使用 AUR 中的包时,往往会执行到 AUR 官方网站查找所需的包、下载该包的 Tarball 文件、在命令行下对 Tarball 文件解压、通过 makepkg 编译打包、最后使用 pacman 安装这样一个过程。仔细打量这个过程,你是否觉得稍微有些繁琐呢?有解决的方案吗?回答是肯定的。这就是我们今天将要介绍的主角──Yaourt。

    简介

    Yaourt 是一个由 Julien Mischkowitz 所编写的 Bash 脚本,它是将 Pacman 与 AUR 这两者相结合的绝佳工具。通过 Yaourt 安装 AUR 中的包十分方便,它不仅简化了上述繁琐的过程,而且把这一过程半自动化,使用者只需在它的交互模式中简单的回答几个问题即可。此外,Yaourt 支持将结果以鲜亮的颜色输出,非常抢眼。

    安装 Yaourt

    除了在 Arch Linux 的 AUR 中可以找到 Yaourt 外,中国和法国的 Arch Linux 社区源也包含 Yaourt。我们采用 Arch Linux 中文社区的源来安装 Yaourt。为了 customizepkg 包,我们把 archlinuxfr 源也加上,但是优先使用 archlinuxcn 的。首先,将下列内容添加到 /etc/pacman.conf 文件:

    [archlinuxcn]
    SigLevel = Optional TrustedOnly
    Server = http://repo.archlinuxcn.org/$arch
    
    [archlinuxfr]
    SigLevel = Optional TrustedOnly
    Server = http://repo.archlinux.fr/$arch
    

    接着,我们可以执行下面的命令来安装 Yaourt:

    pacman -Sy yaourt
    

    另外,我们将 aurvote 和 customizepkg 这两个包也装上,前者用于对喜欢的包投票,而后者是定制 PKGBUILD 所需的:

    pacman -S aurvote customizepkg
    

    同时,你需要为 aurvote 建立一个配置文件 .aurvote (位于 ~/ 目录下):

    user=你的 AUR 帐号
    pass=该帐号的密码
    

    如果你没有 AUR 帐号,可到 http://aur.archlinux.org/account.php 注册一个。

    Yaourt 实战

    为了说明 Yaourt 的使用,我们以一个实例来进行。譬如,我对 Phatch 这个批量图片处理程序非常喜欢,我希望在 Arch Linux 中安装它。首先,我们来看一下,在 Arch Linux 中是否存在 Phatch:

    yaourt phatch
    

    Yaourt 在搜索后返回如下结果:

    Yaourt phatch
    Yaourt phatch

    从该结果我们可以断定,Phatch 在 Arch Linux 的 AUR 中。现在,我们只需按 1 并回车就可以安装它了。

    在显示一些输出信息后,Yaourt 会让你作出第一个选择:是否编辑 PKGBUILD 文件?按 y 回答并输入你喜欢的文本编辑器后,你可以针对 PKGBUILD 的内容进行修改。

    Edit PKGBUILD?
    Edit PKGBUILD?

    第二个选择:是否编辑 phatch.install?

    Edit phatch.install?
    Edit phatch.install?

    然后,Yaourt 会询问是否继续编译。我们的回答当然是 y。

    Continue build phatch?
    Continue build phatch?

    要求输入密码:

    Enter password
    Enter password

    接着,Yaourt 询问是否安装已编译好的包,同样回答 y 即可。

    Continue installing phatch?
    Continue installing phatch?

    输入密码后,再输入 y,大功告成:

    Proceed with installation?
    Proceed with installation?

    综观 Yaourt 的命令行选项,与 Pacman 非常相似。关于 Yaourt 的更加详细的用法,通过 man yaourt 可以获得参考。其实,除了从 AUR 安装包外,Yaourt 也可以从 Arch Linux 的源安装包,此不赘述。

文章导航