3.1 HZ

节拍率(HZ)是时钟中断的频率,表示的一律秒内时钟中断的次数。

遵照 HZ=100 表示一致秒内接触发100软时钟中断程序。

 

HZ的价一般和系统布局有关,x86 连串布局相似定义也 100,参考文件
include/asm-generic/param.h

HZ值的分寸的设置过程实际上就是是平衡 精度和属性 的历程,并无是HZ值越强更好。

HZ值

优势

劣势

高HZ 时钟中断程序运行的更加频繁,依赖时间执行的程序更加精确,
对资源消耗和系统运行时间的统计更加精确。
时钟中断执行的频繁,增加系统负担
时钟中断占用的CPU时间过多

 

另外,有好几待专注,内核中采取的HZ可能和用户空间中定义之HZ值不雷同,为了制止用户空间得到错误的时刻,

基础中呢定义了 USER_HZ,即用户空间利用的HZ值。

貌似的话,USER_HZ 和 HZ 都是离开整数加倍,内核中经函数
jiffies_to_clock_t 来将基本来拿基本中之 jiffies转为 用户空间
jiffies

/* 参见文件: kernel/time.c  *
//*
 * Convert jiffies/jiffies_64 to clock_t and back.
 */
clock_t jiffies_to_clock_t(unsigned long x)
{
#if (TICK_NSEC % (NSEC_PER_SEC / USER_HZ)) == 0
# if HZ < USER_HZ
    return x * (USER_HZ / HZ);
# else
    return x / (HZ / USER_HZ);
# endif
#else
    return div_u64((u64)x * TICK_NSEC, NSEC_PER_SEC / USER_HZ);
#endif
}
EXPORT_SYMBOL(jiffies_to_clock_t);

 

后面几龙看到园子里一样首关于 Url
重写的稿子《获取ISAPI_Rewrite重写后的URL》
URL-Rewrite
那项技艺一度不是同等宗新技巧了,这些话题为都给广大总人口谈论了频繁。搜索一下URL-Rewrite可以找到多URL-Rewrite方面的章与零部件,自己先为再三触及过之东东,也来说说吧。
斯考特(Scott)Gu 有同一首非凡经典的 URL-Rewrite Blog
Tip/Trick: Url Rewriting with
ASP.NET

http://weblogs.asp.net/scottgu/archive/2007/02/26/tip-trick-url-rewriting-with-asp-net.aspx

3. 定时器相关概念

定时器的施用受到,下边3单概念特别重要:

  1. HZ
  2. jiffies
  3. 日暂停处理程序

 

ranking.

 
第一沾原因备受所讲述的景,在Web站点改版中时时遭逢。Web站点改版经常会见调动片页面的职位,QueryString中参数的结构等等。很可能而原来用户以藏夹着珍藏的链接成为死链。在这种气象下URL-Rewrite像是软件架构技术被的一个中间层的概念,URL-Rewrite对外公开之URL是为重复写过的,这些URL被用户收藏,不碰面转换,当Web站点调整,内部Page的职位变动了,使得中实际的URL地址为转移了,这时修改中的重写规则,让本来对外公开的URL重写及新的内URL上。从而保证了对外URL不换,其实对内已经就了页面地点的调。即便URL-Rewrite可以就防微杜渐死链的有,可是多数站点在改版或调整时,不碰面以URL-Rewrite来防范死链的发,一般会一向改动404
The page cannot be found
页面,把404发生错页面改成为一个更为融洽之提醒页面,并且会在几乎分钟后越反至网站首页。

  第二碰原因是SEO了,假使您的站点是单里头OA ERP
CRM这种站点,只需要好内部人士来拜访。其实全可不要做SEO,因为这种站点从不待寻找引擎来用,也不需要旁人通过搜索引擎找到这站点,所以那种站点全没必要开展SEO优化。虽然您的站点是单商贸站点,消息站点,娱乐站点,越两人口走访进一步好的站点,SEO优化是异常关键,此时透过URL-Rewrite举办SEO优化也就生必要了。随着搜索引擎渐渐成为众人找信息,索取资源的首选工具,搜索引擎对一个站点的熏陶为纵然愈好,下面是
zhangsichu.com 9-1~9-10 这段时间内的老三正在来程数据总括。

统计 1

来程总括是经过记录httpheader中的Referer,来得知用户以浏览这么些页面往日所当的卓殊页面。从而得出用户是由此杀页面到达这么些页面的。
每当266单独立IP中,有200只IP是自搜索引擎的。也就是说,用户优先经查找引擎的搜索结果,然后来zhangsichu.com的用户暴发200只。占及了75.2%。一大半底食指是通过查找来之。充裕表达了SEO对站点的关键,在这种意况下,就亟须做URL-Rewrite进行SEO优化了。

 
倘您的站点既无欲考虑URL兼容避免死链问题,也未待展开SEO优化,就完全没必要举行URL-Rewrite。URL-Rewrite是一个对准性有害的处理过程。

