linux usb枚举过程分析之守护进程及其唤醒【转】
转自:http://blog.csdn.net/xuelin273/article/details/38646765
usb热插拔,即usb设备可以实现即插即用,像U盘一样,插到电脑里就可以用,不用时可以直接拔除,这个动作不会影响USB设备使用性能。
在linx 系统中,usb热插拔由两部分方面共同实现,即内核空间和用户空间,内核由一个守护进程实现,用户空间由udev 程序实现。在内核空间里,有一个专门用于监控usb hub的状态的守护进程,守护进程通过等待队列实现,等待队列平时处理休眠状态,当usb hub上状态变化时(即有usb设备从usb hub上插入或拔出)时,便会去唤醒等待队列,然后去实现usb设备枚举,枚举成功后,向linux系统注册usb设备,并通过kobject_event通知用户空间,有设备插入或拔出,用户空间里有一个专门用于实现动态加载设备节点的程序udev,当它收到内核通知后,能够动态创建usb设备节点,这样便实现了usb的热插拔。
本文主要从usb设备枚举最基本的几个方面进行讲解,即usb守护进程、守护进程如何唤醒、被谁唤醒。
一.守护进程
- khubd_task = kthread_run(hub_thread, NULL, "khubd");
- if (!IS_ERR(khubd_task))
- return 0;
- static int hub_thread(void *__unused)
- {
- set_freezable();
- do {
- hub_events();
- wait_event_freezable(khubd_wait,
- !list_empty(&hub_event_list) ||
- kthread_should_stop());
- } while (!kthread_should_stop() || !list_empty(&hub_event_list));
- pr_debug("%s: khubd exiting\n", usbcore_name);
- return 0;
- }
第3行的set_freezable作用是清除当前线程标志flags中的PF_NOFREEZE位,表示当前线程能进入挂起或休眠状态。
- static DECLARE_WAIT_QUEUE_HEAD(khubd_wait);
二 .守护进程唤醒
- static void kick_khubd(struct usb_hub *hub)
- {
- unsigned long flags;
- spin_lock_irqsave(&hub_event_lock, flags);
- if (!hub->disconnected && list_empty(&hub->event_list)) {
- list_add_tail(&hub->event_list, &hub_event_list);
- /* Suppress autosuspend until khubd runs */
- usb_autopm_get_interface_no_resume(
- to_usb_interface(hub->intfdev));
- wake_up(&khubd_wait);
- }
- spin_unlock_irqrestore(&hub_event_lock, flags);
- }
第7行,将hub的event_list添加到hub_event_list列表,用于满足守护进程被唤醒时的条件。
- hub->urb = usb_alloc_urb(0, GFP_KERNEL);
- if (!hub->urb) {
- ret = -ENOMEM;
- goto fail;
- }
- usb_fill_int_urb(hub->urb, hdev, pipe, *hub->buffer, maxp, hub_irq, hub, endpoint->bInterval);
urb的回调函数为hub_irq:
- static void hub_irq(struct urb *urb)
- {
- struct usb_hub *hub = urb->context;
- int status = urb->status;
- unsigned i;
- unsigned long bits;
- switch (status) {
- case -ENOENT: /* synchronous unlink */
- case -ECONNRESET: /* async unlink */
- case -ESHUTDOWN: /* hardware going away */
- return;
- default: /* presumably an error */
- /* Cause a hub reset after 10 consecutive errors */
- dev_dbg (hub->intfdev, "transfer --> %d\n", status);
- if ((++hub->nerrors < 10) || hub->error)
- goto resubmit;
- hub->error = status;
- /* FALL THROUGH */
- /* let khubd handle things */
- case 0: /* we got data: port status changed */
- bits = 0;
- for (i = 0; i < urb->actual_length; ++i)
- bits |= ((unsigned long) ((*hub->buffer)[i]))
- << (i*8);
- hub->event_bits[0] = bits;
- break;
- }
- hub->nerrors = 0;
- /* Something happened, let khubd figure it out */
- kick_khubd(hub);
- resubmit:
- if (hub->quiescing)
- return;
- if ((status = usb_submit_urb (hub->urb, GFP_ATOMIC)) != 0
- && status != -ENODEV && status != -EPERM)
- dev_err (hub->intfdev, "resubmit --> %d\n", status);
- }
在hub_irq中可以看到,如果urb被成功提交给控制器,并成功获取port状态,就会调用kick_khubd唤醒守护进程;
- retval = usb_hcd_request_irqs(hcd, irqnum, irqflags);
- if (retval)
- goto err_request_irq;
- if (ints & OHCI_INTR_RHSC) {
- ohci_vdbg(ohci, "rhsc\n");
- ohci->next_statechange = jiffies + STATECHANGE_DELAY;
- ohci_writel(ohci, OHCI_INTR_RD | OHCI_INTR_RHSC, ®s->intrstatus);
- ohci_writel(ohci, OHCI_INTR_RHSC, ®s->intrdisable);
- usb_hcd_poll_rh_status(hcd);
- }
当port上状态发生变化时,就会调用usb_hcd_poll_rh_status去查询usb root hub port状态,并调用hub中interrupt urb的回调函数hub_irq,最终去唤醒usb守护进程。
linux usb枚举过程分析之守护进程及其唤醒【转】的更多相关文章
- Linux中的两种守护进程stand alone和xinetd
Linux中的两种守护进程stand alone和xinetd --http://www.cnblogs.com/itech/archive/2010/12/27/1914846.html#top 一 ...
- Windows 和 Linux 上Redis的安装守护进程配置
# Windows 和 Linux 上Redis的安装守护进程配置 Redis 简介 Redis是目前最常用的非关系型数据库(NOSql)之一,常以Key-Value的形式存储.Redis读写速度 ...
- ASP.NET Core Linux下为 dotnet 创建守护进程(必备知识)
前言 在上篇文章中介绍了如何在 Docker 容器中部署我们的 asp.net core 应用程序,本篇主要是怎么样为我们在 Linux 或者 macOs 中部署的 dotnet 程序创建一个守护进程 ...
- ASP.ENT Core Linux 下 为 donet创建守护进程(转载)
原文地址:http://www.cnblogs.com/savorboard/p/dotnetcore-supervisor.html 前言 在上篇文章中介绍了如何在 Docker 容器中部署我们的 ...
- [Linux] PHP程序员玩转Linux系列-使用supervisor实现守护进程
1.PHP程序员玩转Linux系列-怎么安装使用CentOS 2.PHP程序员玩转Linux系列-lnmp环境的搭建 3.PHP程序员玩转Linux系列-搭建FTP代码开发环境 4.PHP程序员玩转L ...
- linux下编写简单的守护进程
搭建linux服务器的时候,需要写一个简单的守护进程来监控服务的运行情况,shell脚本如下: #!/bin/sh function daemon() { while true do server=` ...
- Linux网络编程学习(四) -----守护进程的建立(第三章)
本文介绍一个例程daemon_init() #include <sys/types.h> #include <signal.h> #include <unistd.h&g ...
- Linux下性能监控、守护进程与计划任务管理
目录 一:监视系统进程(ps .top) 二:查看网络连接信息 (netstat) 三:文件进程.端口关联(lsof) 四:计划任务管理(at .crontab) at crontab 一:监视系统进 ...
- Linux学习笔记(9)-守护进程
明天学这个!! ---------------------------------------------------------- 守护进程(Daemon)是运行在后台的一种特殊进程.它独立于控制终 ...
随机推荐
- LeetCode 167. 两数之和 II - 输入有序数组
题目: 给定一个已按照升序排列 的有序数组,找到两个数使得它们相加之和等于目标数. 函数应该返回这两个下标值index1 和 index2,其中 index1 必须小于 index2. 说明: 返回的 ...
- VSCode和VUE的初始安装及简单使用入门
(版本1.0) npm run dev 运行工程 PS F:\SDN\VIMS--前端\vue> cnpm install PS F:\SDN\VIMS--前端\vue> cnpm reb ...
- 2 引入jquery和boot
vue引入bootstrap——webpack https://blog.csdn.net/wild46cat/article/details/77662555(copy) 想要在vue中引入boot ...
- 深入理解Vue的生命周期
谈到Vue的生命周期,相信许多人并不陌生.但大部分人和我一样,只是听过而已,具体用在哪,怎么用,却不知道.我在学习vue一个多礼拜后,感觉现在还停留在初级阶段,对于mounted这个挂载还不是很清楚. ...
- Rabbitmq基本原理(转)
https://www.cnblogs.com/jun-ma/p/4840869.html
- MySQL5.7安装(RPM)笔记
1. 检查MySQL是否安装,如果有安装,则移除(rpm –e 名称)[root@localhost ~]# rpm -qa | grep -i mysqlmysql-libs-xxxxxxxxxx. ...
- linux下转换windows文件格式为unix sed -i 's/\r//' <filename> 转化为unix格式
sed -i 's/\r//' <filename> 转化为unix格式
- 【BZOJ1452】[JSOI2009]Count(树状数组)
[BZOJ1452][JSOI2009]Count(树状数组) 题面 BZOJ 洛谷 题解 数据范围这么小?不是对于每个颜色开一个什么东西记一下就好了吗. 然而我不会二维树状数组? 不存在的,凭借多年 ...
- 【BZOJ2084】[Poi2010]Antisymmetry(manarcher)
[BZOJ2084][Poi2010]Antisymmetry(manarcher) 题面 BZOJ 洛谷 题解 一眼马拉车吧...明显就是在回文串的基础上随便改了改. 似乎还可以魔改回文树,然而我这 ...
- POJ 2235 Frogger / UVA 534 Frogger /ZOJ 1942 Frogger(图论,最短路径)
POJ 2235 Frogger / UVA 534 Frogger /ZOJ 1942 Frogger(图论,最短路径) Description Freddy Frog is sitting on ...