注册 登录  
 加关注
   显示下一条  |  关闭
温馨提示!由于新浪微博认证机制调整,您的新浪微博帐号绑定已过期,请重新绑定!立即重新绑定新浪微博》  |  关闭

Tsecer的回音岛

Tsecer的博客

 
 
 

日志

 
 

crond实现概述  

2012-09-09 17:07:27|  分类: Linux系统编程 |  标签: |举报 |字号 订阅

  下载LOFTER 我的照片书  |
一、crond及crontab
这是linux下执行定时任务的一个常用工具,事实上真正的守护任务时crond,而crontab只是一个来修改系统内容的接口而已;就好像mysql事实上只是一个数据库的操作命令接口,而真正的数据是存放在mysqld中一样。这一点通过crontab的配置就可以知道,它的配置文件中并不支持以秒为单位的执行,wiki百科中对配置格式的说明
*    *    *    *    *  command to be executed ┬    ┬    ┬    ┬    ┬ │    │    │    │    │ │    │    │    │    │ │    │    │    │    └───── day of week (0 - 6) (0 is Sunday, or use names) │    │    │    └────────── month (1 - 12) │    │    └─────────────── day of month (1 - 31) │    └──────────────────── hour (0 - 23) └───────────────────────── min (0 - 59)

二、crond的实现
crond工具是通过redhat的cronie软件包生成的,而主题的程序位于
cronie-1.4.8\src\cron.c文件的main函数体内,其主要代码为
    /*
     * Too many clocks, not enough time (Al. Einstein)
     * These clocks are in minutes since the epoch, adjusted for timezone.
     * virtualTime: is the time it *would* be if we woke up
     * promptly and nobody ever changed the clock. It is
     * monotonically increasing... unless a timejump happens.
     * At the top of the loop, all jobs for 'virtualTime' have run.
     * timeRunning: is the time we last awakened.
     * clockTime: is the time when set_time was last called.
     */ 这个函数中使用了三个时间存储结构。作者在这里进行了说明,但是看得不是很明白。大致的意思是说。virtual time是我们期望的、正常情况下的良好数值,而timeRunning则是从sleep返回之后的时间,clockTIme则是set_time最后一次执行的时间
    while (TRUE) {
        int timeDiff;
        enum timejump wakeupKind;

        /* ... wait for the time (in minutes) to change ... */
        do {
            cron_sleep(timeRunning + 1, &database); 这里唤醒是每分钟的整点唤醒。因为cron_sleep中的timeRunning是分钟为单位,cron_sleep会计算目标分钟和当前时间的秒差,然后通过sleep来完成这个等待时间。
            set_time(FALSE)
; 这里就是cron精确度限制的根本原因,它在每次开始的时候休眠了1分钟。也就是它每分钟会检测并执行一下系统中的定时任务,它无法达到秒即精确度
        } while (clockTime == timeRunning);
        timeRunning = clockTime;
#if defined WITH_INOTIFY
        if (inotify_enabled) {
            check_inotify_database(&database);
        }
        else {
            if (load_database(&database) && (EnableClustering != 1))
                /* try reinstating the watches */
                set_cron_watched(fd);
        }
#else
        load_database(&database);每隔一分钟会自动检测crontab中文件是否已经发生配置变化,所以不用重启crond
#endif

        /*
         * Calculate how the current time differs from our virtual
         * clock.  Classify the change into one of 4 cases.
         */
        timeDiff = timeRunning - virtualTime;
        check_orphans(&database);
……
        /* shortcut for the most common case */
        if (timeDiff == 1) {
            virtualTime = timeRunning;
            oldGMToff = GMToff;
            find_jobs(virtualTime, &database, TRUE, TRUE, oldGMToff);
        }
 
        /* Jobs to be run (if any) are loaded; clear the queue. */
        job_runqueue();
        handle_signals(&database);
    }
三、环境变量的读取
这些内容来自用户的密码配置文件/etc/passwd
load_entry
    if (!env_get("SHELL", e->envp)) {
        if (glue_strings(envstr, sizeof envstr, "SHELL", _PATH_BSHELL, '=')) {
            if ((tenvp = env_set(e->envp, envstr)) == NULL) {
                ecode = e_memory;
                goto eof;
            }
            e->envp = tenvp;
        }
        else
            log_it("CRON", getpid(), "ERROR", "can't set SHELL", 0);
    }
    if (!env_get("HOME", e->envp)) {
        if (glue_strings(envstr, sizeof envstr, "HOME", pw->pw_dir, '=')) {
            if ((tenvp = env_set(e->envp, envstr)) == NULL) {
                ecode = e_memory;
                goto eof;
            }
            e->envp = tenvp;
        }
        else
            log_it("CRON", getpid(), "ERROR", "can't set HOME", 0);
    }