常用的URL-Rewrite方案
URL-Rewrite既好有在Web服务器(IIS/Apache)一流,也可出在Web应用程序一级(Asp.Net/Jsp/PHP/…)。

 
1.Web应用程序级其它URL-Rewrite
  于Web应用程序级另外URL-Rewrite。有多少个相比知名的现成组件。
  1) 微软供的 URL-Rewrite
http://msdn2.microsoft.com/zh-cn/library/ms972974.aspx
  2) Open Source的 UrlRewriter.NET http://urlrewriter.net/
  3) UrlRewriting http://www.urlrewriting.net/en/Download.aspx

这种组件内部基本之干活原理: 都是当投机的Web
Application的web.config中上加httpModule。用这httpModule来拍卖重写。(其实也只是连续System.Web.HttpApplication,在Application_BeginRequest中插入一个祥和之道处理重写)

内部基本的拍卖代码,下边的代码摘引自UrlRewriter.NET组件。
 
1)从IHttpModule继承得到一个要好之HttpModule,这么些HttpModule需要以web.config中配置,表达所有的央浼都要透过这一个HttpModule。

public sealed class RewriterHttpModule : IHttpModule
  {
    /// <summary>
    /// Initialises the module.
    /// </summary>
    /// <param name="context">The application context.</param>
    void IHttpModule.Init(HttpApplication context)
    {
      context.BeginRequest += new EventHandler(BeginRequest);
    }
…
private void BeginRequest(object sender, EventArgs e)
    {
      // Add our PoweredBy header
      HttpContext.Current.Response.AddHeader(Constants.HeaderXPoweredBy, Configuration.XPoweredBy);

      _rewriter.Rewrite();
    }
}

 

2)读取重写规则,判断是否要重写,确定咋样重写,举行重写。

 

public void Rewrite()
    {
      string originalUrl = ContextFacade.GetRawUrl().Replace("+", " ");
      RawUrl = originalUrl;

      // Create the context
      RewriteContext context = new RewriteContext(this, originalUrl,
        ContextFacade.GetHttpMethod(), ContextFacade.MapPath,
        ContextFacade.GetServerVariables(), ContextFacade.GetHeaders(), ContextFacade.GetCookies());

      // Process each rule.
      ProcessRules(context);

      // Append any headers defined.
      AppendHeaders(context);

      // Append any cookies defined.
      AppendCookies(context);

      // Rewrite the path if the location has changed.
      ContextFacade.SetStatusCode((int)context.StatusCode);
      if ((context.Location != originalUrl) && ((int)context.StatusCode < 400))
      {
        if ((int)context.StatusCode < 300)
        {
          // Successful status if less than 300
          _configuration.Logger.Info(MessageProvider.FormatString(Message.RewritingXtoY, 
            ContextFacade.GetRawUrl(), context.Location));

          // Verify that the url exists on this server.
          HandleDefaultDocument(context);// VerifyResultExists(context);

          ContextFacade.RewritePath(context.Location);
        }
        else
        {
          // Redirection
          _configuration.Logger.Info(MessageProvider.FormatString(Message.RedirectingXtoY,
            ContextFacade.GetRawUrl(), context.Location));

          ContextFacade.SetRedirectLocation(context.Location);
        }
      }
      else if ((int)context.StatusCode >= 400)
      {
        HandleError(context);
      }
      else if (HandleDefaultDocument(context))
      {
        ContextFacade.RewritePath(context.Location);
      }

      // Sets the context items.
      SetContextItems(context);
    }

 

那种重写是ASP.NET
Pipeline级其它重写,可以再度写满Asp.net接管的央浼。

 

统计 2

 

以此地针对/Pd/Book.aspx的伸手于再一次写及了 /Pd.aspx?Cg=books.
Web应用程序级别之URL-Rewrite只能重新写Web应用程序接管的呼吁。它并未法处理.js
.jpg的重写。原因是这多少个请求到达IIS后,IIS根本就从未管这一个请求分发到Asp.Net,所以这一个请求虽未会师发更写的拍卖同操作。在IIS中得安排,对怎么后缀的乞求是吃IIS分发及Asp.Net的。

 

统计 3

 

只要您肯定要在Asp.Net级别对.js的请求举行重写,可以于此指定.js的哀告由Asp.Net接管,然而这你得自己处理.js的Response。Web服务器级此外URL-Rewrite可以比好之解决当时地点的题目吧。

2. Web服务器级其它URL-Rewrite

 

Apache服务器
Apache服务器原生补助了URL-Rewrite。在config中开辟LoadModule
rewrite_module modules/mod_rewrite.so 然后安排更写的正则表明式。例如:

引用自Apache2.2中文参考手册
统计,中文手册
Apache-UrlRewrite

---------------------------------------------
描述: 
这个规则的目的是强制使用特定的主机名以代替其他名字。比如,你想强制使用www.example.com代替example.com,就可以在以下方案的基础上进行修改: 
解决方案: 
对运行在非80端口的站点

RewriteCond %{HTTP_HOST} !^fully\.qualified\.domain\.name [NC]
RewriteCond %{HTTP_HOST} !^$
RewriteCond %{SERVER_PORT} !^80$
RewriteRule ^/(.*) http://fully.qualified.domain.name:%{SERVER_PORT}/$1 [L,R]

