Linux内核之softirq机制

2023-10-31

中断的上半部执行紧要的任务,下半部则可以处理数据等次要的任务。tasklet也是基于softirq实现的。

不同于tasklet的是,softirq是kernel编译时静态分配的,所以如果动态创建softirq或者kill softirqs,需要修改内核代码,再编译替换掉内核

概要

修改内核

如果想编写内核模块动态的使用softirq,需要修改内核导出一些函数符号才能使用,并且添加一个软中断TEST_SOFTIRQ

diff --git a/include/linux/interrupt.h b/include/linux/interrupt.h
index ee8299eb1..7a249b64e 100644
--- a/include/linux/interrupt.h
+++ b/include/linux/interrupt.h
@@ -537,6 +537,7 @@ enum
        SCHED_SOFTIRQ,
        HRTIMER_SOFTIRQ,
        RCU_SOFTIRQ,    /* Preferable RCU should always be the last softirq */
+       TEST_SOFTIRQ,   /* manually added for study kernel softirqs */
 
        NR_SOFTIRQS
 };
diff --git a/kernel/softirq.c b/kernel/softirq.c
index 09229ad82..887dd60d3 100644
--- a/kernel/softirq.c
+++ b/kernel/softirq.c
@@ -469,6 +469,7 @@ inline void raise_softirq_irqoff(unsigned int nr)
        if (!in_interrupt())
                wakeup_softirqd();
 }
+EXPORT_SYMBOL(raise_softirq_irqoff);
 
 void raise_softirq(unsigned int nr)
 {
@@ -478,6 +479,7 @@ void raise_softirq(unsigned int nr)
        raise_softirq_irqoff(nr);
        local_irq_restore(flags);
 }
