不可变对象是传值的,可变对象是传引用的。
其中,标量类型(整数、元组)是不可变类型, 剩下的列表、字典、类、实例都是可变的。
请看下面的例子:
>>> mylist = [1, 'a', ['foo','bar']]
>>> mylist2 = list(mylist)
>>> mylist2[0] = 2
>>> mylist2[2][0] = 'biz'
>>> print mylist
[1, 'a', ['biz', 'bar']]
>>> print mylist2
[2, 'a', ['biz', 'bar']]
注意区分浅复制和深度复制
含有一个元素的元组(1,), 别忘了最后的”,”
注意python中类的使用
from time import ctime
class MyClass(object):
def __init__(self, date):
print "instance created at:", date
>>> m = MyClass(ctime())
instance created at: Fri May 12 11:03:06 2017
>>> ctime()
'Fri May 12 11:03:13 2017'
也就是这个类,使用self实例化,self类似与c++的this,上面的例子中object传递给了date
其他语言中使用new关键字新添属性,在python中,可以直接使用。
>>> class AddressBook(object):
... def __init__(self, name, phone):
... self.name = name
... self.phone = phone
...
>>> john = AddressBook('john Doe', '18263896585')
>>> jane = AddressBook('Chun He', '188')
>>> print john.name
john Doe
>>> join.sex = 'Female'
Traceback (most recent call last):
File "<console>", line 1, in <module>
NameError: name 'join' is not defined
>>> john.sex = 'Female'
>>> print john
<AddressBook object at 0xa952eec>
>>> print john.sex
Female
首先记住,linux kernel networking layers 分为L2(link layer)、L3(ipv4、ipv6)、L4(TCP/UDP)
Network device drivers 就在L2中,与此对应的,你应该知道net_device结构,这是对network stack理解的基础。
struct net_device {
char name[IFNAMSIZE];
struct hlist_node name_hlist;
char *ifalias;
/* I/O specific fields */
unsigned long men_end;
unsigned long men_start;
unsigned long base_addr;
int irq;
...
const struct net_device_ops *netdev_ops;
const struct ethtool_ops *ethtool_ops;
const struct header_ops *header_ops;
unsigned int flags;
unsigned int priv_flags;
unsigned short gflags;
unsigned short padded;
unsigned char operstate;
unsigned char link_mode;
unsigned char if_port;
unsigned char dma;
unsigned int mtu;
unsigned int min_mtu;
unsigned int max_mtu;
unsigned short type;
unsigned short hard_header_len;
unsigned char min_header_len;
...
};
最好的方式是阅读源代码的注释。
当promiscuity计数器大于0的时候,网络栈不会丢弃不是发给本地主机的包。
在old版本中的网络内核中,网络设备驱动是工作在中断驱动模式下的,这就意味着当内核接收到一个packet时,就会陷入中断,这种模式,已经被证明是低效的,尤其是在高负载的情况下。
所以,内核就引入了NAPI(New API)模式,这样, 网络就会工作在polling模式下,当接收到一个包,会缓存进入网络buffer,而不是基于中断驱动了。然后再一个一个进行解包。
对于每一个包,网络子系统在处理的时候,会依据路由子系统对包对出相应的处理从而决定是转发还是发送哪一个接口。路由子系统不是影响这个唯一的因素,比如,在netfilter子系统,有5个钩子函数可以被注册。头一个是NF_INET_PRE_ROUTING.当一个packet被一个由宏NF_HOOK()引发的回调函数处理的时候,它将会根据verdict回调函数继续在内核栈中旅行,比如, 如果verdict是NF_DROP,这个包将会被丢弃;如果verdict是NF_ACCEPT,这个包继续往下;netfilter回调是被nf_register_hook()方法或者nf_register_hooks()方法注册的。
除了netfilter钩子函数,IPsec子系统也会影响包的去行。Ipsec提供了一个网络层的安全解决方案,并且它使用ESP和AH协议。IPsec在IPV6中是强制的,在IPv4中是可选的。它有两种操作模式:传输模式和隧道模式。这是实现一些VPN(virtual private network)方案的基础,当然,也有一些非Ipsec实现VPN的VPN。
这里还有其他因素决定网络包的行程。比如, 被转发包中Ipv4中头部ttl的值。当packet被转发一个设备,ttl的值就会减1.当他为0的时候,这个packet就会丢弃,并且一个ICMPv4消息:”时间超出”随着”ttl count exceeded”的代码返回。每转发一次,这个ttl就会减1.在Ipv6中ttl被替换为hop_limit.
packet在内核中的旅行有很多的变动:大的包在发送之前会被分拆;另一方面,被分拆的包需要组装。不同的包处理的方式也是不同的,比如,多播packet被一组主机处理(这一点很明显与目的地址是特定的单播packets不同)。多播技术可以用在流媒体,这样可以减少网络资源。在ipv4中,The Internet Group Management Protocol(IGMP)处理多波成员。
为了更好的理解网络的流程,你需要知道sk_buff,无论是Rx,还是Tx,都离不开这个结构。<include/linux/skbuff.h>
/**
* struct sk_buff - socket buffer
* @next: Next buffer in list
* @prev: Previous buffer in list
* @tstamp: Time we arrived/left
* @rbnode: RB tree node, alternative to next/prev for netem/tcp
* @sk: Socket we are owned by
* @dev: Device we arrived on/are leaving by
* @cb: Control buffer. Free for use by every layer. Put private vars here
* @_skb_refdst: destination entry (with norefcount bit)
* @sp: the security path, used for xfrm
* @len: Length of actual data
* @data_len: Data length
* @mac_len: Length of link layer header
* @hdr_len: writable header length of cloned skb
* @csum: Checksum (must include start/offset pair)
* @csum_start: Offset from skb->head where checksumming should start
* @csum_offset: Offset from csum_start where checksum should be stored
* @priority: Packet queueing priority
* @ignore_df: allow local fragmentation
* @cloned: Head may be cloned (check refcnt to be sure)
* @ip_summed: Driver fed us an IP checksum
* @nohdr: Payload reference only, must not modify header
* @pkt_type: Packet class
* @fclone: skbuff clone status
* @ipvs_property: skbuff is owned by ipvs
* @tc_skip_classify: do not classify packet. set by IFB device
* @tc_at_ingress: used within tc_classify to distinguish in/egress
* @tc_redirected: packet was redirected by a tc action
* @tc_from_ingress: if tc_redirected, tc_at_ingress at time of redirect
* @peeked: this packet has been seen already, so stats have been
* done for it, don't do them again
* @nf_trace: netfilter packet trace flag
* @protocol: Packet protocol from driver
* @destructor: Destruct function
* @_nfct: Associated connection, if any (with nfctinfo bits)
* @nf_bridge: Saved data about a bridged frame - see br_netfilter.c
* @skb_iif: ifindex of device we arrived on
* @tc_index: Traffic control index
* @hash: the packet hash
* @queue_mapping: Queue mapping for multiqueue devices
* @xmit_more: More SKBs are pending for this queue
* @ndisc_nodetype: router type (from link layer)
* @ooo_okay: allow the mapping of a socket to a queue to be changed
* @l4_hash: indicate hash is a canonical 4-tuple hash over transport
* ports.
* @sw_hash: indicates hash was computed in software stack
* @wifi_acked_valid: wifi_acked was set
* @wifi_acked: whether frame was acked on wifi or not
* @no_fcs: Request NIC to treat last 4 bytes as Ethernet FCS
* @dst_pending_confirm: need to confirm neighbour
* @napi_id: id of the NAPI struct this skb came from
* @secmark: security marking
* @mark: Generic packet mark
* @vlan_proto: vlan encapsulation protocol
* @vlan_tci: vlan tag control information
* @inner_protocol: Protocol (encapsulation)
* @inner_transport_header: Inner transport layer header (encapsulation)
* @inner_network_header: Network layer header (encapsulation)
* @inner_mac_header: Link layer header (encapsulation)
* @transport_header: Transport layer header
* @network_header: Network layer header
* @mac_header: Link layer header
* @tail: Tail pointer
* @end: End pointer
* @head: Head of buffer
* @data: Data head pointer
* @truesize: Buffer size
* @users: User count - see {datagram,tcp}.c
*/
struct sk_buff {
union {
struct {
/* These two members must be first. */
struct sk_buff *next;
struct sk_buff *prev;
union {
ktime_t tstamp;
struct skb_mstamp skb_mstamp;
};
};
struct rb_node rbnode; /* used in netem & tcp stack */
};
struct sock *sk;
union {
struct net_device *dev;
/* Some protocols might use this space to store information,
* while device pointer would be NULL.
* UDP receive path is one user.
*/
unsigned long dev_scratch;
};
/*
* This is the control buffer. It is free to use for every
* layer. Please put your private variables there. If you
* want to keep them across layers you have to do a skb_clone()
* first. This is owned by whoever has the skb queued ATM.
*/
char cb[48] __aligned(8);
unsigned long _skb_refdst;
void (*destructor)(struct sk_buff *skb);
#ifdef CONFIG_XFRM
struct sec_path *sp;
#endif
#if defined(CONFIG_NF_CONNTRACK) || defined(CONFIG_NF_CONNTRACK_MODULE)
unsigned long _nfct;
#endif
#if IS_ENABLED(CONFIG_BRIDGE_NETFILTER)
struct nf_bridge_info *nf_bridge;
#endif
unsigned int len,
data_len;
__u16 mac_len,
hdr_len;
/* Following fields are _not_ copied in __copy_skb_header()
* Note that queue_mapping is here mostly to fill a hole.
*/
kmemcheck_bitfield_begin(flags1);
__u16 queue_mapping;
/* if you move cloned around you also must adapt those constants */
#ifdef __BIG_ENDIAN_BITFIELD
#define CLONED_MASK (1 << 7)
#else
#define CLONED_MASK 1
#endif
#define CLONED_OFFSET() offsetof(struct sk_buff, __cloned_offset)
__u8 __cloned_offset[0];
__u8 cloned:1,
nohdr:1,
fclone:2,
peeked:1,
head_frag:1,
xmit_more:1,
__unused:1; /* one bit hole */
kmemcheck_bitfield_end(flags1);
/* fields enclosed in headers_start/headers_end are copied
* using a single memcpy() in __copy_skb_header()
*/
/* private: */
__u32 headers_start[0];
/* public: */
/* if you move pkt_type around you also must adapt those constants */
#ifdef __BIG_ENDIAN_BITFIELD
#define PKT_TYPE_MAX (7 << 5)
#else
#define PKT_TYPE_MAX 7
#endif
#define PKT_TYPE_OFFSET() offsetof(struct sk_buff, __pkt_type_offset)
__u8 __pkt_type_offset[0];
__u8 pkt_type:3;
__u8 pfmemalloc:1;
__u8 ignore_df:1;
__u8 nf_trace:1;
__u8 ip_summed:2;
__u8 ooo_okay:1;
__u8 l4_hash:1;
__u8 sw_hash:1;
__u8 wifi_acked_valid:1;
__u8 wifi_acked:1;
__u8 no_fcs:1;
/* Indicates the inner headers are valid in the skbuff. */
__u8 encapsulation:1;
__u8 encap_hdr_csum:1;
__u8 csum_valid:1;
__u8 csum_complete_sw:1;
__u8 csum_level:2;
__u8 csum_bad:1;
__u8 dst_pending_confirm:1;
#ifdef CONFIG_IPV6_NDISC_NODETYPE
__u8 ndisc_nodetype:2;
#endif
__u8 ipvs_property:1;
__u8 inner_protocol_type:1;
__u8 remcsum_offload:1;
#ifdef CONFIG_NET_SWITCHDEV
__u8 offload_fwd_mark:1;
#endif
#ifdef CONFIG_NET_CLS_ACT
__u8 tc_skip_classify:1;
__u8 tc_at_ingress:1;
__u8 tc_redirected:1;
__u8 tc_from_ingress:1;
#endif
#ifdef CONFIG_NET_SCHED
__u16 tc_index; /* traffic control index */
#endif
union {
__wsum csum;
struct {
__u16 csum_start;
__u16 csum_offset;
};
};
__u32 priority;
int skb_iif;
__u32 hash;
__be16 vlan_proto;
__u16 vlan_tci;
#if defined(CONFIG_NET_RX_BUSY_POLL) || defined(CONFIG_XPS)
union {
unsigned int napi_id;
unsigned int sender_cpu;
};
#endif
#ifdef CONFIG_NETWORK_SECMARK
__u32 secmark;
#endif
union {
__u32 mark;
__u32 reserved_tailroom;
};
union {
__be16 inner_protocol;
__u8 inner_ipproto;
};
__u16 inner_transport_header;
__u16 inner_network_header;
__u16 inner_mac_header;
__be16 protocol;
__u16 transport_header;
__u16 network_header;
__u16 mac_header;
/* private: */
__u32 headers_end[0];
/* public: */
/* These elements must be at the end, see alloc_skb() for details. */
sk_buff_data_t tail;
sk_buff_data_t end;
unsigned char *head,
*data;
unsigned int truesize;
atomic_t users;
};
就捡着重要的知识说一说。你要熟悉SKB的API。
transport_header();
/*tcp udp icmp and more */
network_header();
/*ip ipv6 arp*/
mac_header();
#link layer
如果你想使用skb->data,你不应该直接使用它,相反,你应该这样
skb_pull_inline();
/* 或者 */
skb_pull();
如果你想fetch L4(transport header),你应该使用
skb_transport_header();
同样, L3(network header),你需要
skb_network_header();
如果你想fetch L2(MAC header)
skb_mac_header();
这三个方法将SKB作为唯一的参数。分别返回头部的指针。
当一个包来临的时候,一个SKB被netdev_alloc_skb()方法分配(或者dev_alloc_skb(),这个方法也是调用netdev_alloc_skb(),但是第一个参数是NULL)。如果遇到包被丢弃的情景,我们将会调用kfree_skb()或者dev_kfree_skb().
在SKB中的一些成员,会被L2层决定,例如,这个pkt_type 就会被eth_type_trans()方法决定,这个方法根据目的的Ethernet地址,如果这个地址是(多播)multicast,就会被设置为PACKET_MULTICASE,如果是(广播)broadcast,则会被设置为PACKET_BROADCASE;如果是本地主机地址,则是PACKET_HOST.绝大多数Ethernet驱动将会在Rx路径调用eth_type_trans()这个方法。这个eth_type_trans()方法也会设置SKB的协议域根据Ethernet的头部。这个方法也会调用skb_pull_inline()提前预付SKB的数据指针,大小为14(ETH_HLEN),这就是Ethernet header的大小。
请看下面的图片:
每一个SKB有一个dev成员,这是一个net_device结构的实例化。接收和发出的包是不同的。在这个过程中,需要不停的fetch能影响包在内核栈中行程的信息。比如MTU。每一个传输的SKB有一个sock对象赋予它(sk),如果这个包是被转发的包,这个sk为NULL,因为它不是有本地主机生成。
每一个接收的包应该通过网络层协议处理,比如,一个ipv4包应该被ip_rcv()方法处理,ipv6应该是ipv6_rcv(),相反的过程,这二者都需要dev_add_pack()方法去注册协议。ip_rcv()方法检查自己的参数,一切没有问题的话被NF_INET_PRE_ROUTING钩子调用。接下来是ip_rcv_finish()方法。在路由子系统中,会建立一个目的缓存项(dst_entry),同时在dst_entry中会有input和output方法。
int (*input)(struct sk_buff);
int (*output)(struct sk_buff);
input()可以被下面的函数指定: ip_local_deliver(),ip_forward(), ip_mr_input(),ip_error()或者dst_discard_in.
output() 可以被下面的使用ip_output,ip_mc_output, ip_rt_bug或者dst_discard_out
在这里简单说一下v4和v6的区别。地址空间不说了,v6的header 是40字节,而v4是20~40,这样v6的性能就会提升了一大截;在ICMP中,v6也加入了很多其他的东西。
接收包被网络设备驱动传递给ipv4或者ipv6-网络层,如果是局部传输,他们会被传递给传输层(l4)的监听socket处理。在L4层上,有UDP和TCP两种协议。此外,还有两种新的协议,Stream Control Transmission Protocol(SCTP),Datagram Congestion Control Protocol(DCCP),这两者结合了tcp和udp的特征。
本地主机产生的packets由L4的TCP或者UDP负责。这些都是Sockets APi可以提供。struct socket是提供给用户空间的接口,struct sock提供L3的接口。
每一个L2网络层接口有一个L2的地址去鉴别它。在Ethernet中,这是一个48位的地址,并且MAC地址被赋予每一个Ethernet网络接口(唯一),每一个Ethernet包以Ethernet header开始,它包含Ethernet 类型(2-bit), 源MAC地址(6-bit),目的MAC地址(6-bit).注意,Ethernet 的类型值ipv4是0x0800,ipv6是0x86DD.对于发出的包来讲,一个Ethernet Header也应该被创建,其中的目的MAC地址通过邻居子系统找到。邻居协议被IPv4中的ARP处理(ipv6中的NDISC),这两者在处理上也有不同的地方。ARP协议依靠于发送广播请求,而NDISC依靠ICMPv6请求,该者实际上是多播请求。
netlink为内核和用户空间之间的交流提供通道。iproute2就是依靠netlink实现的。
路由子系统可以帮助我们找到net设备、找到被发送包的目的主机。
static inline int fib_lookup(struct net *net, const struct flowi4 *flp,
struct fib_result *res, unsigned int flags)
{
struct fib_table *tb;
int err = -ENETUNREACH;
rcu_read_lock();
tb = fib_get_table(net, RT_TABLE_MAIN);
if (tb)
err = fib_table_lookup(tb, flp, res, flags | FIB_LOOKUP_NOREF);
if (err == -EAGAIN)
err = -ENETUNREACH;
rcu_read_unlock();
return err;
}
struct fib_table {
struct hlist_node tb_hlist;
u32 tb_id;
int tb_num_default;
struct rcu_head rcu;
unsigned long *tb_data;
unsigned long __data[0];
};
这个结构位于include/net/ip_net.h. FIB的意思是”Forwarding Information Base”
这里有两个默认的基本表(在没有配置的情况下),比如,局部表(local FIB table)(ip_fib_local_table: ID 255)和主表(main fib table)(ip_fib_main_table; ID 254)
主路由表可以有3种方式改变:
1.系统命令(route add/ip route) 2. (routing daemons) 3.(ICMP 重定向)
fib_loolup 首先寻找local FIB 表,其次再寻找main FIB,route -C这个命令可以查看缓存。另一种就是cat /proc/net/rt_cache,在这样的情况下,地址是十六进制的。
struct rtable {
struct dst_entry dst;
int rt_genid;
unsigned int rt_flags;
__u16 rt_type;
__u8 rt_is_input;
__u8 rt_uses_gateway;
int rt_iif;
/* Info on neighbour */
__be32 rt_gateway;
/* Miscellaneous cached information */
u32 rt_pmtu;
u32 rt_table_id;
struct list_head rt_uncached;
struct uncached_list *rt_uncached_list;
};
分配一个rtable的实例可以使用dst_alloc()方法来进行(net/core/dst.c)。
void *dst_alloc(struct dst_ops *ops, struct net_device *dev,
int initial_ref, int initial_obsolete, unsigned short flags)
{
struct dst_entry *dst;
if (ops->gc && dst_entries_get_fast(ops) > ops->gc_thresh) {
if (ops->gc(ops))
return NULL;
}
dst = kmem_cache_alloc(ops->kmem_cachep, GFP_ATOMIC);
if (!dst)
return NULL;
dst_init(dst, ops, dev, initial_ref, initial_obsolete, flags);
return dst;
}
顺序有些乱,先凑活着看,到时候我再一并整理。
首先看一下ip packet:
git 产生patch还是非常简单的,我们使用最多的就是git format-patch
命令了。
产生单个patch
git format-patch -o ~/patch/ HEAD^
注意这里的HEAD^(HEAD~也可以),如果只有HEAD是不可以的,当然,如果你查看log的话,使用下面的命令:
git log --pretty=oneline --abbrev-commit
git show HEAD # 展示的第一个commit日志
很简单的一点就是 mutt -H your-pacth,then send it,oh, sure, don not forget maintainer of address and who is them. 如何得到维护人的地址或者相关列表呢?使用下面的命令:
git show HEAD | perl scripts/get_maintainer.pl --separator , --nokeywords --nogit --nogit-fallback --norolestats --nol
perl scripts/get_maintainer.pl --separator , --nokeywords --nogit --nogit-fallback --norolestats --nol -f source-files
way 1: git send-email –annotate HEAD^,这一个方式不可控。 way 2: git send-email your-patch.使用这个方法,你上面产生patch的格式稍微复杂些:使用下面的命令:
git format-patch -o ~/patch/ --cc=mail-list1 --to=[email protected] HEAD^
上面的地址在上面的命令中挑出即可。为什么我们在这里这么费劲地产生一个patch,这种方式貌似有些复杂,其实用起来就很简单的。
git send-email是为了提交patch而产生的,所以,你使用它提交patch会非常容易,那么,如果你想提交普通文件的话,需要使用下面的命令:
对,接着使用你上面产生的patch,因为它已经包含相应的信息了,所以仅仅使用:
git send-email --to=[email protected] your-patch
一直按”y”即可,这样单个patch就发出去了。(上面的–to你可以用,也可以不用)
here我是参考的这篇文章。 例如,你要产生patch的commit如下:
db868ad xhci: remove conversion from generic to pci device in xhci_mem.c
c010f0c xhci: remove unnecessary check in xhci_free_stream_info()
a166493 xhci: fix SCT_FOR_CTX(p) macro
56e4cd3 xhci: replace USB_MAXINTERFACES with config->desc.bNumInterface
接下来就是两种方法:
这样你就会得到从c010f0c
,a166493
,56e4cd3
的patch.
还有一种方式就是 在commit-id 处使用 56e4cd3^..c010f0c
,这样也能满足上面的方案。
这里很有意思的一件事就是:
git format-patch -o ~/patch/ -3
就会产生前三个commit的patch.同时,如果你查看0001-xx.patch,你就会看到[PATCH 1/n],这个n就是你上面的”-n”. 你以为这样就结束了,”–to”什么的忘了吗?
使用下面的命令:
git format-patch -o /tmp/ --cover-letter -n --thread=shallow --cc="[email protected]" -3
下面这个实战是我自己的例子,也是目前为止最复杂的一个。 比如,我已经commited两个改变,git log如下:
yubo-2@debian:~/git/linux$ git log --abbrev-commit --oneline
f03d23a34df3 kobject: drop newline from msg string
662efd36ef9a kobject: use pr_warn to replace printk
b72f711a4efa Merge branch 'spectre' of git://git.armlinux.org.uk/~rmk/linux-arm
7e40b56c776f Merge branch 'fixes' of git://git.armlinux.org.uk/~rmk/linux-arm
'''
那么,我使用如下命令:
yubo@debian:~/git/linux$ git format-patch -o ~/patch -2 f03d23a34df3 --subject-prefix="PATCH v3" --cover-letter -n --thread=shallow --cc="[email protected]" --to="[email protected]" --to="[email protected]" --cc="[email protected]" --cc="[email protected]"
结果如下所示:
/home/yubo-2/patch/0000-cover-letter.patch
/home/yubo-2/patch/0001-kobject-use-pr_warn-to-replace-printk.patch
/home/yubo-2/patch/0002-kobject-drop-newline-from-msg-string.patch
为了参加一个小比赛,目前需要了解一下linux usb的知识。
A usb endpoint可以是下面四种类型的其中一个。
CONTROL
顾名思义,就是usb中控制设备行为的。通常当一个usb设备插入时有一个”endpoint 0”被用来使用。
INTERRUPT
这个端口每次以固定的大小有usb host向设备请求数据。USB 键盘和鼠标主要使用这个。
BULK
这个endpoint传输大的数据,如果想要数据没有丢失的话,就使用这个 endpoint.例如打印机、存储设备、网络设备。
ISOCHRONOUS
与上面的类似,区别在于不能保证数据会完全通过。
现在的原理还没有彻底高明白, 但是,任何一个想被硬件捕捉的驱动必须有这个数据结构, 像侦测函数没有也是可以的。
static struct usb_device_id hello_id_table[] = {
{ USB_INTERFACE_INFO(USB_INTERFACE_CLASS_HID,
USB_INTERFACE_SUBCLASS_BOOT,
USB_INTERFACE_PROTOCOL_KEYBOARD) },
{ }, /* Here are entry */
};
/* 向内核注册 */
MODULE_DEVICE_TABLE(usb, hello_id_table);
当你把这个数据结构和hello, world模块融合在一起,只要插入了模块,就可以使用udev规则插入模块。
暂时先记录在这里。
如果你的python版本是2.7.x,貌似不用额外安装,另外我的博客里有3.7.x的安装教程。
我是安装的源代码, setuptools, 解压后进入目录使用命令:
python setup.py build
接着进行安装:
python setup.py install
首先下载源代码, 在相应的目录下,有这个文件: setup.py 使用如下命令:
python setup.py install
执行完毕后你就可以看见相关的消息,
Processing dependencies for Django==1.x.y
Finished processing dependencies for Django==1.x.y
使用
python
import django
django.VERSION
就可以看见相关的信息,说明安装成功.
在«Unix/Linux编程实践»中第7章中,介绍了计时器的简单应用。简单的介绍下。
这个计时器是真实计时器,也就是程序不管在用户态还是核心态用了多少处理器时间它都记录, 当这个计时器用尽,发送SIGALRM信号。
这个计时器仅仅记录程序在用户态运行时的空间,类似于我们在篮球比赛中看到的,死球期间的 时间不记录在比赛时间内。这个计时器用完发送SIGVTALRM信号。
这个计时器在这本书里被描绘成睡眠时态(用户+核心态),并不是核心态,(?)当这个计时器用尽, 会发送SIGPROF信号。
函数alarm只能设置到以秒为单位的,想要使用更精确的时间,要精通以上时间计时器的精通
struct itimeival{
struct timeval it_interval; /* next value 重复间隔 */
struct timeval it_value; /* current value 初始时间 */
}
struct timeval {
long tv_sec; /* seconds */
long tv_usec; /* 微妙 */
}
解释一下这个用法。当你在等待公交车的时候,第一辆到站的时间是上午5点半(it_value),以后每隔半小时(it_interval) 就会有一辆公交车准时到达车站。这就是上面的结构体的简单的一句话总结。
请看下面的例子:
```c
#include
/* 信号处理函数 / /
} /**
以 毫秒(milliseconds)为参数,转化为整秒和微妙(microseconds) * */ int set_ticker(int n_msecs) { struct itimerval new_timeset; long n_sec, n_usecs;
n_sec = n_msecs / 1000; /* 整数部分 / n_usecs = (n_msecs % 1000) * 1000L; / 余数部分 */
new_timeset.it_interval.tv_sec = n_sec; /* 设置间隔的整秒 / new_timeset.it_interval.tv_usec = n_usecs; / 设置间隔的微妙 */
new_timeset.it_value.tv_sec = n_sec; /* 第一次到达的时间 / new_timeset.it_value.tv_usec = n_usecs; / 保存 计时值*/
return setitimer(ITIMER_REAL, &new_timeset, NULL);
}