对运行在80端口的站点

RewriteCond %{HTTP_HOST} !^fully\.qualified\.domain\.name [NC]
RewriteCond %{HTTP_HOST} !^$
RewriteRule ^/(.*) http://fully.qualified.domain.name/$1 [L,R]
---------------------------------------------------------------------------

 

IIS6/IIS7 Web服务器
IIS7初的“管道形式”其实是把ASP.NET中的少数概念与IIS举办了进一步深的并轨。在IIS7
Program Manager: 麦克 Volodarsky的Blog中有一致篇随笔分析了即面的情节:
Breaking Changes for ASP.NET 2.0 applications running in Integrated
mode on IIS
7.0

 

IIS7的“经典形式”与IIS 6基本上是若爆发一致措施的。

当IIS6 +
Asp.Net应用程序级的URL-Rewrite,只可以当求于分配至Asp.Net引擎后才可以出重写操作。在IIS7随即无异于沾给转移了。IIS7可以针对没后缀名的要举行重写,Asp.Net和IIS7举办了纵深的并。IIS7可以于
IIS
请求管道的此外地点实施一个HttpModule,上面是一个IIS7下蛋Asp.Net的重写配置:

摘引自ScottGu的Blog

 

<?xml version="1.0" encoding="UTF-8"?>

<configuration>

<configSections>
<section name="rewriter" 
requirePermission="false" 
type="Intelligencia.UrlRewriter.Configuration.RewriterConfigurationSectionHandler, Intelligencia.UrlRewriter" />
</configSections>

<system.web>

<httpModules>
<add name="UrlRewriter" type="Intelligencia.UrlRewriter.RewriterHttpModule, Intelligencia.UrlRewriter" />
</httpModules>

  </system.web>

<system.webServer>

<modules runAllManagedModulesForAllRequests="true">
<add name="UrlRewriter" type="Intelligencia.UrlRewriter.RewriterHttpModule" />
</modules>

<validation validateIntegratedModeConfiguration="false" />

</system.webServer>

<rewriter>
<rewrite url="~/products/(.+)" to="~/products.aspx?category=$1" />
</rewriter>

</configuration>

 

其中:<rewrite url=”~/products/(.+)” to=”~/products.aspx?category=$1″
/>这漫长规则中之~/products/(.+)这长达正则表明式。匹配了/products/下的备链接。
IIS6服务器级别下的重写需要动用ISAPI Filters Rewrite来贯彻。

ISAPI Filters有半点独坏资深工程:
  1)Helicon Techs ISAPI Rewrite: http://www.isapirewrite.com/
提供一个99英镑(可免费试用30天)的ISAPI
URL重写产品完全版本,以及一个免费的轻量级版本。
  2)Ionics ISAPI Rewrite: http://cheeso.members.winisp.net/IIRF.aspx
全免费开源组件。
  在 ISAPI
Filter编程重写URL

中发出征。

服务器级的重写与应用程序级的重写最深的区分在于他们发的时不比。下图是当劳务器级把/Pd/Book.aspx重写及/Pd.aspx?Cg=books

 

 

统计 4

 

告还并未交Asp.Net引擎,就为重新写了。

3.Asp.Net级别达到重新写的片段略带细节问题(部分内容源自司各脱(Scott)(Scott)Gu 的Blog)
  即使页面被有form且form是runat=server的<form
runat=”server”>,那么这页面在更写后form的action是原始URL,不是再一次写后彻底的URL。例如/Pd/Book.aspx重写及/Pd.aspx?Cg=books这一个情景。实际用户浏览器访问的地方是/Pd/Book.aspx,在服务器级被另行写后请变成了/Pd.aspx?Cg=books,在这种境况下form的action会为render成/Pd.aspx?Cg=books,其实这更加缅怀假设action被render成/Pd/Book.aspx,让页面PostBack到平等职位。在好几情形下action被render成/Pd.aspx?Cg=books是休谋面指向正规干活暴发影响的,只要/Pd.aspx?Cg=books不叫再写规则匹配上,/Pd.aspx?Cg=books会为正确发回去Asp.Net引擎。可是浏览器上之地方栏会变化,暴暴露真正的地址。假使是URL被有分别的条条框框匹配,这就得要求form的action被科学的Render成/Pd/Book.aspx,这种统一的重写后底URL。

解决办法:
  1)自己包装form控件。把url写在某某hidden
field里与postback一起回去,render时修改action为hidden field里的url.
  2)使用JavaScript在form submit前修改action例如
window.document.forms[0].action = window.location;
  3)使用ASP.NET 2.0 Control Adapter(源自ScottGu 的Blog)
 
这种重写是当以使Asp.Net应用程序一级的重写时,使用Context.Request.RawUrl填写form的action,当用IIS应用服务器顶尖的重写时拿干净的URL记录在Request.ServerVariables[“HTTP_X_REWRITE_URL”]中,使用Request.ServerVariables[“HTTP_X_REWRITE_URL”]填写form的action,填写form
action 的历程都是因而Control Adapter对form Control增添,override
form控件的 WriteAttribute方法,在Render时又指定form的action。

