wifijammer源碼分析二

主要詳細(xì)學(xué)習(xí)代碼中以下幾個(gè)模塊

from signal import SIGINT, signal
import argparse
import socket
import struct
import fcntl

signal模塊

信號(signal)-- 進(jìn)程之間通訊的方式,是一種軟件中斷。一個(gè)進(jìn)程一旦接收到信號就會(huì)打斷原來的程序執(zhí)行流程來處理信號。
幾個(gè)常用信號:
SIGINT 終止進(jìn)程 中斷進(jìn)程 (control+c)
SIGTERM 終止進(jìn)程 軟件終止信號
SIGKILL 終止進(jìn)程 殺死進(jìn)程
SIGALRM 鬧鐘信號

import os    
import signal    
from time import sleep    
     
def onsignal_term(a,b):    
    print '收到SIGTERM信號'    
     
#這里是綁定信號處理函數(shù),將SIGTERM綁定在函數(shù)onsignal_term上面    
signal.signal(signal.SIGTERM,onsignal_term)    
     
def onsignal_usr1(a,b):    
    print '收到SIGUSR1信號'    
#這里是綁定信號處理函數(shù),將SIGUSR1綁定在函數(shù)onsignal_term上面    
signal.signal(signal.SIGUSR1,onsignal_usr1)    
     
while 1:    
    print '我的進(jìn)程id是',os.getpid()    
    sleep(10)

在另一個(gè)終端中執(zhí)行

import os    
import signal    
     
#發(fā)送信號,16175是前面那個(gè)綁定信號處理函數(shù)的pid,需要自行修改    
os.kill(16175,signal.SIGTERM)    
#發(fā)送信號,16175是前面那個(gè)綁定信號處理函數(shù)的pid,需要自行修改    
os.kill(16175,signal.SIGUSR1)

上述示意了簡單的信號功能,可以用來自定義捕捉信號并執(zhí)行相應(yīng)的函數(shù)。使用信號需要特別注意的地方:如果一個(gè)進(jìn)程收到一個(gè)SIGUSR1信號,然后執(zhí)行信號綁定函數(shù),第二個(gè)SIGUSR2信號又來了,第一個(gè)信號沒有被處理完畢的話,第二個(gè)信號就會(huì)丟棄。所以,盡量不要在多線程中使用信號。

argparse模塊

命令行解析工具,new in 2.7

import argparse

parser = argparse.ArgumentParser(description='Process some integers.')
parser.add_argument('integers', metavar='N', type=int, nargs='+',
                    help='an integer for the accumulator')
parser.add_argument('--sum', dest='accumulate', action='store_const',
                    const=sum, default=max,
                    help='sum the integers (default: find the max)')

args = parser.parse_args()
print args.accumulate(args.integers)

首先,創(chuàng)建一個(gè) ArgumentParser
對象,
parser = argparse.ArgumentParser(description='Process some integers.')
然后添加參數(shù),實(shí)現(xiàn)參數(shù)添加是通過調(diào)用函數(shù)add_argument,該函數(shù)告訴ArgumentParser對象將新添加的參數(shù)保存為attributes,最后parse_args()會(huì)返回該對象且該對象新增了對應(yīng)添加的屬性。

主要method分析

ArgumentParser.add_argument(name or flags...[, action][, nargs][, const][, default][, type][, choices][, required][, help][, metavar][, dest])
  • name or flags - Either a name or a list of option strings, e.g. foo
    or -f, --foo
  • action - The basic type of action to be taken when this argument is encountered at the command line.
  • nargs - The number of command-line arguments that should be consumed.
  • const - A constant value required by some action and nargs selections.
  • default - The value produced if the argument is absent from the command line.
  • type - The type to which the command-line argument should be converted.
    choices - A container of the allowable values for the argument.
  • required - Whether or not the command-line option may be omitted (optionals only).
  • help - A brief description of what the argument does.
  • metavar - A name for the argument in usage messages.
  • dest - The name of the attribute to be added to the object returned by parse_args()
    .

詳細(xì)用法查閱

nargs

parser.add_argument('--foo', nargs=2),最為簡單用法,表示--foo 需要兩個(gè)參數(shù),如果傳遞的參數(shù)不對,則會(huì)引發(fā)error

parser.add_argument('--foo', nargs='?', const='c', default='d'),從命令行傳遞至多一個(gè)參數(shù),如果沒有則用default指定的。‘?’相對更靈活,當(dāng)命令行沒有參數(shù)輸入時(shí)程序不會(huì)出錯(cuò),使用默認(rèn)的。而上面指定個(gè)數(shù)后輸入?yún)?shù)不對程序直接掛掉。

nargs='*' 不直接指定個(gè)數(shù),傳入的參數(shù)表示成一個(gè)列表。

