当前位置: 首页 > news >正文

嵌入式Linux应用开发-基础知识-第十九章驱动程序基石③

嵌入式Linux应用开发-基础知识-第十九章驱动程序基石③

  • 第十九章 驱动程序基石③
    • 19.5 定时器
      • 19.5.1 内核函数
      • 19.5.2 定时器时间单位
      • 19.5.3 使用定时器处理按键抖动
      • 19.5.4 现场编程、上机
      • 19.5.5 深入研究:定时器的内部机制
      • 19.5.6 深入研究:找到系统滴答
    • 19.6 中断下半部tasklet
      • 19.6.1 内核函数
        • 19.6.1.1 定义 tasklet
        • 19.6.1.2 使能/禁止 tasklet
        • 19.6.1.3 调度 tasklet
        • 19.6.1.4
      • 19.6.2
      • 19.6.3

第十九章 驱动程序基石③

在这里插入图片描述

19.5 定时器

使用 GIT命令载后,本节源码位于这个目录下:

01_all_series_quickstart\ 
05_嵌入式 Linux驱动开发基础知识\source\ 
06_gpio_irq\ 07_read_key_irq_poll_fasync_block_timer   

19.5.1 内核函数

所谓定时器,就是闹钟,时间到后你就要做某些事。有 2个要素:时间、做事,换成程序员的话就是:超时时间、函数。
在内核中使用定时器很简单,涉及这些函数(参考内核源码 include\linux\timer.h):
① setup_timer(timer, fn, data):
设置定时器,主要是初始化 timer_list结构体,设置其中的函数、参数。
② void add_timer(struct timer_list *timer):
向内核添加定时器。timer->expires表示超时时间。
当超时时间到达,内核就会调用这个函数:timer->function(timer->data)。
③ int mod_timer(struct timer_list *timer, unsigned long expires):
修改定时器的超时时间,
它等同于:del_timer(timer); timer->expires = expires; add_timer(timer);
但是更加高效。
④ int del_timer(struct timer_list *timer):
删除定时器。

19.5.2 定时器时间单位

编译内核时,可以在内核源码根目录下用“ls -a”看到一个隐藏文件,它就是内核配置文件。打开后可以看到如下这项:

CONFIG_HZ=100 

这表示内核每秒中会发生 100次系统滴答中断(tick),这就像人类的心跳一样,这是 Linux系统的心跳。每发生一次 tick中断,全局变量 jiffies就会累加 1。
CONFIG_HZ=100表示每个滴答是 10ms。
定时器的时间就是基于 jiffies的,我们修改超时时间时,一般使用这 2种方法:
① 在 add_timer之前,直接修改:

timer.expires = jiffies + xxx;   // xxx表示多少个滴答后超时,也就是 xxx*10ms 
timer.expires = jiffies + 2*HZ;  // HZ等于 CONFIG_HZ,2*HZ就相当于 2秒 

② 在 add_timer之后,使用 mod_timer修改:

mod_timer(&timer, jiffies + xxx);   // xxx表示多少个滴答后超时,也就是 xxx*10ms 
mod_timer(&timer, jiffies + 2*HZ);  // HZ等于 CONFIG_HZ,2*HZ就相当于 2秒 

19.5.3 使用定时器处理按键抖动

在实际的按键操作中,可能会有机械抖动:
在这里插入图片描述
按下或松开一个按键,它的 GPIO电平会反复变化,最后才稳定。一般是几十毫秒才会稳定。 如果不处理抖动的话,用户只操作一次按键,中断程序可能会上报多个数据。
怎么处理?
① 在按键中断程序中,可以循环判断几十亳秒,发现电平稳定之后再上报
② 使用定时器
显然第 1种方法太耗时,违背“中断要尽快处理”的原则,你的系统会很卡。
怎么使用定时器?看下图:
在这里插入图片描述

核心在于:在 GPIO中断中并不立刻记录按键值,而是修改定时器超时时间,10ms后再处理。 如果 10ms内又发生了 GPIO中断,那就认为是抖动,这时再次修改超时时间为 10ms。
只有 10ms之内再无 GPIO中断发生,那么定时器的函数才会被调用。
在定时器函数中记录按键值。

19.5.4 现场编程、上机

19.5.5 深入研究:定时器的内部机制

