中断线程化

news2024/11/25 13:10:31

中断线程化

 中断处理程序包括上半部硬件中断处理程序,下半部处理机制,包括软中断、tasklet、workqueue、中断线程化

当一个外设中断发生后,内核会执行一个函数来响应该中断,这个函数通常被称为中断处理程序或中断服务例程。

上半部硬件中断处理运行在中断上下文中,要求快速完成并且退出中断。

中断线程化是实时Linux项目开发的一个新特性,目的是降低中断处理对系统实时延迟的影响。

在LInux内核里,中断具有最高优先级,只要有中断发生,内核会暂停手头的工作转向中断处理,等到所有挂起等待的中断和软终端处理完毕后才会执行进程调度,因此这个过程会造成实时任务得不到及时处理。

中断上下文总是抢占进程上下文,中断上下文不仅是中断处理程序,还包括softirq、tasklet等,中断上下文成了优化Linux实时性的最大挑战之一。

中断线程化并不是放到普通线程中,而是放到实时线程中,采用 FIFO 的调度方式,优先级为 49


extern int __must_check
request_threaded_irq(unsigned int irq, irq_handler_t handler,
		     irq_handler_t thread_fn,
		     unsigned long flags, const char *name, void *dev);

static inline int __must_check
request_irq(unsigned int irq, irq_handler_t handler, unsigned long flags,
	    const char *name, void *dev)
{
	return request_threaded_irq(irq, handler, NULL, flags, name, dev);
}
wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==

 

request_irq调用request_threaded_irq进行中断注册,只是少了一个thread_fn参数。这也是两则的区别所在,request_irq不能注册线程化中断。

irq:Linux软件中断号,不是硬件中断号。

handler:指primary handler,也即request_irq的中断处理函数handler。

thread_fn:中断线程化的处理函数。

irqflags:中断标志位,见IRQF_*解释。

devname:中断名称。

dev_id:传递给中断处理程序的参数。

handler和thread_fn分别被赋给action->handler和action->thread_fn,组合如下:

handlerthread_fn
1先执行handler,然后条件执行thread_fn。
2×等同于request_irq()
3×handler=irq_default_primary_handler
4××返回-EINVAL
int request_threaded_irq(unsigned int irq, irq_handler_t handler,
			 irq_handler_t thread_fn, unsigned long irqflags,
			 const char *devname, void *dev_id)
{
	struct irqaction *action;
	struct irq_desc *desc;
	int retval;

	/*
	 * Sanity-check: shared interrupts must pass in a real dev-ID,
	 * otherwise we'll have trouble later trying to figure out
	 * which interrupt is which (messes up the interrupt freeing
	 * logic etc).
	 *
	 * Also IRQF_COND_SUSPEND only makes sense for shared interrupts and
	 * it cannot be set along with IRQF_NO_SUSPEND.
	 */
	if (((irqflags & IRQF_SHARED) && !dev_id) || //共享中断设备必须传递啊dev_id参数来区分是哪个共享外设的中断
	    (!(irqflags & IRQF_SHARED) && (irqflags & IRQF_COND_SUSPEND)) ||
	    ((irqflags & IRQF_NO_SUSPEND) && (irqflags & IRQF_COND_SUSPEND)))
		return -EINVAL;

	desc = irq_to_desc(irq);//irq-->desc
	if (!desc)
		return -EINVAL;

	if (!irq_settings_can_request(desc) ||
	    WARN_ON(irq_settings_is_per_cpu_devid(desc)))
		return -EINVAL;

	if (!handler) {
		if (!thread_fn)
			return -EINVAL;
		handler = irq_default_primary_handler;//若handler空,thread_fn非空,hander赋值一个默认函数
	}

	action = kzalloc(sizeof(struct irqaction), GFP_KERNEL);
	if (!action)
		return -ENOMEM;

	action->handler = handler;
	action->thread_fn = thread_fn;//中断线程化赋值
	action->flags = irqflags;
	action->name = devname;
	action->dev_id = dev_id;

	chip_bus_lock(desc);
	retval = __setup_irq(irq, desc, action);//最终这里
	chip_bus_sync_unlock(desc);

	if (retval)
		kfree(action);

#ifdef CONFIG_DEBUG_SHIRQ_FIXME
	if (!retval && (irqflags & IRQF_SHARED)) {
		/*
		 * It's a shared IRQ -- the driver ought to be prepared for it
		 * to happen immediately, so let's make sure....
		 * We disable the irq to make sure that a 'real' IRQ doesn't
		 * run in parallel with our fake.
		 */
		unsigned long flags;

		disable_irq(irq);
		local_irq_save(flags);

		handler(irq, dev_id);

		local_irq_restore(flags);
		enable_irq(irq);
	}
#endif
	return retval;
}

 