+EXPORT_SYMBOL(raise_softirq);
 
 void __raise_softirq_irqoff(unsigned int nr)
 {
@@ -490,6 +492,7 @@ void open_softirq(int nr, void (*action)(struct softirq_action *))
 {
        softirq_vec[nr].action = action;
 }
+EXPORT_SYMBOL(open_softirq);
 
 /*
  * Tasklets

softirq structure

内核中softirqs在代码层面使用结构体softirq_action来表示。

/* softirq mask and active fields moved to irq_cpustat_t in
 * asm/hardirq.h to get better cache usage.  KAO
 */

struct softirq_action
{
	void	(*action)(struct softirq_action *);
};

enum
{
	HI_SOFTIRQ=0,
	TIMER_SOFTIRQ,
	NET_TX_SOFTIRQ,
	NET_RX_SOFTIRQ,
	BLOCK_SOFTIRQ,
	IRQ_POLL_SOFTIRQ,
	TASKLET_SOFTIRQ,
	SCHED_SOFTIRQ,
	HRTIMER_SOFTIRQ,
	RCU_SOFTIRQ,    /* Preferable RCU should always be the last softirq */

	NR_SOFTIRQS
};

然后kernel/softirq.c中定义了数组

static struct softirq_action softirq_vec[NR_SOFTIRQS] __cacheline_aligned_in_smp;

其中NR_SOFTIRQS是enum枚举中定义的,所以如果需要增加softirq,就需要修改enum枚举变量。不过最多注册32个softirqs

定义softirq

需要修改enum枚举,include/linux/interrupt.h中,数值越小优先级越高,HI_SOFTIRQ优先级最高。

enum
{
        HI_SOFTIRQ=0,
        TIMER_SOFTIRQ,
        NET_TX_SOFTIRQ,
        NET_RX_SOFTIRQ,
        BLOCK_SOFTIRQ,
        IRQ_POLL_SOFTIRQ,
        TASKLET_SOFTIRQ,
        SCHED_SOFTIRQ,
        HRTIMER_SOFTIRQ,
        RCU_SOFTIRQ,    /* Preferable RCU should always be the last softirq */
        TEST_SOFTIRQ,   /* manually added for study kernel softirqs */

        NR_SOFTIRQS
};

这里也可以看出softirq的优先级,NET TX/RX > TASKLET > SCHED

创建软中断处理函数

需要按照以下参数格式创建,当触发软中断,kernel去调用对应的handler函数。

void softirq_handler(struct softirq_action *);

比如下面这个handler处理函数,树莓派上的GPIO操作。

/*
** This function is the softirq handler. We are toggling the LED.
*/
static void gpio_interrupt_softirq_handler(struct softirq_action *action)
{
  led_toggle = (0x01 ^ led_toggle);                             // toggle the old value
  gpio_set_value(GPIO_21_OUT, led_toggle);                      // toggle the GPIO_21_OUT
  pr_info("Interrupt Occurred : GPIO_21_OUT : %d ",gpio_get_value(GPIO_21_OUT));
}

触发软中断

大名鼎鼎的**raise_softirq()**函数终于来了。参数nr就是softirq入口,这里就是定义的TEST_SOFTIRQ

void raise_softirq(unsigned int nr)
{
	unsigned long flags;

	local_irq_save(flags);
	raise_softirq_irqoff(nr);
	local_irq_restore(flags);
}

这里local_irq_save()会保存当前中断的开关状态,然后关闭当前处理的中断,然后local_irq_resoter()会恢复之前的中断状态(开或者关)

raise_softirq()调用之后会将TEST_SOFTIRQ挂起(pending),所以softirq handler处理函数将会在处理器下次调用do_softirq()函数时才会被调用。如果interrupt确认已经被关了,可以直接使用下面的函数去触发软中断。

void __raise_softirq_irqoff(unsigned int nr)
{
	lockdep_assert_irqs_disabled();
	trace_softirq_raise(nr);
	or_softirq_pending(1UL << nr);
}

softirq handler何时会被调用

当调用了raise_softirq()或者raise_softirq_irqoff()函数之后,SOFTIRQ(这里是TEST_SOFTIRQ)将被mark为pending状态(挂起),pending状态的softirq将会在以下几个场景中被执行:

  • 硬中断代码处理返回(the return from hardware interrupt code(ISR))
  • 内核ksoftirq线程
  • 任何显性检查或执行pending状态的softirq代码,比如networking subsystem

一些建议

  • 一个软中断永远不会抢占另一个软中断,事实上,唯一能抢占软中断的只有硬中断处理函数(interrupt handler)。同样的softirq可以在不同的处理器上被执行。
  • softirqs一般是下半部中需要处理对时间比较苛刻的任务或者一些比较重要的任务。
  • softirqs一般是在interrupt handlers中才会被raise触发。

示例

#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kdev_t.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/delay.h>
#include <linux/uaccess.h>  //copy_to/from_user()
#include <linux/gpio.h>     //GPIO
#include <linux/interrupt.h>
/* Since debounce is not supported in Raspberry pi, I have addded this to disable 
** the false detection (multiple IRQ trigger for one interrupt).
** Many other hardware supports GPIO debounce, I don't want care about this even 
** if this has any overhead. Our intention is to explain the GPIO interrupt.
** If you want to disable this extra coding, you can comment the below macro.
** This has been taken from : https://raspberrypi.stackexchange.com/questions/8544/gpio-interrupt-debounce
**
** If you want to use Hardaware Debounce, then comment this EN_DEBOUNCE.
**
*/
#define EN_DEBOUNCE
#ifdef EN_DEBOUNCE
#include <linux/jiffies.h>
extern unsigned long volatile jiffies;
unsigned long old_jiffie = 0;
#endif
//LED is connected to this GPIO
#define GPIO_21_OUT (21)
//LED is connected to this GPIO
#define GPIO_25_IN  (25)
//GPIO_25_IN value toggle
unsigned int led_toggle = 0; 
//This used for storing the IRQ number for the GPIO
unsigned int GPIO_irqNumber;
//Interrupt handler for GPIO 25. This will be called whenever there is a raising edge detected. 
static irqreturn_t gpio_irq_handler(int irq,void *dev_id) 
{
  
#ifdef EN_DEBOUNCE
   unsigned long diff = jiffies - old_jiffie;
   if (diff < 20)
   {
     return IRQ_HANDLED;
   }
  
  old_jiffie = jiffies;
#endif  
  
  /* Raise the softirq */
  raise_softirq( EMBETRONICX_SOFT_IRQ );
    
  return IRQ_HANDLED;
}
/*
** This fuction is the softirq handler
*/
static void gpio_interrupt_softirq_handler(struct softirq_action *action)
{
  led_toggle = (0x01 ^ led_toggle);                             // toggle the old value
  gpio_set_value(GPIO_21_OUT, led_toggle);                      // toggle the GPIO_21_OUT
  pr_info("Interrupt Occurred : GPIO_21_OUT : %d ",gpio_get_value(GPIO_21_OUT));
}
dev_t dev = 0;
static struct class *dev_class;
static struct cdev etx_cdev;
 
static int __init etx_driver_init(void);
static void __exit etx_driver_exit(void);
 
 
/*************** Driver functions **********************/
static int etx_open(struct inode *inode, struct file *file);
static int etx_release(struct inode *inode, struct file *file);
static ssize_t etx_read(struct file *filp, 
                char __user *buf, size_t len,loff_t * off);
static ssize_t etx_write(struct file *filp, 
                const char *buf, size_t len, loff_t * off);
/******************************************************/
//File operation structure 
static struct file_operations fops =
{
  .owner          = THIS_MODULE,
  .read           = etx_read,
  .write          = etx_write,
  .open           = etx_open,
  .release        = etx_release,
};
/*
** This function will be called when we open the Device file
*/
static int etx_open(struct inode *inode, struct file *file)
{
  pr_info("Device File Opened...!!!\n");
  return 0;
}
/*
** This function will be called when we close the Device file
*/
static int etx_release(struct inode *inode, struct file *file)
{
  pr_info("Device File Closed...!!!\n");
  return 0;
}
/*
** This function will be called when we read the Device file
*/
static ssize_t etx_read(struct file *filp, 
                char __user *buf, size_t len, loff_t *off)
{
  uint8_t gpio_state = 0;
  
  //reading GPIO value
  gpio_state = gpio_get_value(GPIO_21_OUT);
  
  //write to user
  len = 1;
  if( copy_to_user(buf, &gpio_state, len) > 0) {
    pr_err("ERROR: Not all the bytes have been copied to user\n");
  }
  
  pr_info("Read function : GPIO_21 = %d \n", gpio_state);
  
  return 0;
}
/*
** This function will be called when we write the Device file
*/
static ssize_t etx_write(struct file *filp, 
                const char __user *buf, size_t len, loff_t *off)
{
  uint8_t rec_buf[10] = {0};
  
  if( copy_from_user( rec_buf, buf, len ) > 0) {
    pr_err("ERROR: Not all the bytes have been copied from user\n");
  }
  
  pr_info("Write Function : GPIO_21 Set = %c\n", rec_buf[0]);
  
  if (rec_buf[0]=='1') {
    //set the GPIO value to HIGH
    gpio_set_value(GPIO_21_OUT, 1);
  } else if (rec_buf[0]=='0') {
    //set the GPIO value to LOW
    gpio_set_value(GPIO_21_OUT, 0);
  } else {
    pr_err("Unknown command : Please provide either 1 or 0 \n");
  }
  
  return len;
}
/*
** Module Init function
*/
static int __init etx_driver_init(void)
{
  /*Allocating Major number*/
  if((alloc_chrdev_region(&dev, 0, 1, "etx_Dev")) <0){
    pr_err("Cannot allocate major number\n");
    goto r_unreg;
  }
  pr_info("Major = %d Minor = %d \n",MAJOR(dev), MINOR(dev));
  /*Creating cdev structure*/
  cdev_init(&etx_cdev,&fops);
  /*Adding character device to the system*/
  if((cdev_add(&etx_cdev,dev,1)) < 0){
    pr_err("Cannot add the device to the system\n");
    goto r_del;
  }
  /*Creating struct class*/
  if((dev_class = class_create(THIS_MODULE,"etx_class")) == NULL){
    pr_err("Cannot create the struct class\n");
    goto r_class;
  }
  /*Creating device*/
  if((device_create(dev_class,NULL,dev,NULL,"etx_device")) == NULL){
    pr_err( "Cannot create the Device \n");
    goto r_device;
  }
  
  //Output GPIO configuration
  //Checking the GPIO is valid or not
  if(gpio_is_valid(GPIO_21_OUT) == false){
    pr_err("GPIO %d is not valid\n", GPIO_21_OUT);
    goto r_device;
  }
  
  //Requesting the GPIO
  if(gpio_request(GPIO_21_OUT,"GPIO_21_OUT") < 0){
    pr_err("ERROR: GPIO %d request\n", GPIO_21_OUT);
    goto r_gpio_out;
  }
  
  //configure the GPIO as output
  gpio_direction_output(GPIO_21_OUT, 0);
  
  //Input GPIO configuratioin
  //Checking the GPIO is valid or not
  if(gpio_is_valid(GPIO_25_IN) == false){
    pr_err("GPIO %d is not valid\n", GPIO_25_IN);
    goto r_gpio_in;
  }
  
  //Requesting the GPIO
  if(gpio_request(GPIO_25_IN,"GPIO_25_IN") < 0){
    pr_err("ERROR: GPIO %d request\n", GPIO_25_IN);
    goto r_gpio_in;
  }
  
  //configure the GPIO as input
  gpio_direction_input(GPIO_25_IN);
  
  /*
  ** I have commented the below few lines, as gpio_set_debounce is not supported 
  ** in the Raspberry pi. So we are using EN_DEBOUNCE to handle this in this driver.
  */ 
#ifndef EN_DEBOUNCE
  //Debounce the button with a delay of 200ms
  if(gpio_set_debounce(GPIO_25_IN, 200) < 0){
    pr_err("ERROR: gpio_set_debounce - %d\n", GPIO_25_IN);
    //goto r_gpio_in;
  }
#endif
  
  //Get the IRQ number for our GPIO
  GPIO_irqNumber = gpio_to_irq(GPIO_25_IN);
  pr_info("GPIO_irqNumber = %d\n", GPIO_irqNumber);
  
  if (request_irq(GPIO_irqNumber,             //IRQ number
                  (void *)gpio_irq_handler,   //IRQ handler
                  IRQF_TRIGGER_RISING,        //Handler will be called in raising edge
                  "etx_device",               //used to identify the device name using this IRQ
                  NULL)) {                    //device id for shared IRQ
    pr_err("my_device: cannot register IRQ ");
    goto r_gpio_in;
  }
  
  /* Assign gpio_interrupt_softirq_handler to the EMBETRONICX_SOFT_IRQ */
  open_softirq( EMBETRONICX_SOFT_IRQ, gpio_interrupt_softirq_handler );
 
  pr_info("Device Driver Insert...Done!!!\n");
  return 0;
r_gpio_in:
  gpio_free(GPIO_25_IN);
r_gpio_out:
  gpio_free(GPIO_21_OUT);
r_device:
  device_destroy(dev_class,dev);
r_class:
  class_destroy(dev_class);
r_del:
  cdev_del(&etx_cdev);
r_unreg:
  unregister_chrdev_region(dev,1);
  
  return -1;
}
/*
** Module exit function
*/ 
static void __exit etx_driver_exit(void)
{
  free_irq(GPIO_irqNumber,NULL);
  gpio_free(GPIO_25_IN);
  gpio_free(GPIO_21_OUT);
  device_destroy(dev_class,dev);
  class_destroy(dev_class);
  cdev_del(&etx_cdev);
  unregister_chrdev_region(dev, 1);
  pr_info("Device Driver Remove...Done!!\n");
}
 
module_init(etx_driver_init);
module_exit(etx_driver_exit);
 
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("A simple device driver - SoftIRQ (GPIO Interrupt) ");
MODULE_VERSION("1.42");

reference

Linux Device Driver Tutorial – ch45

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Linux内核之softirq机制 的相关文章

  • rshd.c 源代码中缺少 pam_appl.h 和 pam_misc.h

    我正在研究一个CentOS 5 5 操作系统 它显示错误 security pam appl h 和 security misc h 文件丢失 实际上我的 rshd c 没有加载 PAM 模块 可能是通过放置这个库 它可以帮助我很好地工作我
  • Linux 内核中的 64 位 time_t

    我已经编译了内核 3 19 1 但仍然有问题time t 只是一个简单的程序cout lt lt sizeof time t 给出 4 个字节的大小 而不是我的意图的 8 个字节 我应该在 make menuconfig 期间打开特定选项吗
  • 在 Linux 上使用命令行 PHP 检查互联网连接

    我在 Linux 上使用命令行 PHP 来打开蓝牙拨号连接 并且我需要一种快速的方法来检查互联网连接是否处于活动状态 嗯 不一定要脏 但要快 使用exec运行外部命令不是问题 我正在考虑 ping 一些稳定的服务器 例如谷歌 但我想知道是否
  • 如何在Linux下生成系统范围的唯一ID

    我正在使用多进程 Linux 系统 需要生成唯一的 ID 安全性不是考虑因素 因此 ID 生成器从零开始递增就可以了 而且它只是在本地计算机内 不涉及网络 显然 实现这一点并不难 但我只是想知道是否已经提供了任何东西 最好是轻量级的 这听起
  • 如何在 Linux Bash 中通过 SFTP 将数据传输到远程文件而不将数据存储在本地文件中?

    我需要能够通过 SFTP 将数据从内存传输到远程文件 我最初是通过 SSH 进行此操作的 在工作时发现我没有对远程位置的 SSH 访问权限 只有 SFTP 访问权限 下面是我的原始 SSH 代码的示例 echo secret data ss
  • 如何从存储在 char* 指针中的 name 调用 c 函数?

    我想通过函数的名称动态调用函数 例如 假设有以下函数和字符串 void do fork printf Fork called n char pFunc do fork 现在我需要打电话do fork 就在 pFunc 那么这可能吗 欢迎 C
  • 如何判断输入来自哪个键盘

    设想 我有一个 USB RFID 读取器 将其连接到笔记本电脑后 它可以用作新连接的 USB 键盘 例如无需安装任何驱动程序 当接触带有 RFID 标签的阅读器时 它进入我当前的窗口 例如终端 外壳 RFID 号码 例如0009339384
  • 如何将动态链接的应用程序转换为静态链接的应用程序?

    我有一个应用程序 例如 gedit 它是动态链接的 但我没有源代码 所以我不能按我喜欢的方式编译它 我想要做的是将其静态链接并将其移动到没有运行该应用程序所需的库的系统 那么是否可以做到以及如何做到呢 理论上是可能的 您基本上必须执行与动态
  • 如何在shell脚本中扩展相对路径

    我正在编写一个脚本来使用 bash 在 linux 2 6 上设置环境变量 因此该脚本包含如下命令 export SRC DIR export LIBPATH SRC DIR lib 问题是 当我尝试 echo LIBPATH 时 它显示
  • 在linux中将数据“广播”到多个进程的规范方法?

    我有一个应用程序需要将数据流从一个进程发送到多个读取器 每个读取器都需要查看自己的流副本 这是相当高的速率 100MB s 并不罕见 因此我希望尽可能避免重复 在我的理想世界中 Linux 应该有支持多个读取器的命名管道 并为常见的单读取器
  • 有人可以解释一下以下内存分配 C 程序的性能行为吗?

    在我的机器上 时间 A 和时间 B 交换取决于是否A是 定义或未定义 这会改变两个的顺序 callocs 被称为 我最初将此归因于寻呼系统 奇怪的是 当mmap被用来代替calloc 情况更加奇怪 两个循环花费的时间相同 正如预期的那样 作
  • C++向量数组运算符计算成本高?

    我一直都知道 C 的丰富抽象会带来一定的计算开销 但我的印象是 一旦应用了正确的编译器优化 这种开销几乎可以忽略不计 我很好奇这种开销到底有多大 所以我编写了一个简单的测试来确定这一点 该测试是一个模板化函数 它接受一个容器变量 为容器中的
  • 如何在每个 xargs 命令之间休眠 1 秒?

    例如 如果我执行 ps aux awk print 1 xargs I echo 我想让 shell 在每次之间休眠 1 秒echo 如何更改我的 shell 命令 您可以使用以下语法 ps aux awk print 1 xargs I
  • 使用vim,如何快速刷新正在处理的网页?

    我已经使用 VIM 几个星期了 同时处理各种网络语言 我真的很喜欢它 我发现必须点击或单击浏览器并刷新页面才能看到代码更改的效果 这很麻烦 更烦人的是 因为我使用的是 Virtual Box 而且我倾向于在主机系统上处理 PDF 文件 因此
  • 当我执行 pip --version 时,它显示错误为 ImportError:没有名为 pyparsing 的模块

    我尝试安装 卸载py解析以及它不起作用 我被这个问题困住了 我还必须安装额外的库 这是错误消息 Traceback most recent call last File usr bin pip line 5 in
  • Zip 实用程序在 Linux 中每次都给我不同的 md5sum

    当我在 Linux 中压缩 Zip 2 31 同一个文件时 每次都会得到不同的校验和 如何保持上次的 md5sum 相同 我正在使用 yum 提供的最新 zip 更新 生成的存档不仅包含压缩文件数据 还包含 额外的文件属性 如参考zip 文
  • 32 位 x86 汇编中堆栈对齐的职责

    我试图清楚地了解谁 调用者或被调用者 负责堆栈对齐 64 位汇编的情况相当清楚 它是由caller 请参阅系统 V AMD64 ABI 第 3 2 2 节栈帧 输入参数区域的末尾应按 16 对齐 32 如果 m256 在堆栈 字节边界上传递
  • 在Linux上如何找到当前目录的所有直接子目录?

    在Linux上如何找到当前目录的所有直接子目录 最简单的方法是通过编写来利用 shell 通配功能echo 如果你喜欢使用ls 例如要应用格式 排序选项 请使其ls d 解释 斜杠确保仅考虑目录 而不考虑文件 Option d 列出目录本身
  • 如何检查QProcess是否正确执行?

    QProcess process sdcompare QString command sdcompare QStringList args sdcompare command sdcompare diff args sdcompare lt
  • 为什么在setsid()之前fork()

    Why fork before setsid 守护进程 基本上 如果我想将一个进程与其控制终端分离并使其成为进程组领导者 我使用setsid 之前没有分叉就这样做是行不通的 Why 首先 setsid 将使您的进程成为进程组的领导者 但它也

随机推荐

  • android 手机内存64实际不到,为什么你的手机内存总是达不到64G?丢失的内存去哪了?详细解读...

    近些年手机各项参数快速发展 除了屏幕 处理器 相机等主要零部件性能提升的同时 我们手机的内存也是越来越大 从最刚开始的2GB 到4G 8G 16与32G 再到现在标配64G起步 手机软件生态越来越完善 现在64G的手机也变得捉襟见肘 但是当
  • Linux man 命令详解

    man 命令 Linux man 命令用于显示 Linux 操作系统中的手册页 manual page 它提供了对 Linux 操作系统中各种命令 函数 库等的详细说明 man 命令有许多参数 参数介绍 下面简要介绍一下主要参数的功能 f
  • PyTorch自制数据集

    PyTorch加载数据主要分为两类 只有图片的数据集以及含有csv保存标签的数据集 只有图片的数据集又分为两类 标签在文件夹上和标签在图片名上 学习地址 1 标签在文件夹上 此情况下导入数据集 只需要调用PyTorch中的ImageFold
  • c/c++ 编程软件(IDE)推荐

    声明 本文软件推荐是为初学编程萌新推荐 以帮助新手快速搭建c c 开发环境 掌握语法 推荐原则并不适用与生产领域 另 萌新直接推荐小熊猫C 下载 英语不好可使用小龙dev 小龙 Dev C 中文主页 都是下载即用 优缺点文中均有介绍 0 首
  • 网页版权信息 日期格式

    1 格式 Copyright dates by author owner 例子 2022 baidu 2005 2015 Tencent All Rights Reserved Copyright 1996 2014 SINA Corpor
  • unity3D 音效的设置 音效的开关 制作声音的开关按钮

    使物体图 如图所示 设置界面如下 每一个 列表设置如下 sudio 的设置如下图 Background 的设置如下图 Checkmark的设置如下图 Text 的设置如下图 代码部分 引用是设置 如图所示 音效的开关的设计代码 Game M
  • R语言期末考试复习题第一天整理内容(自己整理+参考博主:紧到长不胖 )请多关注 紧到长不胖 ,每天有惊喜!

    R语言期末考试复习题第一天整理内容 期末考试例题 1 写出函数来计算15 21 39 45 17的和 分别用for循环和while循环 s c 15 21 39 45 17 sum s he 0 for i in 1 length s he
  • python again_收藏!最全从Python小白到大牛,要走的路这里都有(初级篇)

    收藏 长文 从Python小白到大牛 要走的路这里都有面向项目的学习是学习编码的最佳方法 Python是当今最需求的语言 为了帮助您学习它 以下是一些您可以探索的最重要的Python项目 Python游戏Python图像编程CIFAR10在
  • 周鸿祎:什么是好的用户体验?

    说今天是一个体验为王的时代 一点也不过分 做大众消费品的人可能已经感觉到 今天消费者的话语权越来越强 如果你的产品做得好 不久就会口口相传 如果你的产品做得烂 不久就会骂声一片 所有这一切在过去是不可想象的 但今天 每个人都可以发布信息 每
  • hadoop任务执行时,报错

    2020 11 06 03 42 43 205 ERROR org apache hadoop yarn server resourcemanager scheduler SchedulerApplicationAttempt Error
  • Collectors.summing唯独没有BigDecimal的求和方法

    最近在做订单相关的模块 有个订单列表接口 需要对订单金额进行求和 每次都得遍历list 然后用BigDecimal add 方法取求和 感觉很麻烦 想到之前有用到java8的stream collect的Collectors summing
  • 一个例子搞懂 tabelu的上下文筛选器

    示例 1 将维度筛选器转换为上下文筛选器 本示例以及以下示例使用 Tableau Desktop 附带的 Sample Superstore 数据源 在此示例中 视图解决以下问题 按总销售额计 纽约市位居前 10 名的客户有哪些 视图包含两
  • Mono和MonoDevelop源码编译安装

    Mono和MonoDevelop源码编译安装 之所以用源码编译的方式安装mono和monodevelop 是因为通过yum安装的mono不是最新版本 而且monodevelop不能建 asp net MVC3的工程 而且通过源码安装 可以进
  • 世界坐标系、相机坐标系、图像坐标系、像素坐标系之间的转换及三维空间的刚体运动

    基本概念 https blog csdn net sunshine zoe article details 73457686 世界坐标系到相机坐标系下的变换 https www jianshu com p 64b4c887c439 通过两个
  • jquery中的伪数组和each和map静态方法区别,以及其他的一些静态方法

    伪数组 1 必须要有length属性 2 如果这个length的属性值是0 那么这个对象有没有元素无所谓 3 如果这个length的属性值不为0 那么这个对象一定头下标为 length 1 的属性值 列如 伪装组 var obj lengt
  • 公有链、联盟链、私有链区别

    1 公有链 公有链是世界上任何人都可以访问读取的 任何人都可以发送交易并且如果交易有效的话可以将之包括到区块中的 以及任何人都能够参与与其共识过程的区块 优点 所有交易数据公开 透明 无法篡改 缺点 低吞吐量 TPS 交易速度慢 2 联盟链
  • java橙色风格小说精品屋小说网站源码

    没有搭建教程 懂的自行下载研究 文件 590m com f 25127180 486121419 fbf84f 访问密码 551685 安装环境 宝塔面板 tomcat8 nginx1 17 mysql5 6 不知道最高支持到多少 打开服务
  • linux netLink检测usb插拔事件

    include
  • 从一到无穷大

    这本书一开始讲数学 后来讲到4围空间 空间压缩 相对论就头疼了 本不想读下去了 后来硬着头皮往下读 发现后面章节讲解生物 化学 天文等等 哈哈 原来是本科普的读物 大概都知道一些 不错 推荐给初高中生来看一看 2013 12 07
  • Linux内核之softirq机制

    中断的上半部执行紧要的任务 下半部则可以处理数据等次要的任务 tasklet也是基于softirq实现的 不同于tasklet的是 softirq是kernel编译时静态分配的 所以如果动态创建softirq或者kill softirqs