着力源代码
摘引自斯科特(Scott)Gu的Blog

统计 5

 

重写后路径兼容问题
 
以/Pd/Book.aspx重写到/Pd.aspx?Cg=books的场景中,页面中假如有相对地方的资源,如有只img的src=”../logo.gif”或src=”logo.gif”。这时浏览器请求这么些资源条件的职位是/pd/也就是说src=”../logo.gif”请求的途径是/logo.gif,src=”logo.gif”请求的门径是/pd/logo.gif。然而实际上这个资源的尺度地方是
/ 因为原本的URL是/Pd.aspx?Cg=books。这时便会发资源找不至的意况。
  1)使用劳务器端的img使用 ~ 路径可以缓解者问题(源自司各脱(Scott)Gu的Blog)。
  2)使用<base href=” http://xxx/
“></base>标签,这些标签需要写于head里。告诉页面,页面中有所相对路径的格路径是
http://xxx/ ,从而缓解重复写后路径失效的题材。
  base标签的征: http://www.w3school.com.cn/tags/tag_base.asp

 
到此地,URL-Rewrite的题材琢磨截止了。在骨子里项目面临必定还会赶上各类不同之题材,可是解决的思路,估摸会是地点这个技巧的咬合以及扩大,希望通过下面对URL-Rewrite问题之商量,会对撞的初题材会于及部分援的意图。

 

 

作者:葡萄城控件技术团队.Zhang Sichu

职称:Web解决方案专家

 

摘引自ScottGu Blog 的原文

Why does URL mapping and rewriting matter?
The most common scenarios where developers want greater flexibility with
URLs are:
1) Handling cases where you want to restructure the pages within your
web application, and you want to ensure that people who have bookmarked
old URLs dont break when you move pages around. Url-rewriting enables
you to transparently forward requests to the new page location without
breaking browsers.
2) Improving the search relevancy of pages on your site with search
engines like Google, Yahoo and Live. Specifically, URL Rewriting can
often make it easier to embed common keywords into the URLs of the pages
on your sites, which can often increase the chance of someone clicking
your link. Moving from using querystring arguments to instead use fully
qualified URLs can also in some cases increase your priority in search
engine results. Using techniques that force referring links to use the
same case and URL entrypoint (for example: weblogs.asp.net/scottgu
instead of weblogs.asp.net/scottgu/default.aspx) can also avoid diluting
your pagerank across multiple URLs, and increase your search results.
In a world where search engines increasingly drive traffic to sites,
extracting any little improvement in your page ranking can yield very
good ROI to your business. Increasingly this is driving developers to
use URL-Rewriting and other SEO (search engine optimization) techniques
to optimize sites (note that SEO is a fast moving space, and the
recommendations for increasing your search relevancy evolve monthly).
For a list of some good search engine optimization suggestions, Id
recommend reading the SSW Rules to Better Google Rankings, as well as
MarketPositions article on how URLs can affect top search engine

1.1  实际时间

实际时间即是实际中钟表上显得的时光,其实根本中连无常用之日子,首如果用户空间的次有时需要取得当前光阴,

用基本中为管理在是日子。

 

实质上时间之得是于开机后,内核起首化时从RTC读取的。

本读取那些日子后即使用这放入内核中之 xtime
变量中,并且以系统的运转中不断更新那多少个价。

注:RTC就是实时时钟的缩写,它是故来存放在系统时之装置。一般和BIOS一样,由主板及之电池组供电的,所以就是关机吗只是拿日保存。

 

骨子里时间存放的变量 xtime 在文件 kernel/time/timekeeping.c中。

/* 按照16位对齐,其实就是2个long型的数据 */
struct timespec xtime __attribute__ ((aligned (16)));

/* timespec结构体的定义如下, 参考 <linux/time.h>  */
struct timespec {
    __kernel_time_t    tv_sec;            /* seconds */
    long        tv_nsec;        /* nanoseconds */
};

/* _kernel_time_t 定义如下 */
typedef long        __kernel_time_t;

 

系统读写 xtime 时用之虽是逐一锁。

/* 写入 xtime 参考 do_sometimeofday 方法 */
int do_settimeofday(struct timespec *tv)
{
/* 省略 。。。。 */
    write_seqlock_irqsave(&xtime_lock, flags); /* 获取写锁 */
/* 更新 xtime */
    write_sequnlock_irqrestore(&xtime_lock, flags); /* 释放写锁 */
/* 省略 。。。。 */
    return 0;
}

/* 读取 xtime 参考 do_gettimeofday 方法 */
void do_gettimeofday(struct timeval *tv)
{
    struct timespec now;

    getnstimeofday(&now); /* 就是在这个方法中获取读锁,并读取 xtime */
    tv->tv_sec = now.tv_sec;
    tv->tv_usec = now.tv_nsec/1000;
}