/*
 * Internal function to register an irqaction - typically used to
 * allocate special interrupts that are part of the architecture.
 */
static int
__setup_irq(unsigned int irq, struct irq_desc *desc, struct irqaction *new)
{
    struct irqaction *old, **old_ptr;
    unsigned long flags, thread_mask = 0;
    int ret, nested, shared = 0;
    cpumask_var_t mask;

    if (!desc)
        return -EINVAL;

    if (desc->irq_data.chip == &no_irq_chip)----------------------表示没有正确初始化中断控制器,对于GICv2在gic_irq_domain_alloc()中指定chip为gic_chip。
        return -ENOSYS;
    if (!try_module_get(desc->owner))
        return -ENODEV;

    /*
     * Check whether the interrupt nests into another interrupt
     * thread.
     */
    nested = irq_settings_is_nested_thread(desc);-----------------对于设置了_IRQ_NESTED_THREAD嵌套类型的中断描述符,必须指定thread_fn。
    if (nested) {
        if (!new->thread_fn) {
            ret = -EINVAL;
            goto out_mput;
        }
        /*
         * Replace the primary handler which was provided from
         * the driver for non nested interrupt handling by the
         * dummy function which warns when called.
         */
        new->handler = irq_nested_primary_handler;
    } else {
        if (irq_settings_can_thread(desc))-----------------------判断该中断是否可以被线程化,如果没有设置_IRQ_NOTHREAD表示可以被强制线程化。
            irq_setup_forced_threading(new);
    }

    /*
     * Create a handler thread when a thread function is supplied
     * and the interrupt does not nest into another interrupt
     * thread.
     */
    if (new->thread_fn && !nested) {-----------------------------对不支持嵌套的线程化中断创建一个内核线程,实时SCHED_FIFO,优先级为50的实时线程。
        struct task_struct *t;
        static const struct sched_param param = {
            .sched_priority = MAX_USER_RT_PRIO/2,
        };

        t = kthread_create(irq_thread, new, "irq/%d-%s", irq,
                   new->name);-----------------------------------由irq、中断号、中断名组成的中断线程名,处理函数是irq_thread()。
        if (IS_ERR(t)) {
            ret = PTR_ERR(t);
            goto out_mput;
        }

        sched_setscheduler_nocheck(t, SCHED_FIFO, &param);

        get_task_struct(t);
        new->thread = t;

        set_bit(IRQTF_AFFINITY, &new->thread_flags);
    }

    if (!alloc_cpumask_var(&mask, GFP_KERNEL)) {
        ret = -ENOMEM;
        goto out_thread;
    }

    /*
     * Drivers are often written to work w/o knowledge about the
     * underlying irq chip implementation, so a request for a
     * threaded irq without a primary hard irq context handler
     * requires the ONESHOT flag to be set. Some irq chips like
     * MSI based interrupts are per se one shot safe. Check the
     * chip flags, so we can avoid the unmask dance at the end of
     * the threaded handler for those.
     */
    if (desc->irq_data.chip->flags & IRQCHIP_ONESHOT_SAFE)----------表示该中断控制器不支持中断嵌套,所以flags去掉IRQF_ONESHOT。
        new->flags &= ~IRQF_ONESHOT;

    raw_spin_lock_irqsave(&desc->lock, flags);
    old_ptr = &desc->action;
    old = *old_ptr;
    if (old) {-----------------------------------------------------old指向desc->action指向的链表,old不为空说明已经有中断添加到中断描述符irq_desc中,说明这是一个共享中断。shared=1。
...
        /* add new interrupt at end of irq queue */
        do {
            /*
             * Or all existing action->thread_mask bits,
             * so we can find the next zero bit for this
             * new action.
             */
            thread_mask |= old->thread_mask;
            old_ptr = &old->next;
            old = *old_ptr;
        } while (old);
        shared = 1;
    }

    /*
     * Setup the thread mask for this irqaction for ONESHOT. For
     * !ONESHOT irqs the thread mask is 0 so we can avoid a
     * conditional in irq_wake_thread().
     */
    if (new->flags & IRQF_ONESHOT) {
        /*
         * Unlikely to have 32 resp 64 irqs sharing one line,
         * but who knows.
         */
        if (thread_mask == ~0UL) {
            ret = -EBUSY;
            goto out_mask;
        }

        new->thread_mask = 1 << ffz(thread_mask);

    } else if (new->handler == irq_default_primary_handler &&---------非IRQF_ONESHOT类型中断,且handler使用默认irq_default_primary_handler(),如果中断触发类型是LEVEL,如果中断出发后不清中断容易引发中断风暴。提醒驱动开发者,没有primary handler且中断控制器不支持硬件oneshot,必须显式指定IRQF_ONESHOT表示位。
           !(desc->irq_data.chip->flags & IRQCHIP_ONESHOT_SAFE)) {

        pr_err("Threaded irq requested with handler=NULL and !ONESHOT for irq %d\n",
               irq);
        ret = -EINVAL;
        goto out_mask;
    }

    if (!shared) {-------------------------------------------------非共享中断情况
        ret = irq_request_resources(desc);
        if (ret) {
            pr_err("Failed to request resources for %s (irq %d) on irqchip %s\n",
                   new->name, irq, desc->irq_data.chip->name);
            goto out_mask;
        }

        init_waitqueue_head(&desc->wait_for_threads);

        /* Setup the type (level, edge polarity) if configured: */
        if (new->flags & IRQF_TRIGGER_MASK) {
            ret = __irq_set_trigger(desc, irq,-------------------调用gic_chip->irq_set_type设置中断触发类型。
                    new->flags & IRQF_TRIGGER_MASK);

            if (ret)
                goto out_mask;
        }

        desc->istate &= ~(IRQS_AUTODETECT | IRQS_SPURIOUS_DISABLED | \
                  IRQS_ONESHOT | IRQS_WAITING);
        irqd_clear(&desc->irq_data, IRQD_IRQ_INPROGRESS);---------清IRQD_IRQ_INPROGRESS标志位

        if (new->flags & IRQF_PERCPU) {
            irqd_set(&desc->irq_data, IRQD_PER_CPU);
            irq_settings_set_per_cpu(desc);
        }

        if (new->flags & IRQF_ONESHOT)
            desc->istate |= IRQS_ONESHOT;

        if (irq_settings_can_autoenable(desc))
            irq_startup(desc, true);
        else
            /* Undo nested disables: */
            desc->depth = 1;

        /* Exclude IRQ from balancing if requested */
        if (new->flags & IRQF_NOBALANCING) {
            irq_settings_set_no_balancing(desc);
            irqd_set(&desc->irq_data, IRQD_NO_BALANCING);
        }

        /* Set default affinity mask once everything is setup */
        setup_affinity(irq, desc, mask);

    } else if (new->flags & IRQF_TRIGGER_MASK) {
..
    }

    new->irq = irq;
    *old_ptr = new;

    irq_pm_install_action(desc, new);

    /* Reset broken irq detection when installing new handler */
    desc->irq_count = 0;
    desc->irqs_unhandled = 0;

    /*
     * Check whether we disabled the irq via the spurious handler
     * before. Reenable it and give it another chance.
     */
    if (shared && (desc->istate & IRQS_SPURIOUS_DISABLED)) {
        desc->istate &= ~IRQS_SPURIOUS_DISABLED;
        __enable_irq(desc, irq);
    }

    raw_spin_unlock_irqrestore(&desc->lock, flags);

    /*
     * Strictly no need to wake it up, but hung_task complains
     * when no hard interrupt wakes the thread up.
     */
    if (new->thread)
        wake_up_process(new->thread);------------------------------如果该中断被线程化,那么就唤醒该内核线程。这里每个中断对应一个线程。

    register_irq_proc(irq, desc);----------------------------------创建/proc/irq/xxx/目录及其节点。
    new->dir = NULL;
    register_handler_proc(irq, new);-------------------------------以action->name创建目录
    free_cpumask_var(mask);

    return 0;
...
}

