当前位置:  建站>运营/SEO
本页文章导读:
    ▪完整/正确配置exim服务器的方法      一般的debian主机都安装过exim,没有安装的话,请先运行apt-get install exim4 配置方法1:运行dpkg-reconfigure exim4-config 第一个选项选第一个:internet site; mail is sent and received directly using SMTP 第二个选.........
    ▪一步步理解Linux进程(4)--等待队列和进程切换      0. 说明 作者:Gao Peng<gaopenghigh@gmail.com> 本文章由Gao Peng编写,转载请注明出处。 原文地址:http://blog.csdn.net/gaopenghigh/article/details/8836032 1. 等待队列 假如一个进程去读一个磁盘上的.........
    ▪一步步理解Linux进程(5)--进程调度       0. 说明 作者:Gao Peng<gaopenghigh@gmail.com> 本文章由Gao Peng编写,转载请注明出处。 原文地址:http://blog.csdn.net/gaopenghigh/article/details/8836417 1. 什么是进程调度 Linux是多任务系统,系统.........

[1]完整/正确配置exim服务器的方法
    来源: 互联网  发布时间: 2013-10-27

一般的debian主机都安装过exim,没有安装的话,请先运行apt-get install exim4

配置方法1:运行dpkg-reconfigure exim4-config

第一个选项选第一个:internet site; mail is sent and received directly using SMTP

第二个选项输入:你IP对应的DNS名称(发邮件时显示的@后缀)

方法2:vim /etc/exim4/update-exim4.conf.conf

dc_eximconfig_configtype='internet' #对应方法1第一项

dc_other_hostnames='你IP对应的DNS名称'

然后重启exim服务:invoke-rc.d exim4 restart


修改/var/log/exim4/文件夹权限:

chown -R Debian-exim /var/log/exim4

chmod -R u+rw /var/log/exim4(可选)


发一封邮件试试:echo "邮件内容“ | mail email地址 s="邮件标题"

察看有多少发件进程:exim -bpc

删除发件进程中的邮件:exim -bpru | awk {'print $3'} | xargs exim -Mrm


如果出错请:

mkdir -p /var/log/exim4

touch /var/log/exim4/mainlog  无报错



作者:paul_xj 发表于2013-4-22 16:36:40 原文链接
阅读:56 评论:0 查看评论

    
[2]一步步理解Linux进程(4)--等待队列和进程切换
    来源: 互联网  发布时间: 2013-10-27
0. 说明

作者:Gao Peng<gaopenghigh@gmail.com>
本文章由Gao Peng编写,转载请注明出处。
原文地址:http://blog.csdn.net/gaopenghigh/article/details/8836032


1. 等待队列

假如一个进程去读一个磁盘上的文件,但是这个磁盘反应比较慢(相对于CPU来说),这是进程不能马上读取到数据,于是被阻塞了。此时内核一般会把这个进程的状态设置为睡眠TASK_INTERRUPTIBLE或者TASK_UNINTERRUPTIBLE,然后把这个进程放入等待队列里面,直到数据读取出来了才再次运行。

等待队列表示一组睡眠的进程,当某一条件变为真时,由内核唤醒它们。


2. 等待队列的使用

使用等待队列
加入我们定义了一个函数write_to_buffer()把数据写入一个缓存区,但当这个缓存区已满时我们希望这个函数阻塞住,直到缓存区有空间。缓存区是否满由一个变量is_full表示。则大致的实现如下:
/* 建立并初始化一个等待队列头,当然也可以选用已经存在的等待队列 */
DECLARE_WAIT_QUEUE_HEAD(wq);
/* 建立一个等待队列项 */
wait_queue_t my_wait;
/* 用当前进程的进程描述符初始化这个等待队列项 */
init_wait(&my_wait);
/* 将等待队列项添加到等待队列头中,并设置进程的状态 */
prepare_to_wait(&wq, my_wait, state);
/* 调用schedule,告诉内核调用其他的进程执行 */
schedule();
/* schedule返回,完成后续清理工作 */
finish_wait();

等待队列由“等待队列头(struct wait_queue_head_t)”和“等待队列项(struct wait_queue_t)”组成。
schedule函数是内核的进程调度函数,当它被调用是,内核会按照一定的调度算法选择出下一个执行的进程,然后进行进程切换的操作。schedule返回后,说明这个进程醒来了(需要的条件成立了)并且被内核选中开始执行了。

等待队列的操作
2.6内核提供如下关于等待队列的操作:

a. 定义和初始化“等待队列头”
    wait_queue_head_t my_queue;
    init_waitqueue_head(&my_queue);
    或者调用宏:
    DECLARE_WAIT_QUEUE_HEAD(my_queue);

b. 定义并初始化“等待队列项”
    DECLARE_WAITQUEUE(name, tsk);
    其中name是类型为“struct wait_queue_t”的指针,tsk类型为“struct task_struct”的指针

c. 添加/移除“等待队列项”
    void fastcall add_wait_queue(wait_queue_head_t *q, wait_queue_t *wait);
    void fastcall remove_wait_queue(wait_queue_head_t *q, wait_queue_t *wait);