……
#ifndef LOGIN_CAP
    /* If login.conf is in used we will get the default PATH later. */
    if (!env_get("PATH", e->envp)) {
        if (glue_strings(envstr, sizeof envstr, "PATH", _PATH_DEFPATH, '=')) {
            if ((tenvp = env_set(e->envp, envstr)) == NULL) {
                ecode = e_memory;
                goto eof;
            }
            e->envp = tenvp;
        }
        else
            log_it("CRON", getpid(), "ERROR", "can't set PATH", 0);
    }
#endif /* LOGIN_CAP */
……
    if (glue_strings(envstr, sizeof envstr, "LOGNAME", pw->pw_name, '=')) {
        if ((tenvp = env_set(e->envp, envstr)) == NULL) {
            ecode = e_memory;
            goto eof;
        }
        e->envp = tenvp;
    }
    else
        log_it("CRON", getpid(), "ERROR", "can't set LOGNAME", 0);
#if defined(BSD) || defined(__linux)
    if (glue_strings(envstr, sizeof envstr, "USER", pw->pw_name, '=')) {
        if ((tenvp = env_set(e->envp, envstr)) == NULL) {
            ecode = e_memory;
            goto eof;
        }
        e->envp = tenvp;
    }
    else
        log_it("CRON", getpid(), "ERROR", "can't set USER", 0);
#endif
四、环境变量的使用

        if (cron_change_user_permanently(e->pwd, env_get("HOME", jobenv)) < 0)
            _exit(ERROR_EXIT);
……

        /*
         * Exec the command.
         */
        {
            char *shell = env_get("SHELL", jobenv);使用SHELL定义的变量

修改用户组
int cron_change_groups(struct passwd *pw) {
    pid_t pid = getpid();

    if (setgid(pw->pw_gid) != 0) {
        log_it("CRON", pid, "ERROR", "setgid failed", errno);
        return -1;
    }

    if (initgroups(pw->pw_name, pw->pw_gid) != 0) {
        log_it("CRON", pid, "ERROR", "initgroups failed", errno);
        return -1;
    }
五、在crontab中修改环境变量
如果想修改整个所有的用户的环境变量,可以在crontab中配置一行,专门用来进行一个用户特有环境变量的配置。例如
[root@Harry src]# crontab -l
HOME=/home/tsecer/
* *  * * * pwd>/root/crontab.out
[root@Harry src]# cat /root/crontab.out
/home/tsecer
[root@Harry src]#
由于crond在派生子进程的时候使用的是SHELL直接派生,也就是 $SHELL -c 来执行命令行中配置的命令,因此可以通过把环境变量配置到命令前来实现,因为
ENVVAR=ENVVAL  commands
本来就是一个合法的shell命令。
执行cron命令的接口实现do_command.c:child_process

        /*
         * Exec the command.
         */
        {
            char *shell = env_get("SHELL", jobenv);

#if DEBUGGING
            if (DebugFlags & DTEST) {
                fprintf(stderr, "debug DTEST is on, not exec'ing command.\n");
                fprintf(stderr, "\tcmd='%s' shell='%s'\n", e->cmd, shell);
                _exit(OK_EXIT);
            }
#endif         /*DEBUGGING*/
                execle(shell, shell, "-c", e->cmd, (char *) 0, jobenv);
            fprintf(stderr, "execl: couldn't exec `%s'\n", shell);
            perror("execl");
            _exit(ERROR_EXIT);
        }
  评论这张
 
阅读(1330)| 评论(3)
推荐 转载

历史上的今天

评论

<#--最新日志,群博日志--> <#--推荐日志--> <#--引用记录--> <#--博主推荐--> <#--随机阅读--> <#--首页推荐--> <#--历史上的今天--> <#--被推荐日志--> <#--上一篇,下一篇--> <#-- 热度 --> <#-- 网易新闻广告 --> <#--右边模块结构--> <#--评论模块结构--> <#--引用模块结构--> <#--博主发起的投票-->
 
 
 
 
 
 
 
 
 
 
 
 
 
 

页脚

网易公司版权所有 ©1997-2017