void getnstimeofday(struct timespec *ts)
{
/* 省略 。。。。 */

/* 顺序锁中读锁来循环获取 xtime,直至读取过程中 xtime 没有被改变过 */
    do {
        seq = read_seqbegin(&xtime_lock);

        *ts = xtime;
        nsecs = timekeeping_get_ns();

        /* If arch requires, add in gettimeoffset() */
        nsecs += arch_gettimeoffset();

    } while (read_seqretry(&xtime_lock, seq));
/* 省略 。。。。 */
}

上述现象被,写锁得要先行为读锁(因为 xtime
必须即刻更新),而且写锁的使用者很少(一般唯有系统定期更新xtime的线程需要具备这么些锁)。

立正是 顺序锁的接纳场景。

 

胡要举行URL-Rewrite
司各脱(Scott)Gu的blog中被起了片只根本的原故:
1.保证WebApplication在展开结构调整,移动页面地点时,用户收藏之URL不会见坐是要改为死链。
2. SEO优化。

3.2 jiffies

jiffies用来记录由系统启动以来发出的总节拍数。比如系统启动了 N 秒,那么
jiffies就吧 N×HZ

jiffies的连锁定义参考头文件 <linux/jiffies.h> 
include/linux/jiffies.h

/* 64bit和32bit的jiffies定义如下 */
extern u64 __jiffy_data jiffies_64;
extern unsigned long volatile __jiffy_data jiffies;

 

采用定时器时一般都是因jiffies为单位来延缓程序执行的,比如延迟5只点子后实施的话,执行时间哪怕是
jiffies+5

32个之jiffies的太要命价值也 2^32-1,在利用时有可能相会油可是生回绕的题材。

本上面的代码:

unsigned long timeout = jiffies + HZ/2; /* 设置超时时间为 0.5秒 */

while (timeout < jiffies)
{
    /* 还没有超时,继续执行任务 */
}

/* 执行超时后的任务 */

健康状态下,上边的代码没有问题。当jiffies接近最可怜价值的早晚,就会师面世回绕问题。

由于是unsinged
long
系列,所以jiffies达到最好特别价值后会合化为0然后再次逐月变丰盛,如下图所示:

统计 6

 

故而当上述的循环代码中,会晤世如下情状:

统计 7

  1. 循环中第一破相比较常,jiffies = J1,没有过期
  2. 循环中第二潮相比常,jiffies =
    J2,实际已过了,可是出于jiffies超越的极致特别价值后又从0起头,所以J2悠远低于timeout
  3. while循环会执行好充裕时(> 2^32-1
    单点子)不会合了,几乎卓殊给死循环了

 

为了避让回扰的问题,可以使用<linux/jiffies.h>头文件中提供的
time_aftertime_before等宏

#define time_after(a,b)        \
    (typecheck(unsigned long, a) && \
     typecheck(unsigned long, b) && \
     ((long)(b) - (long)(a) < 0))
#define time_before(a,b)    time_after(b,a)

#define time_after_eq(a,b)    \
    (typecheck(unsigned long, a) && \
     typecheck(unsigned long, b) && \
     ((long)(a) - (long)(b) >= 0))
#define time_before_eq(a,b)    time_after_eq(b,a)

上述代码的规律其实就是是以 unsigned long 类型转换为 long
类型来避免回扰带来的荒谬,

long 类型超越最可怜价值时变方向如下:

统计 8

 

long 型的数码的回旋会并发在 2^31-1 变为 -2^32 的早晚,如下图所示:

统计 9

  1. 先是不成比时,jiffies = J1,没有过
  2. 第二坏比常,jiffies = J2,一般 J2 是负数
    理论上 (long)timeout – (long)J2 = 正数 – 负数 = 正数(result)
    可是,这么些正数(result)一般会高于 2^31 –
    1,所以long型的result又有了相同糟回绕,变成了负数。
    除非timeout和J2之间的区间 > 2^32
    单点子,result的值才会也正数(注1)。

注1:result的值吗正数时,必须是于result的值
小于 2^31-1 的情下,大于 2^31-1 会出回绕。

统计 10

达成图中 X + Y 表示timeout 和 J2之间通过的节拍数。

result 小于 2^31-1 ,也就是 timeout – J2 < 2^31 – 1

timeout 和 -J2
表示的板数如达到图所著。(因为J2凡负数,所有-J2意味着达成图所示范围的价值)

因为 timeout + X + Y – J2 = 2^31-1 + 2^32

所以 timeout – J2 < 2^31 – 1 时, X + Y > 2^32

也就是说,当timeout和J2之间通过至少 2^32
只点子后,result才可能变成正数。

timeout和J2之间相距这么多节拍是免可能的(不信仰可以为此HZ将这些点子换算成秒就清楚了。。。)

 

利用time_after宏就好巧妙的免回绕带来的超时判断问题,将事先的代码改化如下代码即可:

unsigned long timeout = jiffies + HZ/2; /* 设置超时时间为 0.5秒 */

while (time_after(jiffies, timeout))
{
    /* 还没有超时,继续执行任务 */
}

/* 执行超时后的任务 */

 

  • 网时
  • 定时器
  • 定时器相关概念
  • 定时器执行流程
  • 实现程序延迟的章程
  • 定时器和延缓的例证

重在内容:

1.2 定时器