中断线程什么时候触发执行,在中断上半部硬中断处理完后,如果上半部返回了IRQ_WAKE_THREAD则调用__irq_wake_thread。

以常用spi 中断为例,spi,lpi中断handle_fasteoi_irq。

irqreturn_t
handle_irq_event_percpu(struct irq_desc *desc, struct irqaction *action)
{
	irqreturn_t retval = IRQ_NONE;
	unsigned int flags = 0, irq = desc->irq_data.irq;

	do {
		irqreturn_t res;

		trace_irq_handler_entry(irq, action);
		res = action->handler(irq, action->dev_id);//执行struct irqaction的handler函数
		trace_irq_handler_exit(irq, action, res);

		if (WARN_ONCE(!irqs_disabled(),"irq %u handler %pF enabled interrupts\n",
			      irq, action->handler))
			local_irq_disable();

		switch (res) { 
		case IRQ_WAKE_THREAD://上半部返回这个的话,一般是使用 request_threaded_irq
			/*
			 * Catch drivers which return WAKE_THREAD but
			 * did not set up a thread function
			 */
			if (unlikely(!action->thread_fn)) {
				warn_no_thread(irq, action);//若没有设置thread_fn,打印
				break;
			}

			__irq_wake_thread(desc, action);//中断线程化 唤醒

			/* Fall through to add to randomness */
		case IRQ_HANDLED:
			flags |= action->flags;//已经处理完毕,可以结束
			break;

		default:
			break;
		}

		retval |= res;
		action = action->next;
	} while (action);

	add_interrupt_randomness(irq, flags);

	if (!noirqdebug)
		note_interrupt(irq, desc, retval);
	return retval;
}

 