d. 等待事件
    wait_event(wq, condition);                               /* 不可中断的等待 */
    wait_event_interruptible(wq, condition)                  /* 可中断的等待 */
    wait_event_timeout(wq, condition, timeout)               /* 带超时返回的等待 */
    wait_event_interruptible_timeout(wq, condition, timeout) /* 可中断并超时返回的等待 */

e. 唤醒队列
    wake_up(wait_queue_head_t *q) ;              /* 唤醒q上所有等待的进程 */ 
    wake_up_interruptible(wait_queue_head_t *q); /* 只唤醒q上执行可中断休眠的进程 */


3. 等待队列的内核实现

等待队列头
等待队列由双向链表实现,每一个等待队列都有一个等待队列头(wait queue head), 其类型是名为wait_queue_head_t的数据结构:
/* <linux/wait.h> */
struct __wait_queue_head {
    spinlock_t lock;              /* 自旋锁 */
    struct list_head task_list;   /* 指向链表 */
};
typedef struct __wait_queue_head wait_queue_head_t;

由于等待队列是由中断处理程序和主要内核函数修改的,所以必须对链表进行保护,防止它被同时访问,这样的同步是通过wait_queue_head结构中的lock自旋锁实现的。task_list是等待进程链表的头。

等待队列项
等待队列项的数据结构是wait_queue_t,定义如下:
/* linux/wait.h> */
typedef struct __wait_queue wait_queue_t;
struct __wait_queue {
    unsigned int flags;
#define WQ_FLAG_EXCLUSIVE   0x01
    void *private;
    wait_queue_func_t func;
    struct list_head task_list;
};

各个字段的解释:
private:
    等待队列项代表一个睡眠的进程。在wait_queue_t结构中,这个进程的进程描述符的地址存放在private字段中。
flags:
    有的时候,如果有两个或者多个进程等待互斥访问某一要释放的资源,这时候,就只需要唤醒其
    
[3]一步步理解Linux进程(5)--进程调度
    来源: 互联网  发布时间: 2013-10-27
0. 说明

作者:Gao Peng<gaopenghigh@gmail.com>
本文章由Gao Peng编写,转载请注明出处。
原文地址:http://blog.csdn.net/gaopenghigh/article/details/8836417


1. 什么是进程调度

Linux是多任务系统,系统中需要运行很很多进程,而CPU就那么几个,于是内核就把CPU资源分配给这些进程,让它们都能运行一小会,然后又让位给其他进程运行。当遇到阻塞,比如从硬盘读取某个文件时,也会把CPU资源让给其它进程使用。 把CPU资源分配给进程,就叫做进程调度。


2. 调度策略

最开始,Linux的调度算法很简单。所有进程都有一个优先级,这个优先级是时刻变化的:当一个进程用了较多的CPU时,内核就把它的优先级调低,而当一个进程用了很少的CPU时,内核就把它的优先级调高,表示它很需要CPU资源了。每次进程切换时,内核扫描可运行进程的链表,计算进程的优先级,然后选择“最佳”进程来运行。当进程数目较大时,选择“最佳”进程所消耗的时间会比较长,这种算法开销太大。内核把进程分为实时进程和普通进程,实时进程的优先级是静态设定的,而且始终大于普通进程的优先级。一共有3中调度策略:SCHED_FIFO, SCHED_RR和SCHED_NORMAL。其中SCHED_FIFO采用先进先出的策略,最先进入runqueue的进程会首先运行,除非它主动放弃使用CPU,否则会一直占用。SCHED_RR则在进程间论转分配CPU时间。这两种策略针对实时进程,SCHED_NORMAL策略针对普通进程。

后来,Linux 2.6采用了一种叫做O(1)的调度程序,该算法的名字就透露出它能在恒定的时间内选出“最佳”进程。这个算法很复杂,事实上很不美观。 调度器为每一个CPU维护了两个进程队列数组:active(时间片未用完的进程)数组和expire(时间片用完的进程)数组。数组中的元素保存着某一优先级的进程队列指针。系统一共有140个不同的优先级,因此这两个数组大小都是140。当需要选择当前最高优先级的进程时,调度器不用遍历整个runqueue,而是直接从active数组中选择当前最高优先级队列中的第一个进程。 每次时钟tick中断中,进程的时间片(time_slice)被减一。当time_slice为0时,调度器判断当前进程的类型,如果是交互式进程或者实时进程,则重置其时间片并重新插入active数组。如果不是交互式进程则从active数组中移到expired数组。这样实时进程和交互式进程就总能优先获得CPU。然而这些进程不能始终留在active数组中,否则进入expire数组的进程就会产生饥饿现象。当进程已经占用CPU时间超过一个固定值后,即使它是实时进程或者交互式进程也会被移到expire数组中。 当active数组中的所有进程都被移到expire数组中后,调度器交换active数组和expire数组。当进程被移入expire数组时,调度器会重置其时间片,因此新的active数组又恢复了初始情况,而expire数组为空,从而开始新的一轮调度。