初学者会用定时器就行,本节不用看。
怎么实现定时器,逻辑上很简单:每发生一次硬件中断时,硬件中断处理完后就会看看有没有软件中断要处理。
定时器就是通过软件中断来实现的,它属于 TIMER_SOFTIRQ软中断。
对于 TIMER_SOFTIRQ软中断,初始化代码如下:

void __init init_timers(void) 
{ init_timer_cpus(); init_timer_stats(); open_softirq(TIMER_SOFTIRQ, run_timer_softirq); 
} 

当发生硬件中断时,硬件中断处理完后,内核会调用软件中断的处理函数。对于 TIMER_SOFTIRQ,会调用 run_timer_softirq,它的函数如下:

run_timer_softirq 
__run_timers(base); while (time_after_eq(jiffies, base->clk)) { …… 
expire_timers(base, heads + levels);     fn = timer->function; data = timer->data; call_timer_fn(timer, fn, data);         fn(data); 

简单地说,add_timer函数会把 timer放入内核里某个链表;
在 TIMER_SOFTIRQ的处理函数中,会从链表中把这些超时的 timer取出来,执行其中的函数。 怎么判断是否超时?jiffies大于或等于 timer->expires时,timer就超时。
内核中有很多 timer,如果高效地找到超时的 timer?这是比较复杂的,
我们以后如果要深入讲解 timer的话,会用视频来讲解。

19.5.6 深入研究:找到系统滴答

这只是一些笔记,初学者不用看。
在开发板执行以下命令,可以看到 CPU0下有一个数值变化特别快,它就是滴答中断:

# cat /proc/interrupts CPU0 16:       2532       GPC  55 Level     i.MX Timer Tick 19:         22       GPC  33 Level     2010000.ecspi 20:        384       GPC  26 Level     2020000.serial 21:          0       GPC  98 Level     sai 

以 xxxxxx_IMX6ULL为做,滴答中断名字就是“i.MX Timer Tick”。
在 Linux内核源码目录下执行以下命令:

$ grep "i.MX Timer Tick" * -nr 
drivers/clocksource/timer-imx-gpt.c:319:        act->name = "i.MX Timer Tick"; 

打开 timer-imx-gpt.c 319行左右,可得如下源码:

 act->name = "i.MX Timer Tick"; act->flags = IRQF_TIMER | IRQF_IRQPOLL; act->handler = mxc_timer_interrupt; act->dev_id = ced; 
return setup_irq(imxtm->irq, act); 
mxc_timer_interrupt应该就是滴答中断的处理函数,代码如下: static irqreturn_t mxc_timer_interrupt(int irq, void *dev_id) { struct clock_event_device *ced = dev_id; struct imx_timer *imxtm = to_imx_timer(ced); uint32_t tstat; 
tstat = readl_relaxed(imxtm->base + imxtm->gpt->reg_tstat); imxtm->gpt->gpt_irq_acknowledge(imxtm); 
ced->event_handler(ced); 
return IRQ_HANDLED; }

在上述代码中没看到对 jiffies的累加操作啊,应该是在 ced->event_handler(ced)中进行。
ced->event_handler(ced)是哪一个函数?不太好找,我使用QEMU来调试内核,在mxc_timer_interrupt中打断点跟踪代码(以后的课程会讲怎么用 QEMU调试内核),发现它对应 tick_handle_periodic。
tick_handle_periodic位于 kernel\time\tick-common.c中,它里面的调用关系如下:

tick_handle_periodic 
tick_periodic(cpu); do_timer(1); jiffies_64 += ticks;  // jiffies就是 jiffies_64 

你为何说 jiffies就是 jiffies_64?在 arch\arm\kernel\vmlinux.lds.S有如下代码:

#ifndef __ARMEB__ 
jiffies = jiffies_64; 
#else 
jiffies = jiffies_64 + 4; 
#endif 

上述代码说明了,对于大字节序的 CPU,jiffies指向 jiffies_64的高 4字节;对于小字节序的 CPU,jiffies指向 jiffies_64的低 4字节。
对 jiffies_64的累加操作,就是对 jiffies的累加操作。

19.6 中断下半部tasklet

使用 GIT命令载后,本节源码位于这个目录下:

01_all_series_quickstart\ 
05_嵌入式 Linux驱动开发基础知识\source\ 
06_gpio_irq\ 08_read_key_irq_poll_fasync_block_timer_tasklet 

在前面我们介绍过中断上半部、下半部。中断的处理有几个原则:
① 不能嵌套;
② 越快越好。
在处理当前中断时,即使发生了其他中断,其他中断也不会得到处理,所以中断的处理要越快越好。但是某些中断要做的事情稍微耗时,这时可以把中断拆分为上半部、下半部。
在上半部处理紧急的事情,在上半部的处理过程中,中断是被禁止的;
在下半部处理耗时的事情,在下半部的处理过程中,中断是使能的。
中断上半部、下半部的关系机制,请回顾第 18.2.5节。

19.6.1 内核函数

19.6.1.1 定义 tasklet

中断下半部使用结构体 tasklet_struct来表示,它在内核源码 include\linux\interrupt.h中定义: struct tasklet_struct
{
struct tasklet_struct *next;
unsigned long state;
atomic_t count;
void (*func)(unsigned long);
unsigned long data;
};
其中的 state有 2位:
① bit0表示 TASKLET_STATE_SCHED
等于 1时表示已经执行了 tasklet_schedule把该 tasklet放入队列了;tasklet_schedule会判断该位,如果已经等于 1那么它就不会再次把 tasklet放入队列。
② bit1表示 TASKLET_STATE_RUN
等于 1时,表示正在运行 tasklet中的 func函数;函数执行完后内核会把该位清 0。
其中的 count表示该 tasklet是否使能:等于 0表示使能了,非 0表示被禁止了。对于 count非 0的tasklet,里面的 func函数不会被执行。
使用中断下半部之前,要先实现一个 tasklet_struct结构体,这可以用这 2个宏来定义结构体:

#define DECLARE_TASKLET(name, func, data) \ 
struct tasklet_struct name = { NULL, 0, ATOMIC_INIT(0), func, data } 
#define DECLARE_TASKLET_DISABLED(name, func, data) \ 
struct tasklet_struct name = { NULL, 0, ATOMIC_INIT(1), func, data } 

使用 DECLARE_TASKLET定义的 tasklet结构体,它是使能的;
使用 DECLARE_TASKLET_DISABLED定义的 tasklet结构体,它是禁止的;使用之前要先调用tasklet_enable使能它。
也可以使用函数来初始化 tasklet结构体:

extern void tasklet_init(struct tasklet_struct *t, void (*func)(unsigned long), unsigned long data); 
19.6.1.2 使能/禁止 tasklet
static inline void tasklet_enable(struct tasklet_struct *t); 
static inline void tasklet_disable(struct tasklet_struct *t); 

tasklet_enable把 count增加 1;tasklet_disable把 count减 1。

19.6.1.3 调度 tasklet
static inline void tasklet_schedule(struct tasklet_struct *t); 

把 tasklet放入链表,并且设置它的 TASKLET_STATE_SCHED状态为 1。

19.6.1.4
kill tasklet 
extern void tasklet_kill(struct tasklet_struct *t); 

如果一个 tasklet未被调度,tasklet_kill会把它的 TASKLET_STATE_SCHED状态清 0;
如果一个 tasklet已被调度,tasklet_kill会等待它执行完华,再把它的 TASKLET_STATE_SCHED状态清 0。
通常在卸载驱动程序时调用 tasklet_kill。

19.6.2

tasklet使用方法
先定义 tasklet,需要使用时调用 tasklet_schedule,驱动卸载前调用 tasklet_kill。
tasklet_schedule只是把 tasklet放入内核队列,它的 func函数会在软件中断的执行过程中被调用。

19.6.3

tasklet内部机制
作为初学者,可以不看本节。
tasklet属于 TASKLET_SOFTIRQ软件中断,入口函数为 tasklet_action,这在内核 kernel\softirq.c中设置:
在这里插入图片描述
当驱动程序调用 tasklet_schedule时,会设置 tasklet的 state为 TASKLET_STATE_SCHED,并把它放入某个链表:
在这里插入图片描述

当发生硬件中断时,内核处理完硬件中断后,会处理软件中断。对于 TASKLET_SOFTIRQ软件中断,会调用 tasklet_action函数。
执行过程还是挺简单的:从队列中找到 tasklet,进行状态判断后执行 func函数,从队列中删除 tasklet。
从这里可以看出:
① tasklet_schedule调度 tasklet时,其中的函数并不会立刻执行,而只是把 tasklet放入队列;
② 调用一次 tasklet_schedule,只会导致 tasklnet的函数被执行一次;
③ 如果 tasklet的函数尚未执行,多次调用 tasklet_schedule也是无效的,只会放入队列一次。
tasklet_action函数解析如下:
在这里插入图片描述

相关文章:

嵌入式Linux应用开发-基础知识-第十九章驱动程序基石③

嵌入式Linux应用开发-基础知识-第十九章驱动程序基石③ 第十九章 驱动程序基石③19.5 定时器19.5.1 内核函数19.5.2 定时器时间单位19.5.3 使用定时器处理按键抖动19.5.4 现场编程、上机19.5.5 深入研究:定时器的内部机制19.5.6 深入研究:找到系统滴答 1…...

一文拿捏SpringMVC的调用流程

SpringMVC的调用流程 1.核心元素: DispatcherServlet(前端控制器)HandlerMapping(处理器映射器)HandlerAdapter(处理器适配器) ---> Handler(处理器)ViewResolver(视图解析器 )---> view(视图) 2.调用流程 用户发送请求到前端控制器前端控制器接收用户请求…...

一文详解 JDK1.8 的 Lambda、Stream、LocalDateTime

Lambda Lambda介绍 Lambda 表达式(lambda expression)是一个匿名函数,Lambda表达式基于数学中的λ演算得名,直接对应于其中的lambda抽象(lambda abstraction),是一个匿名函数,即没有函数名的函数。 Lambda表达式的结构 一个 Lamb…...

WebSocket实战之二协议分析

一、前言 上一篇 WebSocket实战之一 讲了WebSocket一个极简例子和基础的API的介绍,这一篇来分析一下WebSocket的协议,学习网络协议最好的方式就是抓包分析一下什么就都明白了。 二、WebSocket协议 本想盗一张网络图,后来想想不太好&#x…...

LeetCode //C - 208. Implement Trie (Prefix Tree)

208. Implement Trie (Prefix Tree) A trie (pronounced as “try”) or prefix tree is a tree data structure used to efficiently store and retrieve keys in a dataset of strings. There are various applications of this data structure, such as autocomplete and s…...

【Python】time模块和datetime模块的部分函数说明

时间戳与日期 在说到这俩模块之前,首先先明确几个概念: 时间戳是个很单纯的东西,没有“时区”一说,因为时间戳本质上是经过的时间。日常生活中接触到的“日期”、“某点某时某分”准确的说是时间点,都是有时区概念的…...

Python 无废话-基础知识元组Tuple详讲

“元组 Tuple”是一个有序、不可变的序列集合,元组的元素可以包含任意类型的数据,如整数、浮点数、字符串等,用()表示,如下示例: 元组特征 1) 元组中的各个元素,可以具有不相同的数据类型,如 T…...

