十年网站开发经验 + 多家企业客户 + 靠谱的建站团队
量身定制 + 运营维护+专业推广+无忧售后,网站问题一站解决
背景:
10年积累的网站建设、做网站经验,可以快速应对客户对网站的新想法和需求。提供各种问题对应的解决方案。让选择我们的客户得到更好、更有力的网络服务。我虽然不认识你,你也不认识我。但先网站设计后付款的网站建设流程,更有潞州免费网站建设让你可以放心的选择与我们合作。
内核接收分组的方式有两种:第一种:传统方式,使用中断的方式;第二种:NAPI,使用中断和轮询结合的方式。
中断方式:
下图为一个分组到达NIC之后,该分组穿过内核到达网络层函数的路径。
此图的下半部分为中断处理,上半部分为软中断。在中断处理中,函数net_interupt是
设备驱动程序的中断处理程序,它将确认此中断是否由接收到分组引发的,如果确实如此,
则控制权移交到函数net_rx。函数net_rx也是特定于NIC,首先创建一个新的套接字缓冲区,
分组的内容接下来从NIC传输到缓冲区(进入到物理内存中),然后使用内核源码中针对
各个传输类型的库函数来分析首部的数据。函数netif_rx函数不是特定于网络驱动函数,该
函数位于net/core/dev.c,调用该函数,标志着控制由特定于网卡的代码转移到了网络层的
通用接口部分。此函数的作用在于,将接收分组放置到一个特定于CPU的等待队列上,并
退出中断上下文。内核使用softnet_data来管理进出流量,其定义入下:
2352 /*
2353 * Incoming packets are placed on per-cpu queues
2354 */
2355 struct softnet_data {
2356 struct list_head poll_list;
2357 struct sk_buff_head process_queue;
2358
2359 /* stats */
2360 unsigned int processed;
2361 unsigned int time_squeeze;
2362 unsigned int cpu_collision;
2363 unsigned int received_rps;
2364 #ifdef CONFIG_RPS
2365 struct softnet_data *rps_ipi_list;
2366 #endif
2367 #ifdef CONFIG_NET_FLOW_LIMIT
2368 struct sd_flow_limit __rcu *flow_limit;
2369 #endif
2370 struct Qdisc *output_queue;
2371 struct Qdisc **output_queue_tailp;
2372 struct sk_buff *completion_queue;
2373
2374 #ifdef CONFIG_RPS
2375 /* Elements below can be accessed between CPUs for RPS */
2376 struct call_single_data csd ____cacheline_aligned_in_smp;
2377 struct softnet_data *rps_ipi_next;
2378 unsigned int cpu;
2379 unsigned int input_queue_head;
2380 unsigned int input_queue_tail;
2381 #endif
2382 unsigned int dropped;
2383 struct sk_buff_head input_pkt_queue; //对所有进入分组建立一个链表。
2384 struct napi_struct backlog;
2385
2386 };
第2383行的input_pkt_queue即是CPU的等待队列。netif_rx的实现如下:
3329 static int netif_rx_internal(struct sk_buff *skb)
3330 {
3331 int ret;
3332
3333 net_timestamp_check(netdev_tstamp_prequeue, skb);
3334
3335 trace_netif_rx(skb);
3336 #ifdef CONFIG_RPS //RPS 和 RFS 相关代码
3337 if (static_key_false(rps_needed)) {
3338 struct rps_dev_flow voidflow, *rflow = voidflow;
3339 int cpu;
3340
3341 preempt_disable();
3342 rcu_read_lock();
3343
3344 cpu = get_rps_cpu(skb-dev, skb, rflow); //选择合适的CPU id
3345 if (cpu 0)
3346 cpu = smp_processor_id();
3347
3348 ret = enqueue_to_backlog(skb, cpu, rflow-last_qtail);
3349
3350 rcu_read_unlock();
3351 preempt_enable();
3352 } else
3353 #endif
3354 {
3355 unsigned int qtail;
3356 ret = enqueue_to_backlog(skb, get_cpu(), qtail); //将skb入队
3357 put_cpu();
3358 }
3359 return ret;
3360 }
3361
3362 /**
3363 * netif_rx - post buffer to the network code
3364 * @skb: buffer to post
3365 *
3366 * This function receives a packet from a device driver and queues it for
3367 * the upper (protocol) levels to process. It always succeeds. The buffer
3368 * may be dropped during processing for congestion control or by the
3369 * protocol layers.
3370 *
3371 * return values:
3372 * NET_RX_SUCCESS (no congestion)
3373 * NET_RX_DROP (packet was dropped)
3374 *
3375 */
3376
3377 int netif_rx(struct sk_buff *skb)
3378 {
3379 trace_netif_rx_entry(skb);
3380
3381 return netif_rx_internal(skb);
3382 }
入队函数 enqueue_to_backlog的实现如下:
View Code
NAPI
NAPI是混合了中断和轮询机制,当一个新的分组到达,而前一个分组依然在处理,这时内核
并不需要产生中断,内核会继续处理并在处理完毕后将中断开启。这样内核就利用了中断和轮询的好处,
只有有分组到达的时候,才会进行轮询。
NAPI存在两个优势
1.减少了CPU使用率,因为更少的中断。
2.处理各种设备更加公平
只有设备满足如下两个条件时,才能实现NAPI:
1.设备必须能够保留多个接收的分组
2.设备必须能够禁用用于分组接收的IRQ。而且,发送分组或其他可能通过IRQ进行的操作,都仍然
必须是启用的。
其运行概览如下:
如上所示,各个设备在进入poll list前需要禁用IRQ,而设备中的分组都处理完毕后重新开启IRQ。poll list使用
napi_struct来管理设备,其定义如下:
View Code
变量state可以为NAPI_STATE_SCHED或NAPI_STATE_DISABLE, 前者表示设备将在
内核的下一次循环时被轮询,后者表示轮询已经结束且没有更多的分组等待处理,但
设备并没有从poll list移除。
支持NAPI的NIC需要修改中断处理程序,将此设备放置在poll list上。示例代码如下:
View Code
函数__napi_schedule的定义入下:
View Code
设备除了对中断处理进行修改,还需要提供一个poll函数,使用此函数从NIC中获取分组。示例代码如下:
特定于硬件的方法 hyper_do_poll 从NIC中获取分组,返回值work_done为处理的分组数目。当分组处理完后,
会调用netif_rx_complete将此设备从poll list中移除。ixgbe网卡的poll函数为ixgbe_poll, 而对于非NAPI的函数,
内核提供默认的处理函数process_backlog。
软中断处理相关
无论是NAPI接口还是非NAPI最后都是使用 net_rx_action 作为软中断处理函数。因此整个流程如下:
上图中有些函数名已经发生变更,但是流程依然如此。在最新的3.19内核代码中,非NAPI的调用流程如下:
neif_rx会调用enqueue_to_backlog 将skb存入softnet_data,并调用____napi_schedule函数。
netif_rx===netif_rx_internal===enqueue_to_backlog===____napi_schedule===net_rx_action===process_backlog===__netif_receive_skb
e100网卡的NAPI调用流程入下:
e100_intr===__napi_schedule===net_rx_action===e100_poll===e100_rx_clean===e100_rx_indicate===netif_receive_skb
System.DateTime currentTime=new System.DateTime();
string str="";
int h=currentTime.Hour;
if(h12)
{
str="下午";
}
else
{
str="上午";
}
1、DateTime 数字型
System.DateTime currentTime=new System.DateTime();
1.1 取当前年月日时分秒
currentTime=System.DateTime.Now;
1.2 取当前年
int 年=currentTime.Year;
1.3 取当前月
int 月=currentTime.Month;
1.4 取当前日
int 日=currentTime.Day;
1.5 取当前时
int 时=currentTime.Hour;
1.6 取当前分
int 分=currentTime.Minute;
1.7 取当前秒
int 秒=currentTime.Second;
1.8 取当前毫秒
int 毫秒=currentTime.Millisecond;
(变量可用中文)
1.9 取中文日期显示——年月日时分
string strY=currentTime.ToString("f"); //不显示秒
1.10 取中文日期显示_年月
string strYM=currentTime.ToString("y");
1.11 取中文日期显示_月日
string strMD=currentTime.ToString("m");
1.12 取中文年月日
string strYMD=currentTime.ToString("D");
1.13 取当前时分,格式为:14:24
string strT=currentTime.ToString("t");
1.14 取当前时间,格式为:2003-09-23T14:46:48
string strT=currentTime.ToString("s");
1.15 取当前时间,格式为:2003-09-23 14:48:30Z
string strT=currentTime.ToString("u");
1.16 取当前时间,格式为:2003-09-23 14:48
string strT=currentTime.ToString("g");
1.17 取当前时间,格式为:Tue, 23 Sep 2003 14:52:40 GMT
string strT=currentTime.ToString("r");
1.18获得当前时间 n 天后的日期时间
DateTime newDay = DateTime.Now.AddDays(100);