>>> parser = argparse.ArgumentParser()
>>> parser.add_argument('--foo', nargs='*')
>>> parser.add_argument('--bar', nargs='*')
>>> parser.add_argument('baz', nargs='*')
>>> parser.parse_args('a b --foo x y --bar 1 2'.split())
Namespace(bar=['1', '2'], baz=['a', 'b'], foo=['x', 'y'])

nargs = '+' 很像'*',但最低要求一個(gè)參數(shù),當(dāng)沒有參數(shù)時(shí)報(bào)錯(cuò)。而‘’可以沒有參數(shù)。
總結(jié)來說nargs常規(guī)用法就是硬性指定參數(shù)個(gè)數(shù),‘?’,‘
’,‘+’則提供了更多的靈活性,也是程序中較常用的。

action

大部分action完成的動(dòng)作就是為對象添加該屬性,默認(rèn)的動(dòng)作是‘store’,parser.parse_args()返回的對象具有新增屬性,如

args = parser.parse_args()
print args.accumulate(args.integers)

'store_const' --This stores the value specified by the const keyword argument.
'store_true' and 'store_false' 類似于上面的’store_const‘,只是存儲(chǔ)的是True和False。
'append',允許多次指定一個(gè)參數(shù),返回該參數(shù)列表集合。

>>> parser = argparse.ArgumentParser()
>>> parser.add_argument('--foo', action='append')
>>> parser.parse_args('--foo 1 --foo 2'.split())
Namespace(foo=['1', '2'])
type

‘type’,默認(rèn)是以字符串讀入命令行的參數(shù),但實(shí)際中還會(huì)要有int 、float等類型的數(shù)據(jù)輸入,這時(shí)可以通過type指定。

>>> import argparse
>>> parser = argparse.ArgumentParser()
>>> parser.add_argument('foo', type=int)
_StoreAction(option_strings=[], dest='foo', nargs=None, const=None, default=None, type=<type 'int'>, choices=None, help=None, metavar=None)
>>> parser.add_argument('bar', type=file)
_StoreAction(option_strings=[], dest='bar', nargs=None, const=None, default=None, type=<type 'file'>, choices=None, help=None, metavar=None)
>>> ar = parser.parse_args('2 myfile.txt'.split())
>>> ar.foo
2
>>> ar.bar
<open file 'myfile.txt', mode 'r' at 0x7f352c089300>
>>> ar.bar.read()  //返回的是字符串
'myfile.txt\n'

上述程序中有以下兩點(diǎn)注意:一是parser.parse_args()函數(shù)返回的對象具有了新增加的屬性,二是可以進(jìn)行整個(gè)文件的輸入操作,只要通過獲取屬性的方法就能獲取到文件句柄,十分方便。

>>> parser = argparse.ArgumentParser()
>>> parser.add_argument('bar', type=argparse.FileType('w'))
>>> parser.parse_args(['out.txt'])
Namespace(bar=<open file 'out.txt', mode 'w' at 0x...>)

通過 type=argparse.FileType('w') 完成文件打開方式,默認(rèn)的type=file為只讀模式。
type還能是函數(shù)名,實(shí)現(xiàn)傳遞函數(shù)的功能。

choices
>>> parser.add_argument('move', choices=['rock', 'paper', 'scissors'])
>>> parser.parse_args(['rock'])
Namespace(move='rock')

給參數(shù)限定了范圍,如上述中參數(shù) move只能是三者之一。

required
>>> parser = argparse.ArgumentParser()
>>> parser.add_argument('--foo', required=True)
>>> parser.parse_args(['--foo', 'BAR'])
Namespace(foo='BAR')
>>> parser.parse_args([])
usage: argparse.py [-h] [--foo FOO]
argparse.py: error: option --foo is required

表示必選參數(shù)。

help

指定 -h時(shí)顯示幫助信息,以下兩種方式可以顯示幫助信息
parser.print_help()
parser.parse_args(['-h'])

metavar
>>> parser = argparse.ArgumentParser()
>>> parser.add_argument('--foo', metavar='YYY')
>>> parser.add_argument('bar', metavar='XXX')
>>> parser.parse_args('X --foo Y'.split())
Namespace(bar='X', foo='Y')
>>> parser.print_help()
usage:  [-h] [--foo YYY] XXX

positional arguments:
 XXX

optional arguments:
 -h, --help  show this help message and exit
 --foo YYY
dest

該參數(shù)決定了parser.parse_args()函數(shù)返回后增加的屬性名,若不指定則一般用第一個(gè)參數(shù)指定。

struct 模塊