【Win】Microsoft Spy++学习笔记

参考资料 《用VisualStudio\Spy查窗口句柄,监控窗口消息》 1. 安装 Spy是VS中的工具,所以直接安装VS就可以了; 2. 检查应用程序架构 ChatGPT-Bing: 对于窗口应用程序分析,确定应用程序是32位还是64位是很重要的,因…...

如何解决版本不兼容Jar包冲突问题

如何解决版本不兼容Jar包冲突问题 引言 “老婆”和“妈妈”同时掉进水里,先救谁? 常言道:编码五分钟,解冲突两小时。作为Java开发来说,第一眼见到ClassNotFoundException、 NoSuchMethodException这些异常来说&…...

数据结构—归并排序-C语言实现

引言:归并排序跟快速排序一样,都运用到了分治的算法,但是归并排序是一种稳定的算法,同时也具备高效,其时间复杂度为O(N*logN) 算法图解: 然后开始归并: 就是这个思想,拆成最小子问题…...

Multiple CORS header ‘Access-Control-Allow-Origin‘ not allowed

今天在修改天天生鲜超市项目的时候,因为使用了前后端分离模式,前端通过网关统一转发请求到后端服务,但是第一次使用就遇到了问题,比如跨域问题: 但是,其实网关里是有配置跨域的,只是忘了把前端项…...

