法外狂徒张三删库跑路

真实案例:在今年2月份,国内一个程序员删库的消息传遍it界。他的几行代码,直接让上市公司微盟的市值一天蒸发超10亿,300百万用户直接受到影响。网上是谣言四起,可谓是最牛逼的删库跑路案例。删库跑路是我们程序员可望而不可及的,是传说。最终这位技术人员,没有逃出法律的制裁,罚钱和判刑一样也没少。真是删库一时爽,身后火葬场。
删库跑路,是技术人员道路上永远无法企及的高度。但是对我们技术人员,千万不要因为一时脑热,做出不可挽回的错误i,让自己身陷牢狱之灾。除此之外,企业也应该完善安全机制和管理制度,顺便在想一下为什么员工会删库。

## Linux内核中断

一、简介
不管是在单片机裸机中,还是在linux中,中断都是频繁使用的对象。记得大学那会接触51单片机中断 :定时和计算器中断 ,给我折腾疯了。

  • 单片机使用中断需要设置寄存器,使能IRQ等,需要清晰的了解对应寄存器每一位的作用。在后来的蓝牙soc,nxp单片机中,就把寄存器设置部分,用api函数封装起来,提供给用户使用。
    总结一下:
    1>初始化寄存器,使能中断
    2>注册中断服务函数,也就是向irqtable数组标号写入中断服务函数
    3>中断发生,进入irq中断服务函数,在irq中断服务函数irqtable数组中查找具体的中断处理函数,执行对应的中断处理函数。

  • 在linux中,同样也进行了封装,我们只需要按照中断的使用模板,在驱动中灵活调用即可。以下就介绍内核中的中断框架。

1.中断API函数
1>中断号
每个中断都有一个中断号,可以中断号可以区分不同的中断。在内核中用一个int变量来表示中断号。中断号已经写道设备数里面,因此可以通过irq_of_parse_and_map函数从interupts属性中获取设备号,获取中断号的api:

1. 非gpio中断请求中断号
//dev : 设备节点
//index :索引号,interrput包含多条中断信息,通过index指定要获取的信息
//return : 返回的中断号
unsigned int irq_of_parse_and_map(struct device_node * dev, int  index)2.gpui申请中断号
//gpio : 对应的gpio编号
//return:返回的中断号
int gpio_to_irq(unsigned int gpio)

2>请求中断函数(request_irq)
在内核中,使用某个中断是需要申请的,request_irq函数用于申请中断:

int request_irq(unsigned int irq,irq_handker_t handler,unsigned long flags,const char *name.void *dev)
//要申请中断的中断号
//中断处理函数,当中断发生时,进入handler
//flags:中断标志,可以在include/linux/interrupput.h中查看
常用:上升沿、下降沿、高电平、
//中断名字
//当flags设置未IRQ_SHARES(共享中断),dev用来区分不同中断;一般情况下将dev设置未设备结构体,dev会传递函数给中断处理函数irq_handler_t的第二个参数。
//return 0 成功

note:request_irq函数可能会导致睡眠,因此不能在中断上下文或者其它禁止睡眠的的代码中使用request_irq函数。

3> 释放中断函数(free_irq)
在使用request_irq申请中断,使用完成之后需要通过free_irq函数释放掉对应中断。

void free_irq(unsigned irq,void *dev)
//要释放的中断
//如果终端设置为共享IROQF_SHARED(共享中断)的话,此参数用来区分具体中断。共享中断只有在释放最后中断处理函数的时候才会被禁止掉。
//返回值:无

4>中断处理函数
使用request-irq申请中断的时候,要设置中断处理函数,中断处理函数如下:

irqreturn_t (*irq_handler_t)(int, void*)
//参数int 是中断处理函数要处理的相应中断号
//void* :指向void ,也就是个通用指针,需要与request_irq函数的dev参数保持一致。用于区分共享中断的不同设备,dev也可以指向设备数据结构。
//返回值irqreturn_t类型,irqretuen_t类型如下:
typedef enum irqreturn irqreturn_t;
enum irqreturn{IRQ_NONE = (0<<0),IRQ_HANDLED = (1<<0),IRQ_WAKE_THREAD = (1 <<1),
};
/*return IRQ_RETVAL(IRQ_HANDLED);*/