定时器是水源中要接纳的日管理方法,通过定时器,可以有效之调度程序的施行。

动态定时器是水源中动用于多之定时器,上面要商量的啊是动态定时器。

 

4.1 定时器的定义

定时器在本中用一个链表来保存的,链表的每个节点都是一个定时器。

参见头文件 <linux/timer.h>

struct timer_list {
    struct list_head entry;
    unsigned long expires;

    void (*function)(unsigned long);
    unsigned long data;

    struct tvec_base *base;
#ifdef CONFIG_TIMER_STATS
    void *start_site;
    char start_comm[16];
    int start_pid;
#endif
#ifdef CONFIG_LOCKDEP
    struct lockdep_map lockdep_map;
#endif
};

经过出席条件编译的参数,可以多一些调试信息。

 

 

4.2 定时器的生命周期

一个动态定时器的生命周期中,一般会经过上边的几乎单步骤:

统计 11

  1. 初步化定时器:

    struct timer_list my_timer; / 定义定时器 /
    init_timer(&my_timer); / 开头化定时器 /

 

  1. 填充定时器:

    my_timer.expires = jiffies + delay; / 定义超时的点子数 /
    my_timer.data = 0; / 给定时器函数传入的参数 /
    my_timer.function = my_function; / 定时器超时时,执行之自定义函数 /

    / 从定时器结构体中,我们可见到这一个函数的原型应该如下所示: /
    void my_function(unsigned long data);

 

  1. 激活定时器和改动定时器:

激活定时器之后才会师吃硌,否则定时器不会见履行。

改定时器首假如修改定时器的延迟时间,修改定时器后,不管原先定时器有无来让激活,都谋面处于激活状态。

 

填充定时器结构从此,可以独自激活定时器,也堪只有修改定时器,也堪激活定时器后更修改定时器。

因此填充定时器结构及接触定时器之间的手续,也便是虚线框中之步骤是不确定的。

add_timer(&my_timer);  /* 激活定时器 */
mod_timer(&my_timer, jiffies + new_delay);  /* 修改定时器,设置新的延迟时间 */

 

  1. 点定时器:

历次时钟中断处理程序会检讨已经激活的定时器是否过,若是超时就尽定时器结构中的自定义函数。

 

  1. 剔除定时器:

激活和无为激活的定时器都足以吃删除,已经过期的定时器会活动删除,不用特别去去。

/*
 * 删除激活的定时器时,此函数返回1
 * 删除未激活的定时器时,此函数返回0
 */
del_timer(&my_timer);

以多对处理器上之所以 del_timer
函数删除定时器时,可能当剔除时巧另一个CPU核及之钟表中断处理程序正在举办之定时器,于是便形成了竞争规则。

为了制止竞争法,提出用 del_timer_sync 函数来删除定时器。

del_timer_sync
函数会等待其他电脑上的定时器处理程序全体毕晚,才去指定的定时器。

/*
 * 和del_timer 不同,del_timer_sync 不能在中断上下文中执行
 */
del_timer_sync(&my_timer);

 

1. 系列时

系统受到管理之年华有2种:实际时间及定时器。

4. 定时器执行流程

此研究的定时器执行流程是动态定时器的履行流程。

 

2. 定时器

水源中的定时器有2种植,静态定时器和动态定时器。

静态定时器一般实施了有些周期性的固定工作:

  • 创新系统运作时刻
  • 更新实际时间
  • 当SMP系统及,平衡各种处理器上之运作班
  • 反省时过程是否就此一味矣团结之时片,假若用一味,需要重新调度。
  • 更新资源消耗和总括机时间总结值

 

动态定时器顾名思义,是以待时(一般是推程序执行)动态制造的定时器,使用后销毁(一般仍旧光所以同样不成)。

貌似大家当本代码中使用的定时器基本都是动态定时器,下边要啄磨动态定时器相关的概念与用办法。

 

5. 落实程序延迟的方法

本中出个利用定时器实现延迟的函数 schedule_timeout

此函数会将手上底职责睡眠及指定时间后指示,所以待时莫会师占用CPU时间。

/* 将任务设置为可中断睡眠状态 */
set_current_state(TASK_INTERRUPTIBLE);

/* 小睡一会儿,“s“秒后唤醒 */
schedule_timeout(s*HZ);

 

查看 schedule_timeout 函数的兑现格局,可以看到是怎么着采纳定时器的。