參考學(xué)習(xí)來源
實(shí)現(xiàn)python值和結(jié)構(gòu)體之間的轉(zhuǎn)換,當(dāng)網(wǎng)絡(luò)中傳遞字符串時(shí)很方便,但結(jié)構(gòu)體由于包含int、char等不同類型,需要一種機(jī)制將其打包成二進(jìn)制流的字符串。
struct模塊中最主要的三個(gè)函數(shù)式pack()、unpack()、calcsize()。
pack(fmt, v1, v2, ...) ------ 根據(jù)所給的fmt描述的格式將值v1,v2,...轉(zhuǎn)換為一個(gè)字符串。
unpack(fmt, bytes) ------ 根據(jù)所給的fmt描述的格式將bytes反向解析出來,返回一個(gè)元組。
calcsize(fmt) ------ 根據(jù)所給的fmt描述的格式返回該結(jié)構(gòu)的大小。

 struct header

      {

          unsigned short  usType;

          char[4]               acTag;

          unsigned int      uiVersion;

          unsigned int      uiLength;

      };

 # 在C語言對將該結(jié)構(gòu)體封裝到一塊緩存中是很簡單的,可以使用memcpy()實(shí)現(xiàn)。在Python中,使用struct就需要這樣:

str = struct.pack('B4sII', 0x04, 'aaaa', 0x01, 0x0e)

 # 'B4sII'  ------   有一個(gè)unsigned short、char[4], 2個(gè)unsigned int。其中s之前的數(shù)字說明了字符串的大小 。
type, tag, version, length = struct.unpack('B4sll', str)

常用的格式有以下:


image.png

除了上述直接調(diào)用struct模塊中的函數(shù),struct封裝了一個(gè)類,class struct.Struct(format),該類中同樣封裝了pack,unpack等method,但是封裝格式在struct.Struct(format)中format參數(shù)指定,不用再method中指定。該類的效率更高因?yàn)橹恍枰幾g一次。

Creating a Struct object once and calling its methods is more efficient than calling the struct
functions with the same format since the format string only needs to be compiled once.

import struct
import binascii
values = (1, 'abc', 2.7)
s = struct.Struct('I3sf')
packed_data = s.pack(*values)
unpacked_data = s.unpack(packed_data)
print 'Original values:', values
print 'Format string :', s.format
print 'Uses :', s.size, 'bytes'
print 'Packed Value :', binascii.hexlify(packed_data)
print 'Unpacked Type :', type(unpacked_data), ' Value:', unpacked_data

輸出:
Original values: (1, 'abc', 2.7)
Format string : I3sf
Uses : 12 bytes
Packed Value : 0100000061626300cdcc2c40
Unpacked Type : <type 'tuple'> Value: (1, 'abc', 2.700000047683716)

擴(kuò)展:計(jì)算機(jī)存儲(chǔ)格式

打包的后的字節(jié)順序默認(rèn)上是由操作系統(tǒng)的決定的,當(dāng)然struct模塊也提供了自定義字節(jié)順序的功能,可以指定大端存儲(chǔ)、小端存儲(chǔ)等特定的字節(jié)順序,對于底層通信的字節(jié)順序是十分重要的,不同的字節(jié)順序和存儲(chǔ)方式也會(huì)導(dǎo)致字節(jié)大小的不同。在format字符串前面加上特定的符號即可以表示不同的字節(jié)順序存儲(chǔ)方式,例如采用小端存儲(chǔ) s = struct.Struct(‘<I3sf’)就可以了。

image.png

若沒有指定,默認(rèn)為@,即本地默認(rèn)字節(jié)序,不同系統(tǒng)不一樣,可以通過sys.byteorder查看。網(wǎng)絡(luò)字節(jié)序是大端格式,用!以防自己記不起來大端還是小端。

fcntl 模塊

根據(jù)文件描述符對文件或IO進(jìn)行控制,
fcntl.flock(f,fcntl.LOCK_EX)
這樣就對文件test加鎖了,如果有其他進(jìn)程對test文件加鎖,則不能成功,會(huì)被阻塞,但不會(huì)退出程序。
解鎖:
fcntl.flock(f,fcntl.LOCK_UN)

flock() : flock(f, operation)
operation : 包括:
fcntl.LOCK_UN 解鎖
fcntl.LOCK_EX 排他鎖
fcntl.LOCK_SH 共享鎖
fcntl.LOCK_NB 非阻塞鎖
LOCK_SH 共享鎖:所有進(jìn)程沒有寫訪問權(quán)限,即使是加鎖進(jìn)程也沒有。所有進(jìn)程有讀訪問權(quán)限。
LOCK_EX 排他鎖:除加鎖進(jìn)程外其他進(jìn)程沒有對已加鎖文件讀寫訪問權(quán)限。
LOCK_NB 非阻塞鎖:
如果指定此參數(shù),函數(shù)不能獲得文件鎖就立即返回,否則,函數(shù)會(huì)等待獲得文件鎖。LOCK_NB可以同LOCK_SH或LOCK_NB進(jìn)行按位或(|)運(yùn)算操作。 fcnt.flock(f,fcntl.LOCK_EX|fcntl.LOCK_NB)

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容