msvcp100.dll丢失怎样修复,msvcp100.dll丢失问题全面解析

msvcp100.dll是一个动态链接库文件,属于 Microsoft Visual C Redistributable 的一个组件。它包含了 C 运行时库,这些库在运行程序时会被加载到内存中。msvcp100.dll文件的主要作用是为基于 Visual C 编写的程序提供必要的运行时支持。 当您运行一个基于…...

最新AI智能问答系统源码/AI绘画系统源码/支持GPT联网提问/Prompt应用+支持国内AI提问模型

一、AI创作系统 SparkAi创作系统是基于国外很火的ChatGPT进行开发的AI智能问答系统和AI绘画系统。本期针对源码系统整体测试下来非常完美,可以说SparkAi是目前国内一款的ChatGPT对接OpenAI软件系统。那么如何搭建部署AI创作ChatGPT?小编这里写一个详细图…...

全连接网络实现回归【房价预测的数据】

也是分为data,model,train,test import torch import torch.nn as nn import torch.nn.functional as F import torch.optim as optimclass FCNet(nn.Module):def __init__(self):super(FCNet,self).__init__()self.fc1 nn.Linear(331,200)s…...

mysql八股

1、请你说说mysql索引,以及它们的好处和坏处 检索效率、存储资源、索引 索引就像指向表行的指针,是一个允许查询操作快速确定哪些行符合WHERE子句中的条件,并检索到这些行的其他列值的数据结构索引主要有普通索引、唯一索引、主键索引、外键…...