signed long __sched schedule_timeout(signed long timeout)
{
    /* 定义一个定时器 */
    struct timer_list timer;
    unsigned long expire;

    switch (timeout)
    {
    case MAX_SCHEDULE_TIMEOUT:
        /*
         * These two special cases are useful to be comfortable
         * in the caller. Nothing more. We could take
         * MAX_SCHEDULE_TIMEOUT from one of the negative value
         * but I' d like to return a valid offset (>=0) to allow
         * the caller to do everything it want with the retval.
         */
        schedule();
        goto out;
    default:
        /*
         * Another bit of PARANOID. Note that the retval will be
         * 0 since no piece of kernel is supposed to do a check
         * for a negative retval of schedule_timeout() (since it
         * should never happens anyway). You just have the printk()
         * that will tell you if something is gone wrong and where.
         */
        if (timeout < 0) {
            printk(KERN_ERR "schedule_timeout: wrong timeout "
                "value %lx\n", timeout);
            dump_stack();
            current->state = TASK_RUNNING;
            goto out;
        }
    }

    /* 设置超时时间 */
    expire = timeout + jiffies;

    /* 初始化定时器,超时处理函数是 process_timeout,后面再补充说明一下这个函数 */
    setup_timer_on_stack(&timer, process_timeout, (unsigned long)current);
    /* 修改定时器,同时会激活定时器 */
    __mod_timer(&timer, expire, false, TIMER_NOT_PINNED);
    /* 将本任务睡眠,调度其他任务 */
    schedule();
    /* 删除定时器,其实就是 del_timer_sync 的宏
    del_singleshot_timer_sync(&timer);

    /* Remove the timer from the object tracker */
    destroy_timer_on_stack(&timer);

    timeout = expire - jiffies;

 out:
    return timeout < 0 ? 0 : timeout;
}
EXPORT_SYMBOL(schedule_timeout);

/* 
 * 超时处理函数 process_timeout 里面只有一步操作,唤醒当前任务。
 * process_timeout 的参数其实就是 当前任务的地址
 */
static void process_timeout(unsigned long __data)
{
    wake_up_process((struct task_struct *)__data);
}

schedule_timeout 一般用于延迟时间较充分之次第。

这边的延迟时间较充分是对于电脑而言的,其实呢就是缓大于 1
独点子(jiffies)。

 

对于一些最短暂之推,比如就出1ms,甚至1us,1ns底延,必须采取异乎平日之缓方法。

1s = 1000ms = 1000000us = 1000000000ns
(1秒=1000毫秒=1000000微秒=1000000000纳秒)

一经 HZ=100,那么 1独点子的辰间隔是 1/100秒,大概10ms左右。

因此对于那一个最短暂的缓,schedule_timeout 函数是心有余而力不足采用的。

好以本对于这个短暂,精确的推要求啊提供了相应的特大。

/* 具体实现参见 include/linux/delay.h
 * 以及 arch/x86/include/asm/delay.h
 */
#define mdelay(n) ...
#define udelay(n) ...
#define ndelay(n) ...

通过那一个巨大,能够省略的实现延迟,比如延迟 5ns,只待 ndelay(5); 即可。

 

这一个短延迟的落实原理并无复杂,

率先,内核在启动时即计发生了近日电脑1秒能尽稍微坏巡回,即
loops_per_jiffy

(loops_per_jiffy 的精打细算方法参见 init/main.c 文件被的
calibrate_delay 方法)。

下一场算有延迟 5ns 需要循环多少次,执行这累空循环即可达到延迟的效能。

 

loops_per_jiffy 的值好当启动音讯碰到看到:

[root@vbox ~]# dmesg | grep delay
Calibrating delay loop (skipped), value calculated using timer frequency.. 6387.58 BogoMIPS (lpj=3193792)

自己之虚拟机中视 (lpj=3193792)

 

网被出过多跟工夫相关的次(比如定期执行的天职,某一时间执行的任务,推迟一段时间执行之任务),因而,时间的保管于linux来说卓殊重要。

6. 定时器和延缓的事例

脚的例证测试了短延迟,自定义定时器以及 schedule_timeout 的使用:

#include <linux/sched.h>
#include <linux/timer.h>
#include <linux/jiffies.h>
#include <asm/param.h>
#include <linux/delay.h>
#include "kn_common.h"

MODULE_LICENSE("Dual BSD/GPL");

static void test_short_delay(void);
static void test_delay(void);
static void test_schedule_timeout(void);
static void my_delay_function(unsigned long);

static int testdelay_init(void)
{
    printk(KERN_ALERT "HZ in current system: %dHz\n", HZ);

    /* test short delay */
    test_short_delay();

    /* test delay */
    test_delay();

    /* test schedule timeout */
    test_schedule_timeout();

    return 0;
}

static void testdelay_exit(void)
{
    printk(KERN_ALERT "*************************\n");
    print_current_time(0);
    printk(KERN_ALERT "testdelay is exited!\n");
    printk(KERN_ALERT "*************************\n");
}

static void test_short_delay()
{
    printk(KERN_ALERT "jiffies [b e f o r e] short delay: %lu", jiffies);
    ndelay(5);
    printk(KERN_ALERT "jiffies [a f t e r] short delay: %lu", jiffies);
}

static void test_delay()
{
    /* 初始化定时器 */
    struct timer_list my_timer;
    init_timer(&my_timer);

    /* 填充定时器 */
    my_timer.expires = jiffies + 1*HZ; /* 2秒后超时函数执行 */
    my_timer.data = jiffies;
    my_timer.function = my_delay_function;

    /* 激活定时器 */
    add_timer(&my_timer);
}