void __irq_wake_thread(struct irq_desc *desc, struct irqaction *action)
{
	/*
	 * In case the thread crashed and was killed we just pretend that
	 * we handled the interrupt. The hardirq handler has disabled the
	 * device interrupt, so no irq storm is lurking.
	 */
	if (action->thread->flags & PF_EXITING)
		return;

	/*
	 * Wake up the handler thread for this action. If the
	 * RUNTHREAD bit is already set, nothing to do.
	 */
	if (test_and_set_bit(IRQTF_RUNTHREAD, &action->thread_flags))//若已经对IRQF_RUNTHREAD置位,表示已经处于唤醒中,该函数直接返回
		return;

	
	desc->threads_oneshot |= action->thread_mask;

	 */
	atomic_inc(&desc->threads_active);//活跃中断线程计数

	wake_up_process(action->thread);//唤醒action的thread内核线程
}
/*
 * Interrupt handler thread
 */
static int irq_thread(void *data)//创建后就执行了,会在while中睡眠
{
	struct callback_head on_exit_work;
	struct irqaction *action = data;
	struct irq_desc *desc = irq_to_desc(action->irq);
	irqreturn_t (*handler_fn)(struct irq_desc *desc,
			struct irqaction *action);

	if (force_irqthreads && test_bit(IRQTF_FORCED_THREAD,
					&action->thread_flags))
		handler_fn = irq_forced_thread_fn;
	else
		handler_fn = irq_thread_fn;//函数指针

	init_task_work(&on_exit_work, irq_thread_dtor);、//task work
	task_work_add(current, &on_exit_work, false);

	irq_thread_check_affinity(desc, action);

	while (!irq_wait_for_interrupt(action)) {
		irqreturn_t action_ret;

		irq_thread_check_affinity(desc, action);

		action_ret = handler_fn(desc, action);//唤醒之后执行irq_thread_fn
		if (action_ret == IRQ_HANDLED)
			atomic_inc(&desc->threads_handled);

		wake_threads_waitq(desc);//中断线程执行完了,所以唤醒等待队列头上的进程
	}

	/*
	 * This is the regular exit path. __free_irq() is stopping the
	 * thread via kthread_stop() after calling
	 * synchronize_irq(). So neither IRQTF_RUNTHREAD nor the
	 * oneshot mask bit can be set. We cannot verify that as we
	 * cannot touch the oneshot mask at this point anymore as
	 * __setup_irq() might have given out currents thread_mask
	 * again.
	 */
	task_work_cancel(current, irq_thread_dtor);
	return 0;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/90583.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

G631两级电液伺服阀控制器

G631系列两级电液伺服阀在电气液压伺服系统中作为执行元件&#xff0c;在伺服系统中液压执行机构同电气及气动执行机构相比&#xff0c;具有快速性好、单位重量输出功率大、传动平稳、抗干扰能力强等特点。另一方面&#xff0c;在伺服系统中传递信号和校正特性时多用电气元件。…

数据结构---散列表

散列表哈希函数散列表读写写读扩容总结散列表也叫作 哈希表&#xff08;hash table&#xff09;&#xff0c;这种数据结构提供了键&#xff08;Key&#xff09;和值&#xff08;Value&#xff09;的映射关系。只要给出一个Key&#xff0c;就可以高效查找到它所匹配的Value&…

如何使用HTML制作个人网站(如何搭建个人博客)

&#x1f389;精彩专栏推荐 &#x1f4ad;文末获取联系 ✍️ 作者简介: 一个热爱把逻辑思维转变为代码的技术博主 &#x1f482; 作者主页: 【主页——&#x1f680;获取更多优质源码】 &#x1f393; web前端期末大作业&#xff1a; 【&#x1f4da;毕设项目精品实战案例 (10…

Datawhale 202212 设计模式 | 第二章 23种设计模式

23种设计模式讲解策略模式问题&#xff1a;解决方案&#xff1a;效果&#xff1a;适用场景&#xff1a;实际应用&#xff1a;优点缺点&#xff1a;实例&#xff1a;装饰 6问题&#xff1a;解决方案&#xff1a;效果&#xff1a;代理 7问题&#xff1a;解决方案&#xff1a;效果…

(附源码)springboot球鞋文化交流论坛 毕业设计 141436

目 录 摘要 1 1 绪论 1 1.1 研究意义 1 1.2研究方法 1 1.3论文结构与章节安排 1 2 球鞋文化交流论坛分析 3 2.1 可行性分析 3 2.2 系统流程分析 3 2.2.1数据增加流程 3 2.3.2数据修改流程 4 2.3. 数据删除流程 4 2.3 系统功能分析 4 2.3.1 功能性分析 4 2.3.2 非功能性分析 5 …

【MySQL】说透锁机制(二)行锁 加锁规则 之 范围查询(你知道会锁表吗?)

本文会按照 聚集集索->唯一索引->普通索引 的顺序 地毯式分析 范围查询中 <、<、>、> 的行锁情况&#xff0c;锁表分析在唯一索引 章节&#xff0c;万字长文&#xff0c;力求分析全面&#xff0c;很硬核全网独一份&#xff0c;别忘了收藏&#xff01; 当然如…

中学课程辅导杂志中学课程辅导杂志社中学课程辅导编辑部2022年第34期目录

教育视点《中学课程辅导》投稿&#xff1a;cn7kantougao163.com 基于整体英语教学理念的初中英语读写课研究 徐杰; 3-5 初中语文阅读教学的有效追问策略 徐生; 6-8 初中音乐教学中融入美育的实践探讨 张静; 9-11 “让学引思”理念下的初中物理高效课堂构建 张敏…

部署Neo4j +Rasa+Rasa UI

一、前言 最近在学习kbqa&#xff0c;调研时发现gitee上有关于多轮对话系统源码&#xff0c;该仓库主要是基于rasa框架&#xff08;python语言&#xff09;实现&#xff0c;并且带有web的UI配置&#xff0c;相当于比较全面的对话平台。Rasa UI 可以基于客户已有的平台&#xf…

西电雨课堂测试大合集【导航】

文章目录基础写作 一、二科学道德与学风研究生的压力与心理健康学术规范与论文写作中国传统文化✨感谢各位博主的分享&#xff01; &#x1f4d2;我在这里只是进行了一些整理&#xff0c;方便大家查找。 ⚠️大家看完可以给博主们点点赞、收藏、关注。&#x1f601; 祝各位学业…

Linux机器安装Nginx配置并实现双向认证

Linux机器安装Nginx配置并实现双向认证 一.Nginx的安装配置 Nginx(“engine x”)是一款是由俄罗斯的程序设计师Igor Sysoev所开发高性能的 Web和 反向代理 服务器&#xff0c;也是一个 IMAP/POP3/SMTP 代理服务器。 在高连接并发的情况下&#xff0c;Nginx是Apache服务器不错的…

TiDB | TiDB在5A级物流企业核心系统的应用与实践

TiDB在5A级物流企业核心系统的应用与实践前言一、业务背景科捷物流概况神州金库简介二、现状与挑战神州金库现有技术体系业务挑战应对方案三、TiDB解决方案测试迁移收益问题四、说在最后前言 历经了近半年的测试验证和迁移准备&#xff0c;神州金库3.0核心系统 WMS 正式从 MyS…

力扣(LeetCode)152. 乘积最大子数组(C++)

序列dp f[i]f[i]f[i] 表示以 iii 结尾的连续子数组的最大乘积&#xff0c;d[i]d[i]d[i] 表示以 iii 结尾的连续子数组的最小乘积 。 如果只有正数&#xff0c;我们只需要考虑最大乘积 f[i]f[i]f[i] &#xff1b;有负数&#xff0c;需要考虑与负数相乘的数&#xff0c;越小越好…

【unity】基础交互入门(碰撞交互事件OnTriggerEnter和OnCollisionEnter)

一、碰撞交互事件OnTriggerEnter 1、在场景中添加两个几何体&#xff08;例如Cube和Sphere&#xff09; 2、添加Rigidbody&#xff08;刚体&#xff09;component 点击Cube&#xff08;正方体&#xff09;对象&#xff0c;在面板找到这个按钮&#xff0c;添加component 搜索…

Zabbix6.0使用教程 (五)—zabbix从二进制包安装上篇

大家好&#xff0c;我是乐乐。上一期我们已经讲过从源代码安装zabbix&#xff0c;本期着重讲从二进制包安装zabbix。 当我们在ZABBIX官方存储库可以看到&#xff0c;Zabbix SIA 提供如下官方RPM和DEB包&#xff1a; Red Hat Enterprise Linux/CentOS Debian/Ubuntu/Raspbian …

即时通讯音视频开发编码H.264的特点与优势

即时通讯应用中的实时音视频技术&#xff0c;几乎是IM开发中的最后一道高墙。原因在于&#xff1a;实时音视频技术 音视频处理技术 网络传输技术 的横向技术应用集合体&#xff0c;而公共互联网不是为了实时通信设计的。 国际上制定视频编解码技术的组织有两个&#xff0c;一…

[附源码]Python计算机毕业设计SSM基于Java的运动健身平台(程序+LW)

项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; SSM mybatis Maven Vue 等等组成&#xff0c;B/S模式 M…

博客网页设计制作 简单静态HTML网页作品 DIV CSS个人网页作业成品 dreamweaver学生个人博客网站模板

&#x1f389;精彩专栏推荐&#x1f447;&#x1f3fb;&#x1f447;&#x1f3fb;&#x1f447;&#x1f3fb; ✍️ 作者简介: 一个热爱把逻辑思维转变为代码的技术博主 &#x1f482; 作者主页: 【主页——&#x1f680;获取更多优质源码】 &#x1f393; web前端期末大作业…

Mentor-dft 学习笔记 day42-ASCII File Format(2)

FORCE “primary_input_pin” <value> <time>; 此命令用于在给定时间强制选定主输入引脚上的值&#xff08;0,1、X或Z&#xff09;。时间值不得低于该程序的先前时间值。每个程序的时间在时间0再次开始。主输入引脚将用双引号括起来。APPLY “scan_group_procedure…

高通Ride软件开发包使用指南(10)

高通Ride软件开发包使用指南&#xff08;10&#xff09;7 平台管理任务7.1 连接到控制台7.1.1 串口连接SOC1和SOC27.1.2 识别 SOC1 或 SOC 27.1.3 识别平台构建7.1.4 串口连接SA9000P7.1.5 通过串口连接到 Aurix7.2 在 SIP1 和 SIP2 上为 SA8xxxP 分配 IP 地址7.2.1 分配静态 I…

【数据预处理机器学习】对于薪资数据的倾斜情况以及盒图离群点的探究

文章目录一.需求背景二. 任务开始2.1 薪酬的中位数、均值和众数和数据倾斜模块详细设计2.2 按不同的类别划分职位中的薪酬数据&#xff0c;画盒图/箱线图&#xff0c;检查孤立点/离群点一.需求背景 课题中心&#xff1a;招聘网站的职位招聘数据预处理 之前的文章&#xff0c;…