kk Blog —— 通用基础


date [-d @int|str] [+%s|"+%F %T"]
netstat -ltunp
sar -n DEV 1

收包软中断和netif_rx

初始化报文接收软中断
1
2
3
4
5
6
static int __init net_dev_init(void)
{
	......
	open_softirq(NET_RX_SOFTIRQ, net_rx_action);
	......
}
报文接收软中断的处理函数net_rx_action详解:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
static void net_rx_action(struct softirq_action *h)
{
	/*取得本地cpu 的softnet_data 的poll_list  链表*/
	struct list_head *list = &__get_cpu_var(softnet_data).poll_list;
	/*设置软中断处理程序一次允许的最大执行时间为2个jiffies*/
	unsigned long time_limit = jiffies + 2;

	/*设置软中断接收函数一次最多处理的报文个数为 300 */
	int budget = netdev_budget;
	/*关闭本地cpu的中断,下面判断list是否为空时防止硬中断抢占*/
	local_irq_disable();
	/*循环处理pool_list 链表上的等待处理的napi*/
	while (!list_empty(list))
	{
		struct napi_struct *n;
		int work, weight;

		/*如果处理报文超出一次处理最大的个数
		  或允许时间超过最大时间就停止执行,
		  跳到softnet_break 处*/
		if (unlikely(budget <= 0 || time_after(jiffies, time_limit)))
		{
			goto softnet_break;
		}
		/*使能本地中断,上面判断list为空已完成,下面调用NAPI
		  的轮询函数是在硬中断开启的情况下执行*/
		local_irq_enable();

		/* 取得softnet_data pool_list 链表上的一个napi,
		   即使现在硬中断抢占软中断,会把一个napi挂到pool_list的尾端
		   软中断只会从pool_list 头部移除一个pool_list,这样不存在临界区*/
		n = list_entry(list->next, struct napi_struct, poll_list);
		/*用weighe 记录napi 一次轮询允许处理的最大报文数*/
		weight = n->weight;
		/* work 记录一个napi总共处理的报文数*/
		work = 0;

		/*如果取得的napi状态是被调度的,就执行napi的轮询处理函数*/
		if (test_bit(NAPI_STATE_SCHED, &n->state))
		{
			work = n->poll(n, weight);
		}
		WARN_ON_ONCE(work > weight);
		/*预算减去已经处理的报文数*/
		budget -= work;
		/*禁止本地CPU 的中断,下面会有把没执行完的NAPI挂到softnet_data
		  尾部的操作,和硬中断存在临界区。同时while循环时判断list是否
		  为空时也要禁止硬中断抢占*/
		local_irq_disable();

		/*如果napi 一次轮询处理的报文数正好等于允许处理的最大数,
		  说明一次轮询没处理完全部需要处理的报文*/
		if (unlikely(work == weight))
		{
			/*如果napi已经被禁用,就把napi 从 softnet_data 的pool_list 上移除*/
			if (unlikely(napi_disable_pending(n)))
			{
				local_irq_enable();
				napi_complete(n);
				local_irq_disable();
			}
			else
			{
				/*否则,把napi 移到 pool_list 的尾端*/
				list_move_tail(&n->poll_list, list);
			}
		}
	}
out:
	local_irq_enable();
	return;

	/*如果处理时间超时,或处理的报文数到了最多允许处理的个数,
	  说明还有napi 上有报文需要处理,调度软中断。
	  否则,说明这次软中断处理完全部的napi上的需要处理的报文,不再需要
	  调度软中断了*/
softnet_break:
	__get_cpu_var(netdev_rx_stat).time_squeeze++;
	__raise_softirq_irqoff(NET_RX_SOFTIRQ);
	goto out;
}
虚拟NAPI backlog 的轮询函数process_backlog():

参数:
napi : 本地cpu上softnet_data 的backlog .
quota : 一次轮询可以处理的最多报文数。