static void my_delay_function(unsigned long data)
{
    printk(KERN_ALERT "This is my delay function start......\n");
    printk(KERN_ALERT "The jiffies when init timer: %lu\n", data);
    printk(KERN_ALERT "The jiffies when timer is running: %lu\n", jiffies);
    printk(KERN_ALERT "This is my delay function end........\n");
}

static void test_schedule_timeout()
{
    printk(KERN_ALERT "This sample start at : %lu", jiffies);

    /* 睡眠2秒 */
    set_current_state(TASK_INTERRUPTIBLE);
    printk(KERN_ALERT "sleep 2s ....\n");
    schedule_timeout(2*HZ);

    printk(KERN_ALERT "This sample end at : %lu", jiffies);
}

module_init(testdelay_init);
module_exit(testdelay_exit);

中间以的 kn_common.h 和 kn_common.c 参见往日的博客
《Linux内核设计以及落实》读书笔记(六)-
内核数据结构

Makefile如下:

# must complile on customize kernel
obj-m += mydelay.o
mydelay-objs := testdelay.o kn_common.o

#generate the path
CURRENT_PATH:=$(shell pwd)
#the current kernel version number
LINUX_KERNEL:=$(shell uname -r)
#the absolute path
LINUX_KERNEL_PATH:=/usr/src/kernels/$(LINUX_KERNEL)
#complie object
all:
    make -C $(LINUX_KERNEL_PATH) M=$(CURRENT_PATH) modules
    rm -rf modules.order Module.symvers .*.cmd *.o *.mod.c .tmp_versions *.unsigned
#clean
clean:
    rm -rf modules.order Module.symvers .*.cmd *.o *.mod.c *.ko .tmp_versions *.unsigned

 

履测试命令和查看结果的法如下:(我之测试网是 CentOS 6.3 x64)

[root@vbox chap11]# make
[root@vbox chap11]# insmod mydelay.ko 
[root@vbox chap11]# rmmod mydelay.ko 
[root@vbox chap11]# dmesg | tail -14
HZ in current system: 1000Hz
jiffies [b e f o r e] short delay: 4296079617
jiffies [a f t e r] short delay: 4296079617
This sample start at : 4296079619
sleep 2s ....
This is my delay function start......
The jiffies when init timer: 4296079619
The jiffies when timer is running: 4296080621
This is my delay function end........
This sample end at : 4296081622
*************************
2013-5-9 23:7:20
testdelay is exited!
*************************

 

结果证实:

  1. 短延迟只延迟了 5ns,所以举行前后的jiffies是一致的。

    jiffies [b e f o r e] short delay: 4296079617
    jiffies [a f t e r] short delay: 4296079617

 

  1. 自打定义定时器延迟了1秒后行打定义函数,由于我的系
    HZ=1000,所以jiffies应该去1000

    The jiffies when init timer: 4296079619
    The jiffies when timer is running: 4296080621

实际上jiffies相差了 1002,多了2个节拍

 

  1. schedule_timeout 延迟了2秒,jiffies应该去 2000

    This sample start at : 4296079619
    This sample end at : 4296081622

实际上jiffies相差了 2003,多了3个节拍

 

以上结果为印证了定时器的延并无是那么准确,差了2,3只点子其实虽然是误差2,3飞秒(因为HZ=1000)

只要HZ=100之话语,一个旋律是10飞秒,那么定时器的误差可能就意识不了了(误差只发2,3飞秒,没有超多1只点子)。

3.3 时钟中断处理程序

钟表中断处理程序作为系统定时器而注册到本中,体系布局的差,可能时钟中断处理程序中拍卖的情节不一。

不过以下这么些主题的办事都谋面履:

  • 获得 xtime_lock 锁,以便对走访 jiffies_64 和墙上时间 xtime
    举办维护
  • 急需经常回答或重复设置系统时钟
  • 周期性的施用墙上时间更新实时时钟
  • 调用 tick_periodic()

 

tick_periodic函数位于: kernel/time/tick-common.c

static void tick_periodic(int cpu)
{
    if (tick_do_timer_cpu == cpu) {
        write_seqlock(&xtime_lock);

        /* Keep track of the next tick event */
        tick_next_period = ktime_add(tick_next_period, tick_period);

        do_timer(1);
        write_sequnlock(&xtime_lock);
    }

    update_process_times(user_mode(get_irq_regs()));
    profile_tick(CPU_PROFILING);
}

内最为根本之是 do_timer 和 update_process_times 函数。

自家了然之步调进行了概括的讲明。

void do_timer(unsigned long ticks)
{
    /* jiffies_64 增加指定ticks */
    jiffies_64 += ticks;
    /* 更新实际时间 */
    update_wall_time();
    /* 更新系统的平均负载值 */
    calc_global_load();
}

void update_process_times(int user_tick)
{
    struct task_struct *p = current;
    int cpu = smp_processor_id();

    /* 更新当前进程占用CPU的时间 */
    account_process_tick(p, user_tick);
    /* 同时触发软中断,处理所有到期的定时器 */
    run_local_timers();
    rcu_check_callbacks(cpu, user_tick);
    printk_tick();
    /* 减少当前进程的时间片数 */
    scheduler_tick();
    run_posix_cpu_timers(p);
}