虚拟网站免费注册,如何建设网站安全,学python能干嘛,全媒体广告策划营销文章目录 前言并发和竞争原子操作自旋锁信号量互斥体 中断中断简介中断API上半部和下半部设备树分析中断号获取源码 后续参考文献 前言
中断会引起线程的切换#xff0c;并发和竞争也是对线程切换的一种灵活保护和处理#xff0c;因此这里将中断和并发与竞争放在一块讲解说明… 文章目录 前言并发和竞争原子操作自旋锁信号量互斥体 中断中断简介中断API上半部和下半部设备树分析中断号获取源码 后续参考文献 前言
中断会引起线程的切换并发和竞争也是对线程切换的一种灵活保护和处理因此这里将中断和并发与竞争放在一块讲解说明。
并发和竞争
为保护共享资源防止进行并发访问Linux 内核提供的几种并发和竞争的处理方法以下是对这几种处理方法的详细分析
原子操作
起因当运行如下图的两个线程时正常情况是线程 A 将 ro 地址的变量设置为 10线程 B 将将 ro 地址的变量设置为 20。 但是实际上的执行流程可能如下图所示要解决这个问题就要保证的三行汇编指令作为一个整体运行也就是作为一个原子存在。
常用API函数 使用方法非常简单如下所示
atomic_t v ATOMIC_INIT(0); /* 定义并初始化原子变零 v0 */
atomic_set(v, 10); /* 设置 v10 */
atomic_read(v); /* 读取 v 的值肯定是 10 */
atomic_inc(v); /* v 的值加 1v11 */适用场景两组原子操作 API 函数一组是对整形变量进行操作的一组是对位进行操作。 弊端仅仅用于对变量的保护。
自旋锁
起因原子操作只能对整形变量或者位进行保护但是在实际的使用环境中怎么可能只有整形 变量或位这么简单的临界区。举个最简单的例子设备结构体变量就不是整型变量我们对于 结构体中成员变量的操作也要保证原子性在线程 A 对结构体变量使用期间应该禁止其他的 线程来访问此结构体变量这些工作原子操作都不能胜任需要本节要讲的锁机制在 Linux 内核中就是自旋锁。 原理当一个线程要访问某个共享资源的时候首先要先获取相应的锁锁只能被一个线程持有 只要此线程不释放持有的锁那么其他的线程就不能获取此锁。对于自旋锁而言如果自旋锁正在被线程 A 持有线程 B 想要获取自旋锁那么线程 B 就会处于忙循环-旋转-等待状态线程 B 不会进入休眠状态或者说去做其他的处理而是会一直傻傻的在那里“转圈圈”的等待锁可用。 API函数 除了上表第一个方法外也可以直接用下面这个方式定义
spinlock_t lock; //定义自旋锁死锁现象如下图所示线程 A 先运行并且获取到了 lock 这个锁当线程 A 运行 functionA 函 数的时候中断发生了中断抢走了 CPU 使用权。右边的中断服务函数也要获取 lock 这个锁 但是这个锁被线程 A 占有着中断就会一直自旋等待锁有效。但是在中断服务函数执行完之前线程 A 是不可能执行的线程 A 说“你先放手”中断说“你先放手”场面就这么僵持着 死锁发生最好的解决方法就是获取锁之前关闭本地中断Linux 内核提供了相应的 API 函数如下表所示。 内核很庞大运行也是“千变万化”我们是很难确定某个时刻的中断状态因此不推荐使用 spin_lock_irq/spin_unlock_irq。建议使用 spin_lock_irqsave/ spin_unlock_irqrestore因为这一组函 数会保存中断状态在释放锁的时候会恢复中断状态。一般在线程中使用 spin_lock_irqsave/ spin_unlock_irqrestore在中断中使用 spin_lock/spin_unlock 其它衍生锁在自旋锁的基础上衍生出来了读写自旋锁顺序锁等。 适用场景自旋锁适合用于短时期的轻量级加锁 缺点1、自旋锁的线程会一直处于自旋状态2、临界区比较大运行时间比较长的话要选择其他的并发处理方式比如信号量和互斥体3、自旋锁保护的临界区内不能调用任何可能导致线程休眠的 API 函数不能递归申请自旋锁否则可能导致死锁。
信号量
起因自选锁在获取锁时会将本线程一直处于忙碌状态直到锁被释放相比于自旋锁信号量可以使该线程进入休眠状态当信号量被释放时就会提醒该线程退出休眠态执行接下来的程序。 工作原理信号量有一个信号量值相当于一个房子有 10 把钥匙这 10 把钥匙就相当于信号量值为 10。因此可以通过信号量来控制访问共享资源的访问数量如果要想进房间那就要先获取 一把钥匙信号量值减 1直到 10 把钥匙都被拿走信号量值为 0这个时候就不允许任何人 进入房间了因为没钥匙了。如果有人从房间出来那他要归还他所持有的那把钥匙信号量 值加 1此时有 1 把钥匙了那么可以允许进去一个人。 注意事项通过信号量控制访问资源的线程数在初始化的时候将信号量值设置得大于 1那么这个信号量就是计数型信号量计数型信号量不能用于互斥访问因为它允许多个线程同时访问共享资源。如果要互斥的访问共享资源那么信号量的值就不能大于 1此时的信号量就是一个二值信号量。 API函数在选择API函数时要注意区分是否会进入休眠态具体函数如下 使用方法Linux 内核使用 semaphore 结构体表示信号量因此使用时要先定义一个结构体变量或者上述的第一个API函数具体方法如下所示
struct semaphore sem; /* 定义信号量 */
sema_init(sem, 1); /* 初始化信号量 */
down(sem); /* 申请信号量 */
/* 临界区 */
up(sem); /* 释放信号量 */适用场景因为信号量可以使等待资源线程进入休眠状态因此适用于那些占用资源比较久的场合。 缺点信号量不能用于中断中因为信号量会引起休眠中断不能休眠如果共享资源的持有时间比较短那就不适合使用信号量了因为频繁的休眠、切换线程引起的开销要远大于信号量带来的那点优势。
互斥体
起因虽然信号量的值设置为 1 就可以使用信号量进行互斥访问了但是 Linux 提供了一个比信号量更专业的机制来进行互斥它就是互斥体 mutex。 工作原理工作原理类似信号量只是信号量设置为1的一种特殊形式比如 A 与 B、C 合租了一套房子这个 房子只有一个厕所一次只能一个人使用。某一天早上 A 去上厕所了过了一会 B 也想用厕 所因为 A 在厕所里面所以 B 只能等到 A 用来了才能进去。B 要么就一直在厕所门口等着 等 A 出来这个时候就相当于自旋锁。B 也可以告诉 A让 A 出来以后通知他一下然后 B 继续回房间睡觉。 API函数具体函数如下 使用方法Linux 内核使用 mutex 结构体表示互斥体因此使用时要先定义一个结构体变量或者上述的第一个API函数具体方法如下
struct mutex lock; /* 定义一个互斥体 */
mutex_init(lock); /* 初始化互斥体 */
mutex_lock(lock); /* 上锁 */
/* 临界区 */
mutex_unlock(lock); /* 解锁 */适用场景需要互斥访问的地方建议使用 mutex比信号量设置为1更加专业等待资源线程进入休眠状态因此适用于那些占用资源比较久的场合。 缺点mutex 可以导致休眠因此不能在中断中使用 mutex中断中只能使用自旋锁一个线程可以持有 mutex因此必须由 mutex 的持有者释放 mutex。并且 mutex 不能递归上锁和解锁。
中断
不管是裸机MCU开发还是 Linux 下的驱动开发中断都是频繁使用的功能接下来将讲解在linux驱动下中断的实现。
中断简介
中断控制器GIC 是 ARM 公司给 Cortex-A/R 内核提供的一个中断控制器类似 Cortex-M 内核中的 NVIC。目前 GIC 有 4 个版本:V1~V4V1 是最老的版本已经被废弃了。V2~V4 目前正在大 量的使用。GIC V2 是给 ARMv7-A 架构使用的比如 Cortex-A7、Cortex-A9、Cortex-A15 等 V3 和 V4 是给 ARMv8-A/R 架构使用的也就是 64 位芯片使用的。 左侧部分就是中断源中间部分就是 GIC 控制器最右侧就是中断控制器向处理器内核发送中断信息通过四个信号报给 ARM 内核。
这四个信号的含义如下 VFIQ:虚拟快速 FIQ。 VIRQ:虚拟外部 IRQ。 FIQ:快速中断 IRQ。 IRQ:外部中断 IRQ。中断源分为分为三类 ①、SPI(Shared Peripheral Interrupt),共享中断顾名思义所有 Core 共享的中断这个是最 常见的那些外部中断都属于 SPI 中断(注意不是 SPI 总线那个中断) 。比如按键中断、串口 中断等等这些中断所有的 Core 都可以处理不限定特定 Core。 ②、PPI(Private Peripheral Interrupt)私有中断我们说了 GIC 是支持多核的每个核肯定 有自己独有的中断。这些独有的中断肯定是要指定的核心处理因此这些中断就叫做私有中断。 ③、SGI(Software-generated Interrupt)软件中断由软件触发引起的中断通过向寄存器 GICD_SGIR 写入数据来触发系统会使用 SGI 中断来完成多核之间的通信。为了区分这些不同的中断源肯定要给他们分配一个唯一 ID这些 ID 就是 中断 ID。每一个 CPU 最多支持 1020 个中断 ID中断 ID 号为 ID0~ID1019。 ID0 ~ ID15这 16 个 ID 分配给 SGI。 ID16 ~ ID31这 16 个 ID 分配给 PPI。 ID32~ID1019这 988 个 ID 分配给 SPI像 GPIO 中断、串口中断等这些外部中断。
中断流程①、使能中断初始化相应的寄存器。 ②、注册中断服务函数也就是向 irqTable 数组的指定标号处写入中断服务函数 ②、中断发生以后进入 IRQ 中断服务函数在 IRQ 中断服务函数在数组 irqTable 里面查找 具体的中断处理函数找到以后执行相应的中断处理函数。
中断API
关于中断的API函数这里只做简要说明关于具体的形参返回值等信息用的时候再去百度学习更方便更详细。
request_irq 函数用于申请中断free_irq 函数释放 掉相应的中断irqreturn_t (*irq_handler_t) (int, void *)使用 request_irq 函数申请中断的时候需要设置中断处理函数此函数是中断处理函数格式还有一些中断的使能和禁止函数
上半部和下半部
原理中断处理函数一定要快点执行完毕越短越好但是现实往往是残酷的有些中断处理过程就是比较费时间我们必须要对其进行处理缩小中断处理 函数的执行时间。比如电容触摸屏通过中断通知 SOC 有触摸事件发生SOC 响应中断然后 通过 IIC 接口读取触摸坐标值并将其上报给系统。但是我们都知道 IIC 的速度最高也只有 400Kbit/S所以在中断中通过 IIC 读取数据就会浪费时间。我们可以将通过 IIC 读取触摸数据 的操作暂后执行中断处理函数仅仅相应中断然后清除中断标志位即可。这个时候中断处理 过程就分为了两部分 上半部上半部就是中断处理函数那些处理过程比较快不会占用很长时间的处理就可 以放在上半部完成。 下半部如果中断处理过程比较耗时那么就将这些比较耗时的代码提出来交给下半部 去执行这样中断处理函数就会快进快出。 关于什么放在上半部什么放在下半部有一定的讲究以下是一些参考办法 ①、如果要处理的内容不希望被其他中断打断那么可以放到上半部。 ②、如果要处理的任务对时间敏感可以放到上半部。 ③、如果要处理的任务与硬件有关可以放到上半部 ④、除了上述三点以外的其他任务优先考虑放到下半部。
上半部处理很简单直接编写中断处理函数就行了Linux 核提供了多种下半部机制软中断、tasklet、工作队列。 软中断
先使用 open_softirq 函数注册对应的软中断处理函数nr要开启的软中断在 kernel/softirq.c 文件中一共定义了 10 个软中断action软中断对应的处理函数。void open_softirq(int nr, void (*action)(struct softirq_action *))定义好之后只需要通过 raise_softirq 函数触发中断即可进入处理函数。void raise_softirq(unsigned int nr)tasklettasklet 是利用软中断来实现的另外一种下半部机制在软中断和 tasklet 之间建议大家使用 tasklet。Linux 内核使用 tasklet_struct 结构体来表示 tasklet
struct tasklet_struct
{struct tasklet_struct *next;unsigned long state;atomic_t count;void (*func)(unsigned long);unsigned long data;
};func 函数就是 tasklet 要执行的处理函数用户定义函数内容相当于中断处理函数如果要使用 tasklet必须先定义一个 tasklet然后使用 tasklet_init 函数初始化 tasklet也可以使用宏 DECLARE_TASKLET 来一次性完成 tasklet 的定义和初始化在上半部也就是中断处理函数中调用 tasklet_schedule 函数就能使 tasklet 在合适的时间运行。
/* 定义 taselet */
struct tasklet_struct testtasklet;
/* tasklet 处理函数 */
void testtasklet_func(unsigned long data) { /* tasklet 具体处理内容 */
}
/* 中断处理函数 */
irqreturn_t test_handler(int irq, void *dev_id) { ...... /* 调度 tasklet */ tasklet_schedule(testtasklet); ......
}
/* 驱动入口函数 */
static int __init xxxx_init(void)
{ ...... /* 初始化 tasklet */tasklet_init(testtasklet, testtasklet_func, data); /* 注册中断处理函数 */ request_irq(xxx_irq, test_handler, 0, xxx, xxx_dev);......
}工作队列工作队列是另外一种下半部执行方式工作队列在进程上下文执行工作队列将要推后的 工作交给一个内核线程去执行因为工作队列工作在进程上下文因此工作队列允许睡眠或重新调度。如果你要推后的工作可以睡眠那么就可以选择工作队列否则的话就只能选择软中断或 tasklet。 Linux 内核使用 work_struct 结构体表示一个工作这些工作组织成工作队列工作队列使用 workqueue_struct 结构体表示。Linux 内核使用工作者线程(worker thread)来处理工作队列中的各个工作Linux 内核使用 worker 结构体表示工作者线程每个 worker 都有一个工作队列工作者线程处理自己工 作队列中的所有工作。在实际的驱动开发中我们只需要定义工作(work_struct)即可关于工作 队列和工作者线程我们基本不用去管。
定义一个 work_struct 结构体变量即可然后使用 INIT_WORK 宏来初始化工作(也可以使用 DECLARE_WORK 宏一次性完成工作的创建和初始化)和 tasklet 一样工作也是需要调度才能运行的工作的调度函数为 schedule_work。
/* 定义工作(work) */
struct work_struct testwork;
/* work 处理函数 */
void testwork_func_t(struct work_struct *work); { /* work 具体处理内容 */
}
/* 中断处理函数 */
irqreturn_t test_handler(int irq, void *dev_id) { ...... /* 调度 work */ schedule_work(testwork); ......
}
/* 驱动入口函数 */
static int __init xxxx_init(void) { ...... /* 初始化 work */ INIT_WORK(testwork, testwork_func_t); /* 注册中断处理函数 */ request_irq(xxx_irq, test_handler, 0, xxx, xxx_dev); ......
}设备树分析
如果使用设备树的话就需要在设备树中设置好中断属性信息Linux 内核通过读取设备树 中的中断属性信息来配置中断。对于中断控制器而言设备树绑定信息参考文档 Documentation/devicetree/bindings/arm/gic.txt。中断涉及到设备树的内容比较多这里就详细分析一下便于掌握设备树的相关知识。
中断控制器节点的设备树属性如下所示 intc: interrupt-controller00a01000 {compatible arm,cortex-a7-gic;#interrupt-cells 3;interrupt-controller;reg 0x00a01000 0x1000,0x00a02000 0x100;};compatible 属性值为“arm,cortex-a7-gic”在 Linux 内核源码中搜索“arm,cortex-a7gic”即可找到 GIC 中断控制器驱动文件#interrupt-cells 和#address-cells、#size-cells 一样。表示此中断控制器下设备的 cells 大小对于设备而言会使用 interrupts 属性描述中断信息#interrupt-cells 描述了 interrupts 属性的 cells 大小也就是一条信息有几个 cells。每个 cells 都是 32 位整形值对于 ARM 处理的 GIC 来说一共有 3 个 cells这三个 cells 的含义如下 第一个 cells中断类型0 表示 SPI 中断1 表示 PPI 中断。第二个 cells中断号对于 SPI 中断来说中断号的范围为 0 ~ 987对于 PPI 中断来说中断 号的范围为 0~15。第三个 cells标志bit[3:0]表示中断触发类型为 1 的时候表示上升沿触发为 2 的时候 表示下降沿触发为 4 的时候表示高电平触发为 8 的时候表示低电平触发。bit[15:8]为 PPI 中 断的 CPU 掩码。 interrupt-controller 节点为空表示当前节点是中断控制器。
对于 gpio 来说gpio 节点也可以作为中断控制器比如 gpio5 节点内容如下所示
gpio5: gpio020ac000 {compatible fsl,imx6ul-gpio, fsl,imx35-gpio;reg 0x020ac000 0x4000;interrupts GIC_SPI 74 IRQ_TYPE_LEVEL_HIGH,GIC_SPI 75 IRQ_TYPE_LEVEL_HIGH;gpio-controller;#gpio-cells 2;interrupt-controller;#interrupt-cells 2;
};interrupts 描述中断源信息对于 gpio5 来说一共有两条信息中断类型都是 SPI 触发电平都是 IRQ_TYPE_LEVEL_HIGH。不同之处在于中断源一个是 74一个是 75《IMX6ULL 参考手册》的“Chapter 3 Interrupts and DMA Events”章节可以看出GPIO5 一共用了 2 个中断号一个是 74一个是 75。其中 74 对 应 GPIO5_IO00 ~ GPIO5_IO15 这低 16 个 IO75 对应 GPIO5_IO16 ~ GPIOI5_IO31 这高 16 位 IO。interrupt-controller 表明了 gpio5 节点也是个中断控制器#interrupt-cells 修改为 2因为在后面对gpio5的某一个引脚进行 interrupts 属性配置时就只需要配置两个 cells即可下面以gpio1为例。
下面这个是 key 按键的设备树配置信息。
key{#address-cells 1;#size-cells 1;compatible hbb-key;pinctrl-names default;pinctrl-0 pinctrl_key;key-gpio gpio1 18 GPIO_ACTIVE_LOW;interrupt-parent gpio1;interrupts 18 IRQ_TYPE_EDGE_BOTH;status okay;
};interrupt-parent 属性设置中断控制器这里使用 gpio5 作为中断控制器。interrupts 设置中断信息0 表示 GPIO5_IO008 表示低电平触发。
以上便是设备树对一个中断信息配置的全过程。
中断号获取
编写驱动的时候需要用到中断号我们用到中断号中断信息已经写到了设备树里面因 此可以通过 irq_of_parse_and_map 函数从 interupts 属性中提取到对应的设备号
unsigned int irq_of_parse_and_map(struct device_node *dev, int index)如果使用 GPIO 的话可以使用 gpio_to_irq 函数来获取 gpio 对应的中断号
int gpio_to_irq(unsigned int gpio)通过上面两种方式即可获得中断号。
源码
源码大家可以参考参考文献github链接都已经上传上去了 其中实验11讲解了按键的具体中断实现没有用下半部的概念去实现但是用定时器进行了消抖。先初始化io口然后配置中断获取中断号并申请中断当中断发生时进入中断服务函数在中断服务函数中开启定时器定时10ms后进入定时服务函数读取按键是否按下的信息最终在用户态通过read读取内核态的数据内核态通过read函数里面的copy_to_user到用户态read函数的buf里面用户态read函数的返回值reg即为内核态read函数的返回值。
后续
后续对原子操作自旋锁信号量互斥体以及中断有了更深入一步的理解之后会继续更新。
参考文献
个人专栏系列文章正点原子嵌入式驱动开发指南对代码有兴趣的同学可以查看链接https://github.com/NUAATRY/imx6ull_dev 文章转载自: http://www.morning.xsklp.cn.gov.cn.xsklp.cn http://www.morning.sfnjr.cn.gov.cn.sfnjr.cn http://www.morning.rqqmd.cn.gov.cn.rqqmd.cn http://www.morning.rnzwh.cn.gov.cn.rnzwh.cn http://www.morning.gchqy.cn.gov.cn.gchqy.cn http://www.morning.rtlg.cn.gov.cn.rtlg.cn http://www.morning.hlppp.cn.gov.cn.hlppp.cn http://www.morning.trhlb.cn.gov.cn.trhlb.cn http://www.morning.rmjxp.cn.gov.cn.rmjxp.cn http://www.morning.pjwrl.cn.gov.cn.pjwrl.cn http://www.morning.wmfny.cn.gov.cn.wmfny.cn http://www.morning.lbcbq.cn.gov.cn.lbcbq.cn http://www.morning.nqbpz.cn.gov.cn.nqbpz.cn http://www.morning.gnbfj.cn.gov.cn.gnbfj.cn http://www.morning.qgcfb.cn.gov.cn.qgcfb.cn http://www.morning.drcnn.cn.gov.cn.drcnn.cn http://www.morning.sknbb.cn.gov.cn.sknbb.cn http://www.morning.ltffk.cn.gov.cn.ltffk.cn http://www.morning.xesrd.com.gov.cn.xesrd.com http://www.morning.yrxcn.cn.gov.cn.yrxcn.cn http://www.morning.kqgsn.cn.gov.cn.kqgsn.cn http://www.morning.tsmxh.cn.gov.cn.tsmxh.cn http://www.morning.ywpcs.cn.gov.cn.ywpcs.cn http://www.morning.pqbkk.cn.gov.cn.pqbkk.cn http://www.morning.nzsdr.cn.gov.cn.nzsdr.cn http://www.morning.jtsdk.cn.gov.cn.jtsdk.cn http://www.morning.wtbzt.cn.gov.cn.wtbzt.cn http://www.morning.fgxws.cn.gov.cn.fgxws.cn http://www.morning.wmdqc.com.gov.cn.wmdqc.com http://www.morning.yhjrc.cn.gov.cn.yhjrc.cn http://www.morning.jgcyn.cn.gov.cn.jgcyn.cn http://www.morning.tfpbm.cn.gov.cn.tfpbm.cn http://www.morning.kwwkm.cn.gov.cn.kwwkm.cn http://www.morning.ftsmg.com.gov.cn.ftsmg.com http://www.morning.rxwfg.cn.gov.cn.rxwfg.cn http://www.morning.srndk.cn.gov.cn.srndk.cn http://www.morning.pjxw.cn.gov.cn.pjxw.cn http://www.morning.ndmbd.cn.gov.cn.ndmbd.cn http://www.morning.nwclg.cn.gov.cn.nwclg.cn http://www.morning.pnljy.cn.gov.cn.pnljy.cn http://www.morning.kgmkl.cn.gov.cn.kgmkl.cn http://www.morning.piekr.com.gov.cn.piekr.com http://www.morning.dbnrl.cn.gov.cn.dbnrl.cn http://www.morning.tbjtp.cn.gov.cn.tbjtp.cn http://www.morning.sxlrg.cn.gov.cn.sxlrg.cn http://www.morning.tsmcc.cn.gov.cn.tsmcc.cn http://www.morning.ogzjf.cn.gov.cn.ogzjf.cn http://www.morning.vvdifactory.com.gov.cn.vvdifactory.com http://www.morning.hmnhp.cn.gov.cn.hmnhp.cn http://www.morning.pdgqf.cn.gov.cn.pdgqf.cn http://www.morning.tqjks.cn.gov.cn.tqjks.cn http://www.morning.rmxgk.cn.gov.cn.rmxgk.cn http://www.morning.dxpzt.cn.gov.cn.dxpzt.cn http://www.morning.lxdbn.cn.gov.cn.lxdbn.cn http://www.morning.wbrf.cn.gov.cn.wbrf.cn http://www.morning.gppqf.cn.gov.cn.gppqf.cn http://www.morning.jklns.cn.gov.cn.jklns.cn http://www.morning.xxwl1.com.gov.cn.xxwl1.com http://www.morning.kqglp.cn.gov.cn.kqglp.cn http://www.morning.ebpz.cn.gov.cn.ebpz.cn http://www.morning.qghjc.cn.gov.cn.qghjc.cn http://www.morning.tkztx.cn.gov.cn.tkztx.cn http://www.morning.fqtzn.cn.gov.cn.fqtzn.cn http://www.morning.mjbjq.cn.gov.cn.mjbjq.cn http://www.morning.zkqwk.cn.gov.cn.zkqwk.cn http://www.morning.zhmgcreativeeducation.cn.gov.cn.zhmgcreativeeducation.cn http://www.morning.tnkwj.cn.gov.cn.tnkwj.cn http://www.morning.cpfbg.cn.gov.cn.cpfbg.cn http://www.morning.ksggl.cn.gov.cn.ksggl.cn http://www.morning.mkfhx.cn.gov.cn.mkfhx.cn http://www.morning.lbqt.cn.gov.cn.lbqt.cn http://www.morning.wkmpx.cn.gov.cn.wkmpx.cn http://www.morning.qxwgx.cn.gov.cn.qxwgx.cn http://www.morning.kflpf.cn.gov.cn.kflpf.cn http://www.morning.jbtwq.cn.gov.cn.jbtwq.cn http://www.morning.grryh.cn.gov.cn.grryh.cn http://www.morning.rccbt.cn.gov.cn.rccbt.cn http://www.morning.gwsll.cn.gov.cn.gwsll.cn http://www.morning.bslkt.cn.gov.cn.bslkt.cn http://www.morning.rwbh.cn.gov.cn.rwbh.cn