5>中断使能与禁止函数
(1)常用api函数如下:

void enable_irq(unsigned int irq) //使能。。中断号为irq。。的中断
void disable_irq(unsigned int irq) //禁止。。中断号为irq。。的中断
/*
diable_irq 使用注意事项:a.保证中断函数处理完成才返回,不会产生新的中断b.确保已经开始执行的中断处理程序已经全部推出。c.在以上情况下 ,可以使用void disable_irq_nosync(unsigned int irq)
*/
void disable_irq_nosync(unsigned int irq)//函数调用之后,立即返回,不会等待当前中断函数执行完毕。

(2)以上三个函数的,只用与使能或者禁止一个中断,有时候需要关闭处理 器的整个中断。可以使用如下api:

local_irq_enable()//使能当前处理器的中断系统
local_irq_disable()//禁止当前处理器的中断系统
/*有些A场景下不能使用loacal——irq——enable来打开全局中断,而是将中断状态恢复到以前的状态,要考虑到B的感受,此时需要调用一下两个函数
*/
local_irq_save(flags)//禁止中断,并且把中断状态保存在flags中
local_irq_restore(flags)//恢复中断,将中断恢复到flags状态

2.中断上、下半部
在kernel 中,中断有上半部和下半部之分。在使用request_irq申请中断的时候,注册的中断服务函数属于:中断处理的上半部,只要中断触发,中断处理函数就会执行。
在中断中,我们希望处理函数越快处理越好,但是在现实使用中,有些中断往往耗时间。我们需要对其处理,缩小中断处理函数的执行时间。这就出现了下半部。
举个例子:tp触控屏,中断通知soc触控event发生,i2c读取坐标上报给系统。i2c最高速度有400kbit/s,所以在中断中用i2c读取数据就会浪费时间。我们可以将i2c读取坐标值暂后执行(放到下半部),中断处理函数处理相应的中断,然后清楚标志位(上半部处理)。
a.上半部:上半部是request_irq 注册的中断处理函数,处理过程比较快,部耗时的任务可以放到上半部完成。
b.下半部:如果中断函数处理任务较多,比较耗时,那么把这些耗时任务放到下半部来执行。
c.分为上、下半部的目的:实现中断函数快进快出,将对时间敏感,与硬件有关,不希望被中断的函数,希望执行速度快的放到上半部,剩下的放到下半部。

二、下半部处理机制

1>软中断

内核提供了bottom half机制实现下半部。后面引入了软中断和tasklet来代替”BH‘,完全可以使用软中断和tasklet来代替BH.随着内核版本升级,BH在2点几版本就不用了。在linux内核中,使用softirq_action表示软中断,定义在includ/linux/interrupt.h,

//a. 软中断 includ/linux/interrupt.h
struct softirq_action
{void (*action)(struct softirq_action *);
}//b.中断类型 kernel/softirq.h
static struct softirq_action softirq_vec[NR_SOFTIRQS];//NR_SOFTIRQS 在 includ/linux/interrupt.h 中
enum
{HI_SOFTIRQ=0, TIMER_SOFTIRQ, NET_TX_SOFTIRQ, NET_RX_SOFTIRQ, BLOCK_SOFTIRQ, BLOCK_IOPOLL_SOFTIRQ, TASKLET_SOFTIRQ, //tasklet 软中断 SCHED_SOFTIRQ, HRTIMER_SOFTIRQ, RCU_SOFTIRQ, NR_SOFTIRQS
};

可以看到,softirq_action 结构体中的成员变量action就是软中断的服务函数,数组softirq_vec是个全局数组,因此所以cpu都可以访问到,每个cpu都有自己的触发和控制机制,并且只执行自己所触发的软中断。
如何使用软中断?
a. 先使用open_softirq函数,注册对应的软件中断处理函数,函数如下:

void open_softirq(int0nr ,void(*action)(struct softirq_action*))
//nr :要开启的软中断,如 TASKLET_SOFTIRQ
//action :软中断对应的处理函数
//void 无return

b.注册过软中断之后,需要 通过raise_softirq函数触发,函数如下:

void raise_softirq(unsigned int nr)
//nr :要触发的软中断
//void : 无return

c. 软中断必须在编译的时候:静态注册。linux内核使用softirq_irq函数初始化软件中断,
softirq_init函数在kernel/softirq.c ,函数如下

//kernel/softirq.c void __init softirq_init(void){int cpu;for_each_possible_cpu(cpu) {per_cpu(tasklet_vec, cpu).tail = &per_cpu(tasklet_vec, cpu).head;per_cpu(tasklet_hi_vec, cpu).tail = &per_cpu(tasklet_hi_vec,cpu).head;}open_softirq(TASKLET_SOFTIRQ, tasklet_action);open_softirq(HI_SOFTIRQ, tasklet_hi_action);}//从这里我们可以看到,默认打开TASKLET_SOFTIRQ(tasklet软中断),HI_SOFTIRQ(高优先级软中断)。

2>tasklet

tasklet是利用软中断来实现下半部的一种机制,在软中断和taskley之间,我们一般使用tasklet。linux内核使用tasklet_struct结构体来表示tasklet,如下:

struct tasklet_struct{struct tasklet_sturct *next;//下一个taskletunsigned long state ; //tasklet 状态atomic_t count ;// 计数器,记录对tasklet的引用数void (*func)(unsigned long); //tasklet执行函数 ,可以定义函数内容,相当于中断处理函数unsigned long data //函数func的参数
};

如何使用tasklet?
a. 先定义一个tasklet,然后使用tasklet_init函数初始化tasklet,tasklet_init函数原型如下:

void tasklet_init(struct tasklet_struct *t,void (*func)(unsigned long),unsigned long data);
//t:要初始化的tasklet
//func :tasklet的处理函数
//data:传递给func函数的参数
//void 无return/*或者通过宏 : DECCLEARE_TASKLET 来一次性完成tasklet的定义和初始化,
DECCLEARE_TASKLET 定义在 include/linux/interrupt.h
*/
DECLARE_TASKLET(name, func, data)

b.在上半部,也就是中断处理函数中调用tasklet_schedule,就能使tasklet在合适的时间运行,tasklet_schedule 函数如下:

void tasklet_schedul(struct tasklet_struct *t)
//t:要调度的tasklet,也就是DECCLEAR_TASKLET宏里的name
//无返回值

c.参考模板

//1. 定义tasklet
struct tasklet-struct testtasklet;void testtasklet(unsigned long data)
{//5.taskelet 函具体的处理内容
}//中断处理函数test_handler
irqreturn_t test_handler(int irq ,void *dev_id)
{.......//4.调度tasklettasklet_schedule(&testtasklet);
.......
}//驱动入口函数
static int __init xxx_init(coid)
{......//2.初始化tasklettasklet_init(&testtasklet ,testtasklet_func, data);//3. 注册中断处理函数request_irq(xxx_irq ,test_handler ,0 , "xxx" ,&xxx_dev);
......
}

3>工作队列

工作队列是另一种下半部的机制,工作队列在进程上下文执行,工作队列目的:**是将要推后的工作交给一个内核线程去执行,因为工作队列工作在进程上下文,因此工作队列允许睡眠或重新调度。**如果要睡觉或者推后的工作可以选择工作队列,否则选择软中断或者tasklet。

