精华内容
下载资源
问答
  • linux中断子系统及中断上下部分

    千次阅读 2013-11-20 23:11:46
    中断子系统 一、异常 在执行代码时产生,而且由代码本身产生,也就是说这个操作是处理器在执行...设备产生中断--->中断控制器--->处理器打断正在执行代码--->跳相应中断线的初始入口地址--->保存当前中断号和
    中断子系统


    一、异常
    在执行代码时产生,而且由代码本身产生,也就是说这个操作是处理器在执行代码的时候发生的,所以又称作同步中断


    二、中断
    与处理器当前处理的代码无关,处理器并不知道什么时候中断到来,所以中断是异步的


    三、中断处理机制的实现:
    设备产生中断--->中断控制器--->处理器打断正在执行的代码--->跳到相应中断线的初始入口地址--->保存当前中断号和寄存器的值--->内核调用do_IRQ()函数--->开启中断系统并禁止该中断线上的中断---> do_IRQ()调用handle_IRQ()来执行这条中断线上注册过的中断--->中断返回,如果当时中断的是用户态程序,则在中断返回时schedule()被调用重新调度进程,如果中断的是内核本生则反回中断处继续执行

    四、/proc/interrupts显示中断有关的信息

    五、中断上下文
    上下文就是代码所执行时的环境,进程处在进程上下文中,中断处理程序处在中断上下文中,只有处在进程上下文中的代码才可以被重新调度,处在中断上下文中的代码不能被重新调度,所以在中断上下文中不能睡眠,否则中断处理将永远不返回,in_interrupt()返回0代表在进程上下文中,返回非0代表在中断上下文中


    六、中断线和中断号的相互转换
    根据分析,6410支持的中断线如下:
    0~15: 为ISA设备预留
    16~31: 为UART0到UART3准备
    32~95: 为6410的64组中断源各分配了一条中断线
    96~100: Timer0~4
    101~128: 外部IO group 0对应的28个外部中断的中断线
    129~228: 外部group1~9对应的99个外部中断的中断线
    NR_IRQS: 229,linux中支持的中断号的总数量


    七、申请中断
    int request_irq(unsigned int irq, irqreturn_t (*handle)(int, void *), unsigned long irqflags, const char *devname, void *dev_id)
    irq:要申请的中断线
    handle:要注册的中断处理函数 该函数的第一个参数是触发中断后的中断线号,第二个是一个通用指针,当中断线共享的时候要传入一个唯一的值供注销中断的时候用,一般来说传入的是设备结构体的指针
    irqreturn_t:中断处理程序的返回值,中断处理程序正常执行返回该值应该返回IRQ_HANDLED否则该值应该返回IRQ_NONE(当检测到的中断不是想要的中断的时候要返回该值)
    irqflags:对中断的一些特殊要求,比如上升沿等等
    devname:用来在/proc/interrupts 下显示的中断的名字
    dev_id:这个参数便是传入到handle当中的第二个参数,一般用于中断的共享,如果不共享一般传NULL
    返回值:当该函数调用成功则返回0,如果错误返回一个负的错误码,一般是-EBUSY表示该中断线已经被注册了,而且没有被共享
    注意:这个函数可能会睡眠,在创建/proc下和中断相关的文件的时候要用kmalloc分配内存


    五、注销中断:
    void free_irq(unsigned int irq, void *dev_id)
    irq:要释放的中断线,和注册中断时候传入的一样
    dev_id:同一个中断线上不同共享中断处理函数的标志,如果中断是非共享的,则在注销中断处理函数的时候也把中断线禁止掉,如果是共享的,则dev_id就是指定要注销的那个处理函数,直到注销最后一个处理函数的时候才会禁止掉这条中断线

    六、中断处理程序一般不需要可重入:因为在中断处理函数在执行的时候最好的情况是禁止当前中断线,最不好的情况是禁止所有的本地中断


    七、中断控制
    禁止当前处理器的中断系统
    local_irq_disable()
    打开当前处理器的中断系统
    local_irq_enable()
    保存中断状态并且禁止中断
    unsigned long flags
    local_irq_save(flags)
    恢复中断状态
    local_irq_restore(flags)
    禁止指定的中断线
    void disable_irq(unsigned int irq)
    void disable_irq_nosync(unsigned int irq)
    void enable_irq(unsigned int irq)
    void synchronize(unsigned int irq)
    注意:前两个函数的功能是一样的,都是禁止掉irq线上的中断传递,第一个函数只有等到中断处理程序返回时候才会结束,第二个则不会等待。第四个函数用来等待特定的中断线处理程序的退出,这些函数可以嵌套使用,例如使用了两次disable则要使用两次enable才能再次激活该中断线,这四个函数基本上已经不再使用

    八、上半部分和下半部分
    为什么要分上下两部分:
    1.中断处理程序处在中断上下文中,不能睡眠
    2.中断时候会禁止其他的中断,中断处理程序太长会应影响系统的响应速率
    3.操作硬件的时候有严格的时间限制,所以要求响应要迅速



    要求立即处理的代码放到上部分
    和硬件相关的代码放到上部分
    要求不能被其他代码打断的代码放到上部分
    其他基本上放到下部分


    上部分:中断处理此程
    下部分:BH、任务队列、软中断、tasklet、工作队列
    BH、任务队列已经被淘汰掉了

    1.软中断
    软中断是一种需要用静态定义实现的下半部接口,软中断处理函数处在中断上下文中,所以在软中断处理函数不能睡眠或者阻塞,软中断最多有32个,软中断可同时在不同的处理器上运行,即使是相同的软中断也能同时在不同的处理器上运行。


    在内核中用一个结构体表示一个软中断:
    struct softirq_action{
    void (*action)(struct softirq_action *); 
    void *data;//在2.6.28以后的内核中已经去掉
    };
    在内核中用一个拥有32个元素的结构体来表示这三十二个软中断,每注册一个软中断就占用该数组的一项
    struct softirq_action softirq_vec[];

    使用软中断:
    (1)在内核中添加自己的软中断
    (2)注册软中断open_softirq(n, void(*actions)(struct softirq_action *)),其实就是把数组当中的第n个元素的action = actions 
    (3)触发软中断,一般在中断处理函数返回的时候触发
    raise_softirq(n)。其实就是把一个32为的变量pending的第n为设置1
    (4)在中断函数一返回就开始执行do_softirq,这个函数会按顺序检查pending中的每一位,那位是1就去执行相应的软中断处理函数,优先级是从0到31
    注意:执行raise_softirq(n)的时候默认是要关中断的,完事后再打开中断,如果已经知道中断已经关了可以调用raise_softirq_irqoff(n)优化


    2.tasklet
    tasklet是用过软中实现的,所以实质上它还是软中断,任然具有软中断的属性,只不过是把软中断又封装了一层,其实用的软中断中0和软中断5,但同一个tasklet不能同时运行

    在内核中用一个结构体表示一个tasklet:
    struct tasklet_struct{
    struct tasklet_struct *next;//链表中的下一个tasklet
    unsigned long state;状态:0代表该tasklet没有运行也没有被调度
    TASKLET_STATE_SCHED 代表已经调度了
    TASKLET_STATE_RUN 代表正在执行
    atomic_t count;引用计数,只有为0的时候才能执行
    void (*func)(unsigned long);处理函数
    unsigned long data;给处理函数的参数
    };
    调度tasklet:对于已经调用的tasklet存放在两个tasklet_struct类型的链表中,一个是tasklet_vec,用的是软中断的5,用tasklet_schedule(tasklet_struct *)调度,另一个tasklet_hi_vec,用的是软中断的0用tasklet_hi_schedule(tasklet_struct *)调度

    使用tasklet:
    (1)建立tasklet
    静态定义并初始化一个tasklet
    DECLARE_TASKLET(name, func, data)
    = struct tasklet_struct name = { NULL, 0, ATOMIC_INIT(0), func, data }       DECLARE_TASKLET_DISABLED(name, func, data) 
    = struct tasklet_struct name = { NULL, 0, ATOMIC_INIT(1), func, data } 禁止了tasklet但可以激活


    初始化一个tasklet
    void tasklet_init(struct tasklet_struct *t, void (*func)(unsigned long), unsigned long data);
    (2)调度tasklet
    void tasklet_schedule(struct tasklet_struct *t)
    void tasklet_hi_schedule(struct tasklet_struct *t)
    其实做得就是把相应的tasklet结构体插入到相应的链表中
    (3)中断处理程序退出后就执行tasklet_action和tasklet_hi_action来执行调度的tasklet
    (4)其他API
    void tasklet_disable(struct tasklet_struct *t)//会等待taseklet执行完成,所以肯能会睡眠
    void tasklet_disable_nosync(struct tasklet_struct *t)//不会睡眠
    以上两个函数其实就是把count字段++
    void tasklet_enable(struct tasklet_struct *t)
    void tasklet_hi_enable(struct tasklet_struct *t)
    以上两个函数其实就是把count字段--
    tasklet_kill(struct tasklet_struct *t)//可能会睡眠,因为要等待tasklet执行完
    注意:被disable的tasklet能够被调度,但是不能被执行,如果这个时候还是用tasklet_kill来等待的话那么它会一直睡眠

    3.工作队列
    工作队列用内核线程实现的,工作队列默认创建了一个叫做event的工作者线程来执行被退后的工作,工作队列处在进程上下文中,所以可以阻塞,睡眠,在内核中用一个结构体表示于一个工作:
    struct work_struct {
    atomic_long_t data;
    struct list_head entry;工作队列链表
    work_func_t func;处理函数
    struct lockdep_map lockdep_map;
    };
    struct delayed_work {
    struct work_struct work;
    struct timer_list timer;
    };
    这些结构体被连接成链表,在每个处理器上对应这样一个链表。当一个工作者线程被唤醒时,它会执行它的链表上的所有工作。当工作完毕时, 他会将相应的work_struct对象从链表中移去
    使用工作队列:
    (1)创建工作
    静态创建并初始化一个工作
    DECLARE_WORK(work_struct n, void (*f)(void *)) n是工作的名字, f是处理函数
    DECLARE_DELAYED_WORK(work_struct n, void (*f)(void *)) n是工作的名字, f是处理函数
    初始化一个工作
    INIT_WORK(work_struct *, void (*func)(void *))
    INIT_DELAYED_WORK(work_struct *, void (*func)(void *))
    (2)对工作调度
    int schedule_work(struct work_struct *)
    int schedule_delayed_work(struct delayed_work *dwork, unsigned long delay)
    第二个函数调度的工作会延时delay个滴答数后执行
    (3)中断程序退出后event线程调用run_workqueue就开始执行链表上的工作
    (4)其他API
    void flush_scheduled_work(void) //用来以睡眠的方式等待所有的work执行完才退出,一般在模块卸载的时候用来检查
    int cancle_delayed_work(struct work_struct *work) //用来取消延时执行的操作


    创建一个属于自己的工作队列:
    (1)创建一个新的工作队列和相应的工作者线程,名字是name
    struct workqueue_struct *create_workqueue(const char *name)
    (2)向新的工作队列添加任务
    int queue_work(struct workqueue_struct *wq, struct work_struct *work)
    int queue_delayed_work(struct workqueue_struct *wq, struct delayed_work *work, unsigned long delay);
    (3)刷新工作队列
    void flush_workqueue(struct workqueue_struct *wq)
    (4)销毁工作队列
    void destroy_workqueue(struct workqueue_struct *wq)
    展开全文
  • 可以看到,中断处理程序在处理中断时起了关键作用,也是一个中断程序必可少的部分。不过,现如今的中断处理流程都会分为两部分:上半部分(top half)和下半部分(bottom half)。为什么要将一个中断分为如此两...

    上文中我们通过一个简单的例子分析了一个中断程序的基本结构。可以看到,中断处理程序在处理中断时起到了关键作用,也是一个中断程序必不可少的部分。不过,现如今的中断处理流程都会分为两部分:上半部分(top half)和下半部分(bottom half)。为什么要将一个中断分为如此两部分?下面的几个经典原因可以很好的诠释这个问题。

    1.中断可以随时的打断处理机对其他程序的执行,如果被打断的代码对系统很重要,那么此时中断处理程序的执行时间应该是越短越好。

    2.通过上文我们知道,中断处理程序正在执行时,会屏蔽同条中断线上的中断请求;而更严重的是,如果设置了IRQF_DISABLED,那么该中断服务程序执行是会屏蔽所有其他的中断请求。那么此时应该让中断处理程序执行的越快越好。

    上面的几个例子都要求中断服务程序的执行时间越短越好。一般的,中断处理程序会在上半部分执行。而事实上,几乎所有的情况,上半部分就只执行中断处理程序。因此,我们可以这样认为:一个完整的中断处理流程是由中断处理程序和下半部分共同完成的。

    这样划分是有一定原因的,因为我们必须有一个快速、异步而且简单的处理程序专门来负责对硬件的中断请求做出快速响应,与此同时也要完成那些对时间要求很严格的操作。而那些对时间要求相对宽松,其他的剩余工作则会在稍候的任意时间执行,也就是在所谓的下半部分去执行。

    总之,这样划分一个中断处理过程主要是希望减少中断处理程序的工作量(当然了,理想情况是将全部工作都抛给下半段。但是中断处理程序至少应该完成对中断请求的相应。),因为在它运行期间至少会使得同级的中断请求被屏蔽,这些都直接关系到整个系统的响应能力和性能。而在下半段执行期间,则会允许响应所有的中断。

    和上半段只能通过中断处理程序实现不同的是,下半部可以通过多种机制来完成:小任务(tasklet),工作队列,软中断。在本博客后续的文章当中你会看到,不管是那种机制,它们均为下半部提供了一种执行机制,比上半部灵活多了。至于何时执行,则由内核负责。

    以上是上下部分划分的基本概述,通过tasklet工作队列机制,你可以更深刻的理解下部分的执行。

    展开全文
  • 中断服务函数大多需要在关中断的情况下去执行,但是有的中断服务函数执行需要较长的时间,如果系统长时间关中断就会让其他的中断不到响应,所以把需要关中断执行部分放到中断的上部分,而把对实时性要求不高,不...

    linux2.4 内核中断下半部分(bottom half)理解(请结合linux2.4内核代码看):

    首先先说一下为什么要采用中断下半部分:

    中断服务函数大多需要在关中断的情况下去执行,但是有的中断服务函数执行需要较长的时间,如果系统长时间关中断就会让其他的中断得不到响应,所以把需要关中断执行的部分放到中断的上部分,而把对实时性要求不高,不需要关中断执行的操作放到中断下半部分中去。

    但是中断下半部分也不能够嵌套执行,否则会复杂化代码实现,所以需要有个实现中断下半部分串行化的机制,即bh机制,bh机制的串行化有两方面:

    一是bh函数不能嵌套,二是在多cpu系统中在同一时间只允许一个cpu执行bh函数

    由于第二点使bh函数的执行完全串行化了,当有多个cpu的时候,只能一个cpu执行,降低的系统性能。但是如果放宽条件以前的bh函数就不能用了,所以比较好的办法是保留bh,另外在增设一种或几种机制,并把它们纳入统一的框架中,这就是本文要讲的软中断机制。

    void __init softirq_init()
    {
    int i;
    /*在这个循环中将bh_task_vec[32]中的.action指针全部初始化为bh_action函数,然后在init_bh()中将bh_action函数里要执行的bh_base[]函数指针数组初始化为具体的函 *数,由数组下标做索引。*/
    for (i=0; i<32; i++)

    tasklet_init(bh_task_vec+i, bh_action, i);


    /*在open_softirq函数中将全局数组变量softirq_vec[].action指针初始化为tasklet_action和tasklet_hi_action函数,数组下标为TASKLET_SOFTIRQ和HI_SOFTIRQ*/
    open_softirq(TASKLET_SOFTIRQ, tasklet_action, NULL);
    open_softirq(HI_SOFTIRQ, tasklet_hi_action, NULL);
    }


    那么我们注册的这些函数是在什么时候调用的呢,换句话说,中断下部分是在什么时候被执行的呢。

    在linux2.4内核中在中断执行函数do_IRQ和系统调用返回前会判断系统当前有没有软中断在申请执行,下面是do_IRQ和arch/i386/kernel/entry.S的代码片段:

    if (softirq_active(cpu) & softirq_mask(cpu))
    do_softirq();

    +++++++++++++++++++++++++++++++++++++++

    ENTRY(ret_from_sys_call)
    #ifdef CONFIG_SMP
    movl processor(%ebx),%eax
    shll $CONFIG_X86_L1_CACHE_SHIFT,%eax
    movl SYMBOL_NAME(irq_stat)(,%eax),%ecx# softirq_active
    testl SYMBOL_NAME(irq_stat)+4(,%eax),%ecx# softirq_mask
    #else
    movl SYMBOL_NAME(irq_stat),%ecx# softirq_active
    testl SYMBOL_NAME(irq_stat)+4,%ecx# softirq_mask
    #endif
    jne   handle_softirq


    handle_softirq:
    call SYMBOL_NAME(do_softirq)
    jmp ret_from_intr

    看到红色标记的函数了吧,对了,就是执行了do_softirq()函数的,代码如下

    asmlinkage void do_softirq()
    {
    int cpu = smp_processor_id();
    __u32 active, mask;

    if (in_interrupt())
    return;

    local_bh_disable();

    local_irq_disable();
    mask = softirq_mask(cpu);
    active = softirq_active(cpu) & mask;

    if (active) {
    struct softirq_action *h;

    restart:
    /* Reset active bitmask before enabling irqs */
    softirq_active(cpu) &= ~active;

    local_irq_enable();

    /*将数组softirq_vec的指针赋给h,然后执行action指针指向的函数,这个action指向的函数是我们在前面open_softirq函数中赋值的*/
    h = softirq_vec;
    mask &= ~active;

    do {
    if (active & 1)
    h->action(h);
    h++;
    active >>= 1;
    } while (active);

    local_irq_disable();

    active = softirq_active(cpu);
    if ((active &= mask) != 0)
    goto retry;
    }

    local_bh_enable();

    /* Leave with locally disabled hard irqs. It is critical to close
    * window for infinite recursion, while we help local bh count,
    * it protected us. Now we are defenceless.
    */
    return;

    retry:
    goto restart;
    }


    那么,既然do_IRQ里面执行的是softirq_vec数组里的函数,即要么是tasklet_action()要么是tasklet_hi_action(),但和我么提到的bh_task_vec数组函数指针又有什么关系呢要明白这个我们还得再介绍两个函数即:

    static inline void tasklet_schedule(struct tasklet_struct *t)
    {
    if (!test_and_set_bit(TASKLET_STATE_SCHED, &t->state)) {
    int cpu = smp_processor_id();
    unsigned long flags;

    local_irq_save(flags);
    t->next = tasklet_vec[cpu].list;
    tasklet_vec[cpu].list = t;
    __cpu_raise_softirq(cpu, TASKLET_SOFTIRQ);
    local_irq_restore(flags);
    }
    }


    static inline void tasklet_hi_schedule(struct tasklet_struct *t)
    {
    if (!test_and_set_bit(TASKLET_STATE_SCHED, &t->state)) {
    int cpu = smp_processor_id();
    unsigned long flags;


    local_irq_save(flags);
    t->next = tasklet_hi_vec[cpu].list;
    tasklet_hi_vec[cpu].list = t;//注意这两句赋值的意思是将 t 插入到原来的链表头前,同时把链表头指针指向 t ,使t成为了新的链表头了。
    __cpu_raise_softirq(cpu, HI_SOFTIRQ);
    local_irq_restore(flags);
    }
    }


    struct tasklet_head
    {
    struct tasklet_struct *list;
    } __attribute__ ((__aligned__(SMP_CACHE_BYTES)));

    extern struct tasklet_head tasklet_vec[NR_CPUS];
    extern struct tasklet_head tasklet_hi_vec[NR_CPUS];

    这两个函数的函数都是struct tasklet_struct *t。

    tasklet_hi_schedule()中将参数t 链接到tasklet_hi_vec数组元素中的链表头中,这个函数是有bh_mark函数调用的,实现如下:

    static inline void mark_bh(int nr)
    {
    tasklet_hi_schedule(bh_task_vec+nr);
    }

    即把bh_task_vec数组里的第nr个函数指针挂载到tasklet_hi_vec链表头中。

    而tasklet_schedule函数是在其它软中断的函数中被调用的,如sun_kbd_init--->tasklet_schedule(&keyboard_tasklet);即把要注册的tasklet_struct结构体挂载到tasklet_vec链表中。

    注意:bh函数执行的上下文是完全串行化的,而其它软中断函数只是在非中断上下文中执行(即软中断函数和中断函数中)。

    好了,基础知识已经讲完了,现在来讲在do_softirq函数中执行的tasklet_action或者tasklet_hi_action函数。这两个函数非常相近,除了我在下面标记的红色部分不一样以外:


    static void tasklet_action(struct softirq_action *a)
    {
    int cpu = smp_processor_id();
    struct tasklet_struct *list;


    local_irq_disable();
    list = tasklet_vec[cpu].list;
    tasklet_vec[cpu].list = NULL;

    local_irq_enable();


    while (list != NULL) {
    struct tasklet_struct *t = list;


    list = list->next;


    if (tasklet_trylock(t)) {
    if (atomic_read(&t->count) == 0) {
    clear_bit(TASKLET_STATE_SCHED, &t->state);


    t->func(t->data);
    /*
    * talklet_trylock() uses test_and_set_bit that imply
    * an mb when it returns zero, thus we need the explicit
    * mb only here: while closing the critical section.
    */
    #ifdef CONFIG_SMP
    smp_mb__before_clear_bit();
    #endif
    tasklet_unlock(t);
    continue;
    }
    tasklet_unlock(t);
    }
    local_irq_disable();
    t->next = tasklet_vec[cpu].list;
    tasklet_vec[cpu].list = t;
    __cpu_raise_softirq(cpu, TASKLET_SOFTIRQ);
    local_irq_enable();
    }
    }


    static void tasklet_hi_action(struct softirq_action *a)
    {
    int cpu = smp_processor_id();
    struct tasklet_struct *list;


    local_irq_disable();
    list = tasklet_hi_vec[cpu].list;
    tasklet_hi_vec[cpu].list = NULL;

    local_irq_enable();


    while (list != NULL) {
    struct tasklet_struct *t = list;


    list = list->next;


    if (tasklet_trylock(t)) {
    if (atomic_read(&t->count) == 0) {
    clear_bit(TASKLET_STATE_SCHED, &t->state);


    t->func(t->data);
    tasklet_unlock(t);
    continue;
    }
    tasklet_unlock(t);
    }
    local_irq_disable();
    t->next = tasklet_hi_vec[cpu].list;
    tasklet_hi_vec[cpu].list = t;
    __cpu_raise_softirq(cpu, HI_SOFTIRQ);
    local_irq_enable();
    }
    }

    而这两个不同的数组就是我们前面说的tasklet_hi_vec和tasklet_vec,所以tasklet_action和tasklet_hi_action其实执行的就是tasklet_hi_vec和tasklet_vec链表里面的函数了,而这两个链表里面的函数就是我们最前面注册的中断下半部分的函数,即bh函数和其他软中断函数,这样通过softirq机制就把以前老机制的bh函数和新机制的软中断函数给统一到一个框架里面去了。


















    展开全文
  • 猫眼演员信息爬取网站需求代码实现 增量爬虫 通过爬虫程序监测某网站数据更新的情况,以便可以爬取该网站更新出的新数据。如 何进行增量式的爬取工作: 在发送请求之前判断这个 URL 是否爬取过。 在解析内容后...

    增量爬虫

    通过爬虫程序监测某网站数据更新的情况,以便可以爬取到该网站更新出的新数据。如 何进行增量式的爬取工作:

    1. 在发送请求之前判断这个 URL 是否爬取过。
    2. 在解析内容后判断这部分内容是否爬取过。
    3. 写入存储介质时判断内容是否在介质中。

    不难发现,其实增量爬取的核心是去重,至于去重的操作在哪个步骤起作用,只能说各 有利弊。在我看来,前两种思路需要根据实际情况取一个(也可能都用)。

    • 第一种思路适合不断有新页面出现的网站,例如:小说的新章节,每天的最新新闻等等。
    • 第二种思路则适合页面内容会更新的网站。
    • 第三个思路是相当于是最后的一道防线。

    这样做可以最大程度上达到去重的目的。去重方法:

    1. 将爬取过程中产生的 url 进行存储,存储在 redis 的 set 中。当下次进行数据爬取时, 首先判断即将要发起请求的 url 是否在存储 url 的 set 中存在,如果不存在则进行请求。
    2. 对爬取到的网页内容进行唯一标识的限定,然后将该唯一标识存储至 redis 的 set 中。 当下次爬取到网页数据的时候,在进行持久化存储之前,首先可以先判断该数据的唯一标识 在 redis 的 set 中是否存在,进一步决定是否进行持久化存储.

    案例---->猫眼演员信息爬取

    网站

    https://maoyan.com/films/celebrity/29410

    需求

    (1)该案例爬取策略是通过多个演员作为起始任务,后面的演员主要从相关影人处获 取。
    (2)相关影人有可能重复,设计好去重。
    (3)自定义调度器来设置下载的任务。

    代码实现

    import hashlib
    import threading
    import requests
    from lxml import etree
    from queue import Queue
    import redis
    class MaoYanActor(threading.Thread):
        def __init__(self,q_actors,name):
            super().__init__()
            self.name=name
            self.q_actors=q_actors
            self.redis_=redis.Redis()
            self.s=requests.session()
            self.headers={
                'User-Agent':'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/61.0.3163.79 Safari/537.36 Maxthon/5.2.6.1000',
                'Host':'maoyan.com',
                'Connection':'keep-alive',
            }
            self.s.headers=self.headers
    
        def get_md5(self,value):
            return hashlib.md5(bytes(value, encoding='utf-8')).hexdigest()
    
        def get_xpath(self,url):
            rep=self.s.get(url).text
            return etree.HTML(rep)
        def url_seen(self,url):
            fp=self.get_md5(url)
            add=self.redis_.sadd('maoyan_actor:actor_urls',fp)
            return add==0
        def parse_actor(self,tree):
            actor_name=tree.xpath('//div[@class="shortInfo"]/p[1]/text()')[0]
        def run(self):
            while True:
                if q_actors.empty():
                    break
                # 获取url
                url = q_actors.get()
                print(f"{self.name}==========@{url.split('/')[-1]}")
                if not self.url_seen(url):
                    # 请求,得到相应内容
                    tree = self.get_xpath(url)
                    # 解析当前演员信息并保存
                    self.parse_actor(tree)
                    # 获取响应电影人的信息--url
                    relation_actors = tree.xpath('//div[@class="rel-item"]/a/@href')
                    for actors in relation_actors:
                        self.q_actors.put('https://maoyan.com' + actors)
    
    if __name__ == '__main__':
        # 处理url列表
        start_urls = [
            'https://maoyan.com/films/celebrity/29410',
            'https://maoyan.com/films/celebrity/789',
            'https://maoyan.com/films/celebrity/28383',
            'https://maoyan.com/films/celebrity/33280',
            'https://maoyan.com/films/celebrity/28449'
        ]
        q_actors = Queue()
        for url in start_urls:
            q_actors.put(url)
        name_list=['线程1','线程2','线程3','线程4']
        for name in name_list:
            print(name)
            m=MaoYanActor(q_actors,name)
            m.start()
    
    
    展开全文
  • 昨天遇到一个问题,在写入数据STM32F103单片机的Flash中时会出现串口中断接收数据丢失现象,但是我设置的串口接收中断优先级是最高的,并且没有哪里将全局中断关闭很长时间(除了操作系统部分内核代码执行的时候...
  • 这个提示是告诉你,你的程序...看给出的代码,break是中断循环,如果你只要执行一次,那使用循环是没必要的… 如果你要一直循环下去,break就是多余了(break一般来说都放在循环里面的条件判断里面…) return param; ...
  • 中断发生以后,CPU跳内核设置好的中断处理代码中去,由这部分内核代码来处理中断。这个处理过程中的上下文就是中断上下文。 为什么可能导致睡眠的函数都能在中断上下文中使用呢? 首先睡眠的含义是将进程置于...
  • 代码、数据、BSS的内容是可执行文件中对应的内容,加载程序并不是把它们的内容从可执行程序中填充内存中,而是将它们的信息(基地址、长度等)更新进程控制块(task_struct)中,当CPU第 一次实际寻址执行的时候...
  • 中断和异常

    2018-10-29 21:59:00
    课后问老师的部分:  1、SRAM是占了0.5GB的地址...函数调用的地方是确定的,但是中断执行往往受GPIO口的上下脉冲控制,调用的时间和次数是确定的。也正是因为这个原因,我们的中断和异常的代码里面并没有...
  • 转自:http://bbs.chinaunix.net/thread-1969752-1-1.html 中断发生以后,CPU跳内核设置好的中断处理代码中去,由这部分内核代码来处理中断。这个处理过程中的上下文就是中断上下文。为什么可能导致睡眠的函数都...
  • 前面做的两个实验,在裸机下跑PS的timer计时器中断,PLPS的按键中断均工作正常,但是将代码移植freertos工程时,中断不执行,跟中断无关联的task可以运行。产生这个问题的主要原因,在于PS部分只有一个中断控制...
  • 在裸机下跑PS的timer计时器中断,PLPS的按键中断均工作正常,但是将代码移植freertos工程时,中断不执行,跟中断无关联的task可以运行。产生这个问题的主要原因,在于PS部分只有一个中断控制器,因此我们只能...
  • 最简洁的中断现场保护

    千次阅读 2016-07-10 22:23:32
    阅读韦老师的中断现场保护和恢复代码,感觉下面红色部分代码似乎用不到,因为恢复现场的时候,lr的内容早就放在堆栈里面,并且要推送到pc了,我把下面代码的红色部分去掉,做实验,可以顺利进出中断,可见这几句话...
  • 单独运行该部分代码,能够打印结果(False) <pre><code> Traceback (most recent call last): File "C:\Install\Python35\lib\site-packages\airtest\cli\runner.py", line 65, in ...
  • S5PV210-裸机中断

    2018-01-03 09:21:42
    - 在异常处理程序IRQ_handle中将会执行现场的保护与恢复,以及最重要的中断处理函数(注意,这里就来到了中断处理部分,而不是异常处理了,相当于是一个两级的机制)。 - 在中断处理程序irq_handler中(代码如下)
  • 单片机中断函数中的全局变量问题

    千次阅读 2019-06-26 15:42:55
    然后呢,我想在中断函数里写太多代码,便只在中断函数里放了一个全局变量,按下按钮进入中断时改变该变量的值,然后在主程序中判断该值,来执行接下来的程序,部分程序代码如下: void main...
  • 操作系统之中断(四)

    千次阅读 2016-02-16 13:09:36
    上一篇文章中简单的提到了IDT是记录了终端号和中断函数之间的...切换这一步大约位于BIOS加载完bootsector后,执行部分或者执行完bootsector代码时进行的,这么靠前是因为如果切换就无法进入32位模式,寻址空间还是
  • 9.1 临界段简介: 临界段代码,也称作临界域,是一段... 临界段用一句话概括就是一段在执行的时候能被中断代码段。在 uCOS 里面,这个临界段最常出现的就是对全局变量的操作,全局变量就好像是一个枪把子,...
  • linux ------ 中断处理及延时操作

    千次阅读 2014-02-25 15:27:19
    下半部有Tasklet与工作队列2种,是中断处理的延时操作,如果中断需要处理较复杂的事务,一般会放下半部来执行。Tasklet运行在中断上下文,允许睡眠。工作队列运行在进程上下文,可以睡眠,意味着会引起进程的...
  • 因为不管是QNetworkAccessManager还是QNetworkReply都接收不到错误提示,因为执行get的时候确实网络连接无误。所以程序在下载时网络中断就无法给予提示。 尝试过加QTimer,但是因为服务器的速度不给力,所以一个并...
  • STM8S103xx串口一直中断

    2013-01-30 11:57:00
    STM8S103xx串口一直中断 前段时间做了一个项目,有部分功能是接收串口命令...IDE用的是IAR,虽说可以直接在线调试,但这个IDE能在运行时设置断点,所以找了好久一直没有查程序停在了哪里,直接代码单步执行跑...
  • 这里紧跟上一篇谈的0x7C00开始执行,当然此时是在实模式下面,执行最初先禁止中断,因为现在堆栈都没有就算有中断执行,所以第一步禁止中断,一直设置好堆栈才打开中断。这一部分代码很简单,利用ax将段...
  •  0002 系统找不到指定的文件。  0003 系统找不到指定的路径。  0004 系统无法打开文件。  0005 拒绝访问。  0006 句柄无效。  0007 存储区控制块已损坏。  0008 可用的存储区不足, 无法执行该...
  • 我去除了一些暂时用不到代码,这里我们把目光聚焦在<code>middleware这个数组即可。 <pre><code> javascript function Application() { // xxx this.middleware = []; // 这个数组就是用来装一个个...
  • ADS(针对ARM处理器核的C语言编译器)的策略是提供完整的启动代码,启动代码不足部分或者由厂商提供,或者自己编写。启动代码划分为4个文件:Vectors.c、Init.s、Target.c、 Target.h。Vectors.c包含异常向量表、...
  • WIN XP蓝屏代码大全

    2013-08-08 12:29:21
    ◇解决方案:如果是在安装Windows时出现, 有可能是由于你的电脑达不到安装Windows的最小内存和磁盘要求. 4、0x0000001E:KMODE_EXCEPTION_NOT_HANDLED ◆错误分析:Windows内核检查到一个非法或者未知的进程指令, 这个...
  • Java第三部分

    2016-08-18 22:35:40
    1,异常概述在程序中,错误可能产生于程序员没有预料的各种情况,或者是超出了程序员可控范围的环境因素,如用户的坏数据、试图打开一个根本存在的文件等。 Java中这种在程序运行时可能出现的一些错误称为异常。...
  • 我虽然已经有多年代码了,但看这本书的时候,让我又重新感受做程序员的乐趣:用代码建设属于自己的系统,让电脑听从自己的指令,对系统的每个部分都了如指掌。 黑客(hacker)实际是褒义词,维基百科的解释是...

空空如也

空空如也

1 2 3 4 5 ... 8
收藏数 157
精华内容 62
关键字:

中断部分代码执行不到