函数详解:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
static int process_backlog(struct napi_struct *napi, int quota)
{
	int work = 0;

	/*取得本地CPU上的softnet_data  数据*/
	struct softnet_data *queue = &__get_cpu_var(softnet_data);

	/*开始计时,一旦允许时间到,就退出轮询*/
	unsigned long start_time = jiffies;
	napi->weight = weight_p;

	/*循环从softnet_data 的输入队列取报文并处理,直到队列中没有报文了,
	 或处理的报文数大于了允许的上限值了,
	 或轮询函数执行时间大于一个jiffies 了
	*/
	do
	{
		struct sk_buff *skb;
		/*禁用本地中断,要存队列中取skb,防止抢占*/
		local_irq_disable();

		/*从softnet_data 的输入队列中取得一个skb*/
		skb = __skb_dequeue(&queue->input_pkt_queue);

		/*如果队列中没有skb,则使能中断并退出轮询*/
		if (!skb)
		{
			/*把napi 从 softnet_data 的 pool_list 链表上摘除*/
			__napi_complete(napi);
			/*使能本地CPU的中断*/
			local_irq_enable();
			break;
		}
		/*skb 已经摘下来了,使能中断*/
		local_irq_enable();

		/*把skb送到协议栈相关协议模块进行处理,详细处理见后续章节*/
		netif_receive_skb(skb);
	} while (++work < quota && jiffies == start_time);
	/*返回处理报文个数*/
	return work;
}
linux旧的收包方式提供给驱动的接口netif_rx():
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
int netif_rx(struct sk_buff *skb)
{
	struct softnet_data *queue;
	unsigned long flags;

	/*如果接收skb的时间戳没设定,设定接收时间戳*/
	if (!skb->tstamp.tv64)
	{
		net_timestamp(skb);
	}

	/*禁止本地cpu的中断*/
	local_irq_save(flags);

	/*取得本地cpu的softnet_data*/
	queue = &__get_cpu_var(softnet_data);
				   
	/*每个CPU都有一个统计数据,增加统计数据*/
	__get_cpu_var(netdev_rx_stat).total++;

	/*如果本地CPU的输入队列中的skb 个数小于允许的最多的个数*/
	if (queue->input_pkt_queue.qlen <= netdev_max_backlog)
	{
		/*如果本地cpu的输入队列长度不为0,表示输入队列已经有skb了,
		并且特殊的napi backlog 已经挂入了softnet_data  的
		pool_list上了*/
		if (queue->input_pkt_queue.qlen)
		{
enqueue:
			/*把skb 放入CPU的输入队列 input_pkt_queue*/
			__skb_queue_tail(&queue->input_pkt_queue, skb);
					  
			/*使能中断 并 返回*/
			local_irq_restore(flags);
			return NET_RX_SUCCESS;
		}
		/*如果输入队列为空,则把 特殊的napi backlog 挂到softnet_data
		的 pool_list 上 并返回把skb放入输入队列并返回*/
		napi_schedule(&queue->backlog);
		goto enqueue;
	}
	/*如果本地cpu的输入队列已经满了,则丢弃报文,
	  并增加丢包计数并返回*/
	__get_cpu_var(netdev_rx_stat).dropped++;
	local_irq_restore(flags);

	kfree_skb(skb);
	return NET_RX_DROP;
}

ruby字符串处理函数

1.返回字符串的长度
1
str.length => integer
2.字符串索引index
1
2
3
4
5
6
7
8
9
10
11
12
13
str.index(substring [, offset])   => fixnum or nil
str.index(fixnum [, offset])      => fixnum or nil
str.index(regexp [, offset])      => fixnum or nil

Returns the index of the first occurrence of the given substring, character (fixnum), 
or pattern (regexp) in str. Returns nil if not found. If the second parameter is present, 
it specifies the position in the string to begin the search.

   "hello".index('e')             #=> 1
   "hello".index('lo')            #=> 3
   "hello".index('a')             #=> nil
   "hello".index(101)             #=> 1
   "hello".index(/[aeiou]/, -3)   #=> 4
从尾到头rindex
1
2
3
4
5
6
7
8
9
10
11
12
13
str.rindex(substring [, fixnum])   => fixnum or nil
str.rindex(fixnum [, fixnum])   => fixnum or nil
str.rindex(regexp [, fixnum])   => fixnum or nil

Returns the index of the last occurrence of the given substring, character (fixnum), 
or pattern (regexp) in str. Returns nil if not found. If the second parameter is present, 
it specifies the position in the string to end the search---characters beyond this point will not be considered.

   "hello".rindex('e')             #=> 1
   "hello".rindex('l')             #=> 3
   "hello".rindex('a')             #=> nil
   "hello".rindex(101)             #=> 1
   "hello".rindex(/[aeiou]/, -2)   #=> 1