MATLAB算法实战应用案例精讲-【优化算法】狐猴优化器(LO)(附MATLAB代码实现)

代码实现 MATLAB LO.m %======================================================================= % Lemurs Optimizer: A New Metaheuristic Algorithm % for Global Optimization (LO)% This work is published in Journal of "Applied …...

C#WPF动态资源和静态资源应用实例

本文实例演示C#WPF动态资源和静态资源应用 一、资源概述 静态资源(StaticResource)指的是在程序载入内存时对资源的一次性使用,之后就不再访问这个资源了。 动态资源(DynamicResource)指的是在程序运行过程中然会去访问资源。 WPF中,每个界面元素都含有一个名为Resources…...

游戏逆向中的 NoClip 手段和安全应对方式

文章目录 墙壁边界寻找碰撞 NoClip 是一种典型的黑客行为,允许你穿过墙壁,所以 NoClip 又可以认为是避免碰撞体积的行为 墙壁边界 游戏中设置了碰撞体作为墙壁边界,是 玩家对象 和墙壁发生了碰撞,而不是 相机 玩家对象有他的 X…...

nodejs+vue流浪猫狗救助领养elementui

第三章 系统分析 10 3.1需求分析 10 3.2可行性分析 10 3.2.1技术可行性:技术背景 10 3.2.2经济可行性 11 3.2.3操作可行性: 11 3.3性能分析 11 3.4系统操作流程 12 3.4.1管理员登录流程 12 3.4.2信息添加流程 12 3.4.3信息删除流程 13 第四章 系统设计与…...

Css Flex 弹性布局中的换行与溢出处理方法

Css Flex 弹性布局中的换行与溢出处理方法 CSS弹性布局(Flex)是CSS3中的一种新的布局方式,它能够帮助我们更加灵活地布局元素。在Flex弹性布局中,元素的布局仅依赖于父容器的设置,而不再需要复杂的相对或绝对定位。本…...

linux系统与应用

Windows中的硬盘和盘符的关系; 硬盘通常为一块到两块;数量与盘符没有直接关系;一块硬盘可以分为多个盘符,如c,d,e,f,g等;当然理论上也可以一块硬盘只有一个盘符;学习linux时,最好使用固态硬盘&a…...

MySQL的结构化语言 DDL DML DQL DCL

一、SQL结构化语言介绍 数据查询语言DQL:其语句称为“数据检索语言”,用以从库中获取数据,确定数据怎样在应用程序给出,保留select是dql(也是所有sql)用的最多的动词 数据操作语言DML:其语句包括动词insert…...

P5488 差分与前缀和

传送门:洛谷 前题提要:包含了简单的生成函数思想以及多项式乘法,是一道不可多得的多项式好题.故记录一下. 题意:给定一个长为 n 的序列 a,求出其 k 阶差分或前缀和。结果的每一项都需要对 1004535809取模。 对于差分和前缀和我们分开来讨论. 先讨论前缀和部分: …...

uboot启动流程-uboot内存分配

一. uboot启动流程 _main 函数中会调用 board_init_f 函数,本文继续简单分析一下 board_init_f 函数。 具体分析 board_init_f函数的第二部分:内存分配代码。 本文继上一篇文章的学习,地址如下: uboot启动流程-涉及board_init…...