Linux在2.6.23版本以后,使用了“完全公平调度算法”(CFS)。


3. 完全公平调度算法(CFS)

CFS不依靠实时优先级来调度,进程得到的也不是确定的时间片,而是一个CPU的使用百分比。如果有2个进程,则它们各自能得到50%的CPU使用时间。CFS的做法是对每一个进程记录它使用CPU的时间,然后选择使用CPU最少的一个进程作为下一个运行的进程。也就是说,如果一个可运行的进程(没有被阻塞)得到的CPU时间比其他进程少,那么就认为内核对它不公平,就把下一次运行的机会让给它。而每个进程的nice值,则作为一个权重,在计算使用了多少CPU时间时加权进去。比如一个nice值较高(优先级较低)的进程明明跑了了50ms的时间,由于它的nice值比较高,CFS就给它多算点时间。选择下一个进程时,由于是要选得到CPU时间最少的进程,那么这个nice值较高的进程就排到后面去了,正好体现出了它优先级低的属性。

CFS抛弃了active/expire数组,而使用红黑树选取下一个被调度进程。所有状态为RUNABLE的进程都被插入红黑树。在每个调度点,CFS调度器都会选择红黑树的最左边的叶子节点作为下一个将获得cpu的进程。简单地说,红黑数是一个时间复杂度为O(log n)的自平衡二叉搜索树。它提供了一种方式,能够以较小时间对树中的节点位置进行调整,使key值最小的一个节点就在树的最左边。

下面我们来看看CFS具体是怎么实现的。

3.1 时间记账

CFS用调度器实体结构sched_entity来追踪进程运行记账,该结构嵌入在进程描述符task_stuct内。
struct sched_entity {
    struct load_weight  load;       /* for load-balancing */
    struct rb_node      run_node;
    struct list_head    group_node;
    unsigned int        on_rq;

    u64         exec_start;
    u64         sum_exec_runtime;
    u64         vruntime;
    u64         prev_sum_exec_runtime;

    u64         nr_migrations;

#ifdef CONFIG_SCHEDSTATS
    struct sched_statistics statistics;
#endif

#ifdef CONFIG_FAIR_GROUP_SCHED
    struct sched_entity *parent;
    /* rq on which this entity is (to be) queued: */
    struct cfs_rq       *cfs_rq;
    /* rq "owned" by this entity/group: */
    struct cfs_rq       *my_q;
#endif
};

sched_entity中,vruntime变量是进程的虚拟运行时间,CFS使用该时间来记录一个进程到底运行了多长时间以及它还应该运行多久。

update_curr()函数实现了该记账功能:
/* <kernel/sched/fair.c> */
static void update_curr(struct cfs_rq *cfs_rq)
{
struct sched_entity *curr = cfs_rq->curr;
u64 now = rq_of(cfs_rq)->clock_task;
unsigned long delta_exec;

if (unlikely(!curr))
return;

/*
 * Get the amount of time the current task was running
 * since the last time we changed load (this cannot
 * overflow on 32 bits):
 */
delta_exec = (unsigned long)(now - curr->exec_start);
if (!delta_exec)
return;

__update_curr(cfs_rq, curr, delta_exec);
curr->exec_start = now;

if (entity_is_task(curr)) {
struct task_struct *curtask = task_of(curr);

trace_sched_stat_runtime(curtask, delta_exec, curr->vruntime);
cpuacct_charge(curtask, delta_exec);
account_group_exec_runtime(curtask, delta_exec);
}

account_cfs_rq_runtime(cfs_rq, delta_exec);
}

static inline void
__update_curr(struct cfs_rq *cfs_rq, struct sched_entity *curr,
      unsigned long delta_exec)
{
unsigned long delta_exec_weighted;

schedstat_set(curr->statistics.exec_max,
      max((u64)delta_exec, curr->statistics.exec_max));

curr->sum_exec_runtime += delta_exec;
schedstat_add(cfs_rq, exec_clock, delta_exec);
delta_exec_weighted = calc_delta_fair(delta_exec, curr);

curr->vruntime += delta_exec_weighted;
update_min_vruntime(cfs_rq);

#if defined CONFIG_SMP && defined CONFIG_FAIR_GROUP_SCHED
cfs_rq->load_unacc_exec_time += delta_exec;
#endif
}

static inline unsigned long
calc_delta_fair(unsigned long delta, struct sched_entity *se)
{
if (unlikely(se->load.weight != NICE_0_LOAD))
delta = calc_delta_mine(delta, NICE_0_LOAD, &se->load);

return delta;
}

update_curr()是由系统的tick中断周期性调用的。update_curr函数首先统计当前进程所获取的CPU时间(当前时间 - 进程开始时的时间),然后调用__update_
    
最新技术文章:
 




特别声明:169IT网站部分信息来自互联网,如果侵犯您的权利,请及时告知,本站将立即删除!

©2012-2021,,E-mail:www_#163.com(请将#改为@)

浙ICP备11055608号-3