linux 内核使用work_struct 结构体表示一个工作,如下:

 struct work_struct {atomic_long_t data;struct list_head entry;work_func_t func;//工作队列处理函数};

上面工作组织(work_struct)成工作队列,工作队列使用workqueue_struct结构体表示,如下:

struct workqueue_struct {struct list_head pwqs; struct list_head list; struct mutex mutex; int work_color;int flush_color; atomic_t nr_pwqs_to_flush;struct wq_flusher *first_flusher;struct list_head flusher_queue; struct list_head flusher_overflow;struct list_head maydays; struct worker *rescuer; int nr_drainers; int saved_max_active;struct workqueue_attrs *unbound_attrs;struct pool_workqueue *dfl_pwq; char name[WQ_NAME_LEN];struct rcu_head rcu;unsigned int flags ____cacheline_aligned;struct pool_workqueue __percpu *cpu_pwqs;struct pool_workqueue __rcu *numa_pwq_tbl[];};

linux内核使用工作者线程work_thread来处理工作队列中的各个工作,linux内核使用worker结构体表示工作者线程,worker结构体如下:

struct worker {union {struct list_head entry; struct hlist_node hentry;};**struct work_struct *current_work;** work_func_t current_func; struct pool_workqueue *current_pwq;bool desc_valid;struct list_head scheduled; struct task_struct *task; struct worker_pool *pool; struct list_head node; unsigned long last_active; unsigned int flags; int id; char desc[WORKER_DESC_LEN];**struct workqueue_struct *rescue_wq;**
};

从worker结构体可以看出,每一个worker都有一个工作队列(workqueue_struct),工作者线程处理自己工作队列中的所有工作。在实际中,我们只需定义工作(work_struct)即可,关于工作队列和工作者线程基本不需要我们去管。

如何创建work_struct?
a. 直接定义一个work_truct结构体变量即可,然后用INIT_WORK宏来初始化工作,INIT_WORK 定义如下:

#define  INIT_WORK(_work,_func)
//_work表示要初始化的工作,_func是工作对应的处理函数
//or 使用如下方法,一次性完成工作的创建和初始化,宏如下:
#define DECLARE_WORK(n, f)

b. 和tasklet 一样,工作需要调度才能运行,工作的调度函数为schedule_work,函数原型如下:

bool schedule_work(struct work_struct *work);
//work :要调度的工作

c.参考模板

//1. 定义工作work
struct work_struct testwork;void testwork_func_t(unsigned long data)
{//5.work函具体的处理内容
}//中断处理函数test_handler
irqreturn_t test_handler(int irq ,void *dev_id)
{.......//4.调度worktasklet_schedule(&testwork);
.......
}//驱动入口函数
static int __init xxx_init(coid)
{......//2.初始化workINIT_WORK(&testwork ,testwork_func_t);//3. 注册中断处理函数request_irq(xxx_irq ,test_handler ,0 , "xxx" ,&xxx_dev);
......
}

4>dts中断信息节点分析

如果使用设备数,需要在设备数中配置好中断属性信息,kernel 匹配设备树中的中断信息来配置中断。

 intc: interrupt-controller@17a00000 {compatible = "arm,gic-v3"; //匹配控制器驱动文件#interrupt-cells = <3>;//表示控制器下设备的cells大小/*interrupt-cells 描述了interrupts属性cells大小,也就是一条信息有几个cells。每个cells都是32bit整形值。对于arm的gic来说,一共有三个cells1.cells 中断类型,0表示spi中断,1表示ppi中断2.cells 中断号,对于spi中断来说0~987,ppi中断0~153.cells bit[3:0],1 上升沿触发,2 下降沿触发,4 高电平触发,8 低电平触发*/interrupt-controller; //为空,表示当前节点是中断控制器#redistributor-regions = <1>;redistributor-stride = <0x0 0x20000>;reg = <0x17a00000 0x10000>,     /* GICD */<0x17a60000 0x100000>;    /* GICR * 8 */interrupts = <1 9 4>; //指定中断号,触发方式interrupt-parent = <&intc>; //指定父中断,也就是中断控制器。ignored-save-restore-irqs = <38>;};

《linux内核中断》之 法外狂徒张三删库跑路相关推荐

  1. 删库跑路、“投毒”、改协议,开源有哪几大红线千万不能踩?

    作者 | 彭慧中 责编 | 屠敏 出品 | CSDN(ID:CSDNnews) 开源协议是开源世界里的根基.根据CSDN<2021-2022 中国开发者调查报告>数据显示,尽管目前已有94 ...

  2. 从“删库跑路”聊聊开启BinLog防止误删表数据、结构及数据库

    最近网上很多同学都在疯传疫情删库=跑路,工作中误删数据或者数据库我们一定需要跑路吗?我看未必在 MySQL数据库中我们知道 binlog 日志记录了我们对数据库的所有操作接下来就来开启程序员自救之路 ...

  3. 漫画:如何给女朋友解释什么是删库跑路?

    作者 | 漫话编程 来源 | 漫话编程(ID:mhcoding) 在DBA圈子有这样一个段子: 最近几年,经常会出现各种删库跑路的事件发生,前几天还有报道说思科离职5个月的程序员,为了报复公司,删虚拟 ...

  4. 『数据库』你以为删库跑路就能让你老板内(lei)牛(liu)满面--数据库的恢复技术

    数据库从入门到精通:戳我 文章目录 一. 事务的基本概念 1.事务 1.1what's the 事务: 1.2事务的定义 1.2.1 事务的显示定义 1.2.2 事务的隐式定义方式 2.事务的ACID ...

  5. 阿里十年DBA经验产品经理:真的不要再有一起删库跑路事件了

    最近网上又出一起删库跑路事件,本不想过多写此类事件文字,但从业13年,十年DBA工作经验,职业素养还是驱使自己写点内容,以期能够帮助广大企业客户. 本文主要以数据库产品从业者角度,介绍帮助企业减少意外 ...

  6. rm: 无法删除swap: 不允许的操作_safe-rm老板再也不用担心我删库跑路啦[视频]

    saferm 老板再不怕我删库跑路https://www.zhihu.com/video/1177717527541731328 在 linux 上,使用 rm 是一件非常危险的事情(最近又有朋友遇到 ...

  7. 漫话:如何给女朋友解释什么是删库跑路?

    在DBA圈子有这样一个段子: 最近几年,经常会出现各种删库跑路的事件发生,前几天还有报道说思科离职5个月的程序员,为了报复公司,删虚拟机跑路了. 这位思科的离职员工,仅凭一己之力,删掉了思科 456 ...

  8. 漫画:什么是删库跑路?

    作者 | 漫话编程 来源 | 漫话编程(ID:mhcoding) 在DBA圈子有这样一个段子: 最近几年,经常会出现各种删库跑路的事件发生,前几天还有报道说思科离职5个月的程序员,为了报复公司,删虚拟 ...

  9. 删库跑路事件发生,SaaS 云服务如何守护数据安全?

    作者 | 蒋敏峰 责编 | Carol 封图 | CSDN付费下载于视觉中国 近日,某SaaS服务商/微盟遭遇员工删库跑路,服务器出现大面积故障,一时间让平台上的几百万家商户生意基本停摆.这一事件发生 ...

最新文章

  1. 嵌入式linux下如何尽快播放开机音乐
  2. 关于 Group 的另一个函数
  3. __getattribute__
  4. 有意思的select~
  5. 数据类型的判断 c# 1614092544
  6. 如何估算一个分布式系统的容量
  7. 大话 JavaScript 动画
  8. Xcode下的中文乱码问题
  9. os.path.basename()
  10. 【辨异】inverse, reverse, converse
  11. 阶段5 3.微服务项目【学成在线】_day01 搭建环境 CMS服务端开发_23-页面查询服务端开发-Service及Controller...
  12. 中国 python 培训视频下载
  13. 解决 未能为数据库 '数据库用户名' 中的对象 '表名' 分配空间,因为文件组 'PRIMARY' 已满...
  14. 怎么用计算机拨号手机,手机怎么连接电脑拨号打电话
  15. centos7 ies4linux,Ubuntu 7.10中通过IEs4linux安装IE6
  16. pano2vr 缩略图添加场景名称
  17. 绿盟科技实习安服面经
  18. [前端案例]百行代码实现炫酷时钟
  19. 脉动计算机没有指令计数器,脉冲计数器电路图设计(三) - 脉冲计数器电路图大全(六款脉冲计数器电路设计原理图详解)...
  20. 【转载】200多个js技巧代码

热门文章

  1. 无人机集群任务规划方法研究综述论文解读
  2. colorfly i108w 平板电脑装ubuntu系统过程与踩坑总结
  3. 打开bat文件闪退以及‘java‘不是内部或外部命令,也不是可运行的程序或批处理文件【本人亲测解决方法】
  4. 初学Python之fractions模块下Fraction使用方法
  5. 软件测试工程师简历编写规范
  6. 4.5 路径MTU发现
  7. 企业数字化转型:聊聊数据思维!
  8. 传统零售和新零售的本质区别
  9. 【组队学习】【33期】LeetCode 刷题
  10. 从零开始搭建个人静态简历网站