3.判断字符串中是否包含另一个串
1
2
3
4
str.include? other_str => true or false
"hello".include? "lo"   #=> true
"hello".include? "ol"   #=> false
"hello".include? ?h     #=> true
4.字符串插入
1
2
3
4
5
6
7
str.insert(index, other_str) => str
"abcd".insert(0, 'X')    #=> "Xabcd"
"abcd".insert(3, 'X')    #=> "abcXd"
"abcd".insert(4, 'X')    #=> "abcdX"
"abcd".insert(-3, 'X')
-3, 'X')   #=> "abXcd"
"abcd".insert(-1, 'X')   #=> "abcdX"
5.字符串分隔,默认分隔符为空格
1
2
3
4
5
6
7
8
9
str.split(pattern=$;, [limit]) => anArray
" now's the time".split        #=> ["now's", "the", "time"]
"1, 2.34,56, 7".split(%r{,\s*}) #=> ["1", "2.34", "56", "7"]
"hello".split(//)               #=> ["h", "e", "l", "l", "o"]
"hello".split(//, 3)            #=> ["h", "e", "llo"]
"hi mom".split(%r{\s*})         #=> ["h", "i", "m", "o", "m"]
"mellow yellow".split("ello")   #=> ["m", "w y", "w"]
"1,2,,3,4,,".split(',')         #=> ["1", "2", "", "3", "4"]
"1,2,,3,4,,".split(',', 4)      #=> ["1", "2", "", "3,4,,"]
6.字符串替换
1
2
3
4
5
str.gsub(pattern, replacement) => new_str
str.gsub(pattern) {|match| block } => new_str
"hello".gsub(/[aeiou]/, '*')              #=> "h*ll*"     #将元音替换成*号
"hello".gsub(/([aeiou])/, '<\1>')         #=> "h<e>ll<o>"   #将元音加上尖括号,\1表示保留原有字符???
"hello".gsub(/./) {|s| s[0].to_s + ' '}   #=> "104 101 108 108 111 "

ruby中带“!“和不带”!“的方法的最大的区别就是带”!"的会改变调用对象本身了。比方说str.gsub(/a/, ‘b’),不会改变str本身,只会返回一个新的str。而str.gsub!(/a/, ‘b’)就会把str本身给改了。
但是gsub和gsub!还有另外一个不同点就是,gsub不管怎么样都会返回一个新的字符串,而gsub!只有在有字符被替换的情况下才会返回一个新的字符串,假如说没有任何字符被替换,gsub!只会返回nil.

字符串替换二:
1
2
3
str.replace(other_str) => str
s = "hello"         #=> "hello"
s.replace "world"   #=> "world"
7.字符串删除:
1
2
3
4
5
str.delete([other_str]+) => new_str
"hello".delete "l","lo"        #=> "heo"
"hello".delete "lo"            #=> "he"
"hello".delete "aeiou", "^e"   #=> "hell"
"hello".delete "ej-m"          #=> "ho"
8.去掉前和后的空格
1
2
3
str.lstrip => new_str
" hello ".lstrip   #=> "hello "
"hello".lstrip       #=> "hello"
9.字符串匹配
1
str.match(pattern) => matchdata or nil
10.字符串反转
1
2
str.reverse => new_str
"stressed".reverse   #=> "desserts"
11.去掉重复的字符
1
2
3
4
str.squeeze([other_str]*) => new_str
"yellow moon".squeeze                  #=> "yelow mon" #默认去掉串中所有重复的字符
" now   is the".squeeze(" ")         #=> " now is the" #去掉串中重复的空格
"putters shoot balls".squeeze("m-z")   #=> "puters shot balls" #去掉指定范围内的重复字符
12.转化成数字
1
2
str.to_i=> str
"12345".to_i             #=> 12345

ruby基础

打印一个完整的回溯

1
puts caller

Find

http://ruby-doc.org/stdlib-1.9.3/libdoc/find/rdoc/Find.html

1
2
3
4
5
6
7
8
9
10
11
12
13
require 'find'
total_size = 0
Find.find(ENV["HOME"]) do |path|
  if FileTest.directory?(path)
    if File.basename(path)[0] == ?.
      Find.prune       # Don't look any further into this directory.
    else
      next
    end
  else
    total_size += FileTest.size(path)
  end
end

Time

1
2
3
4
p Time.parse(“2002-03-17”)       #=> Sun Mar 17 00:00:00 +0800[v2] 2002
p Time.now        # =>Mon Oct 20 06:02:10 JST 2003
p Time.now.to_a      # => [10, 2, 6, 20, 10, 2003, 1, 293,false, "JST"]
p Time.now.to_f      # => 1418540681.0154862