LeetCode 面试题 08.02. 迷路的机器人

文章目录 一、题目二、C# 题解 一、题目 设想有个机器人坐在一个网格的左上角,网格 r 行 c 列。机器人只能向下或向右移动,但不能走到一些被禁止的网格(有障碍物)。设计一种算法,寻找机器人从左上角移动到右下角的路径…...

画CMB天图使用Planck配色方案

使用Planck的配色方案: 全天图: 或者方形图: 使用下面设置即可: import pspy, pixell from pspy.so_config import DEFAULT_DATA_DIR pixell.colorize.mpl_setdefault("planck")此方法不会改变matplotlib默认配色方案…...

成都瀚网科技有限公司:抖店精选联盟怎么用?

抖音精选联盟是抖音电商平台提供的一项服务,旨在为商家提供更多的推广机会和销售渠道。然而,很多人对于如何使用抖店精选联盟以及如何开通这项服务不太了解。本文将为您详细介绍抖店精选联盟的使用和激活流程。 第一节:如何使用抖店精选联盟 …...

第二章:最新版零基础学习 PYTHON 教程(第五节 - Python 输入/输出–如何在Python中打印而不换行?)

一般来说,从 C/C++ 切换到 Python 的人想知道如何在 python 中打印两个或多个变量或语句而不进入新行。由于Python print() 函数默认以换行符结尾。Python 有一个预定义的格式,如果你使用 print(a_variable) 那么它会自动转到下一行。 例子: # 输入:[csdn, csdnforcsdn] …...

C++实现集群聊天服务器

C实现集群聊天服务器 JSON Json是一种轻量级的数据交换模式(也叫做数据序列化方式)。Json采用完全独立于编程语言的文本格式来存储和表示数据。见解和清晰的层次结构使得Json称为理想的数据交换语言。易于阅读和编写。同时也易于支持机器解析和生成&am…...

40 二叉树的直径

二叉树的直径 总结:两个节点之间最长路径 路径的结点数 - 1题解1 递归——DFS 给你一棵二叉树的根节点,返回该树的 直径。 二叉树的直径是指树中任意两个节点之间最长路径的长度。这条路径可能经过也可能不经过根节点 root 。 两节点之间路径的长度由…...

大型大型网站建设/爱站小工具圣经

{"folders":[{//你项目的相对路径"path": "."}],"settings":{//这里是项目的设置,sublime会自动载入这里的设置(你也可以在这里填写其他插件的配置)"xdebug": {"path_mapping":…...

赣州网站建设如何/自助建站工具

通常,我们用到数据库会有很多种,这里就不做讨论了,我们只来说说如何用room来存储一些复杂数据结构。首先看此文章的都假设你已经看过了room的简单用法,如果没有看过,那你可能需要先去看看了。假设,我们从后…...

学做炒菜的网站/需要优化的网站有哪些

题号: 10247 时限:1000ms 限制内存:32768KB 题目: 字符串包含判断描述给定两个字符串a,b。问在a中是否包含b,如果包含则输出yes,否则输出no。 输入格式一行两个字符串,使用空格分开。 输出格式y…...

做网站用哪里的服务器比较好/百度推广投诉中心

【单选题】与文件系统阶段相比,关系数据库技术的数据管理方式具有许多特点,但不包括【填空题】输入矩阵 A[3,2,5;8,-5,7;-1,4,9] ,使用全下标方式用 A(2,2) 取出元素 ,使用单下标方式用 取出元素 “-5 ” 。【单选题】如果要回滚一个事务,则要使用( )语句【单选题】SELECT语句的…...

代做动画毕业设计的网站/网络优化师

2019独角兽企业重金招聘Python工程师标准>>> 安装前准备 http://mirrors.hust.edu.cn/apache/zookeeper/ http://apache.fayea.com/apache-mirror/tomcat/tomcat-7/v7.0.55/src/ https://issues.apache.org/jira/secure/attachment/12436620/ZooInspector.ziphttp:/…...

做动漫网站需要服务器么/安徽百度关键词优化

php linux下安装xml扩展1、进入PHP安装源码包,找到ext下的ftp,进入cd /home/local/php-5.6.25/ext/xml2、第二步/usr/local/php/bin/phpize3、第三步./configure --with-php-config/usr/local/php/bin/php-config4、第四步make && make install5…...