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)是运行在后台的一种特殊进程.它独立于控制终 ...
随机推荐
- c语言四则运算
#include<stdio.h>#define W 5main(){ int a,b,i=0,c,d,r=0; while(i<W) { i++; srand(time()); a ...
- div z-index无论设置多高都不起作用
这种情况发生的条件有三个: 1.父标签position属性为relative: 2.当前标签无position属性(relative,absolute,fixed): 3.当前标签含有浮动(float ...
- 基于Air800+Arduino+ESP8266的混合物联网开发
流程图如下:
- Varnish 4.0 实战
简介 Varnish 是一款高性能且开源的反向代理服务器和 HTTP 加速器,其采用全新的软件体系机构,和现在的硬件体系紧密配合,与传统的 squid 相比,varnish 具有性能更高.速度更快.管 ...
- ubuntu18.04配置nvidia docker和远程连接ssh+远程桌面连接(二)
ubuntu18.04配置nvidia docker和远程连接ssh+远程桌面连接(二) 本教程适用于想要在远程服务器上配置docker图形界面用于深度学习的用户. (二)nvidia docker配 ...
- PAT 1063 计算谱半径
https://pintia.cn/problem-sets/994805260223102976/problems/994805267860930560 在数学中,矩阵的“谱半径”是指其特征值的模集 ...
- [财务知识]IFRS9
浅谈IFRS9 2018-07-10 23:15信用/收益 原创申明 本文原创作者为金融监管研究院助理研究员李健,未经授权谢绝转载.引用.抄袭. 引言 2018年6月6日,财政部会计司发布了“关于就& ...
- 浅谈JavaSript中的this
JavaScript的this对初学者来说一直是一个很头疼的问题,因为它的指向刚刚接触的时候往往觉得有点莫名奇妙,这篇博客用实例来概括一下,this代表什么以及如何改变函数的this. 在<Ja ...
- CUDA ---- 线程配置
前言 线程的组织形式对程序的性能影响是至关重要的,本篇博文主要以下面一种情况来介绍线程组织形式: 2D grid 2D block 线程索引 矩阵在memory中是row-major线性存储的: 在k ...
- 洛谷P2396 yyy loves Maths VII
P2396 yyy loves Maths VII 题目背景 yyy对某些数字有着情有独钟的喜爱,他叫他们为幸运数字;然而他作死太多,所以把自己讨厌的数字成为"厄运数字" 题目描述 ...