So Tired !_! 逆水行舟, 不进则退!

15Mar/19

ubuntu12.04升级gcc至4.8

Posted by Nick Xu

gcc 4.8.1 是第一个完全支持C++11 的编译器,Windows上可以安装mingw版的,在sourceforge 上有下载,安装也比较方便。在Linux上安装的话需要首先安装一些依赖库。在Ubuntu12.04 lts默认安装的是gcc4.6.3,其实该版本也支持一些c++11的特性,可以通过增加“-std=c++0x” 编译选项来使用这些特性,但是对多线程库的支持较差,gcc 4.8.1 是通过ppa来安装的,因此需要安装ppa repository 。下面就来看一下安装步骤:

首先安装依赖:

sudo apt-get install libgmp-dev
sudo apt-get install libmpfr4 libmpfr-dev
sudo apt-get install libmpc-dev libmpc2
sudo apt-get install libtool
sudo apt-get install m4
sudo apt-get install bison
sudo apt-get install flex
sudo apt-get install autoconf

接下来进入到安装gcc4.8.1 的主要步骤:

sudo add-apt-repository ppa:ubuntu-toolchain-r/test
sudo apt-get update

sudo apt-get install gcc-4.8
sudo apt-get install g++-4.8
sudo apt-get install gcc-4.8-multilib
sudo apt-get install g++-4.8-multilib
sudo apt-get install gcc-4.8-doc
sudo update-alternatives –install /usr/bin/gcc gcc /usr/bin/gcc-4.8 20
sudo update-alternatives –install /usr/bin/g++ g++ /usr/bin/g++-4.8 20
sudo update-alternatives –config gcc
sudo update-alternatives –config g++

sudo apt-get update
sudo apt-get upgrade -y
sudo apt-get dist-upgrade

之后就可以使用了,通过使用gcc -v 命令可以查看当前gcc的版本是不是已经变成4.8了
---------------------
作者:ithewei
来源:CSDN
原文:https://blog.csdn.net/GG_SiMiDa/article/details/75127393
版权声明:本文为博主原创文章,转载请附上博文链接!

 

 

编译安装报错unrecognized command line option “-std=c++11” 无法识别的命令行选项“-std=c++11” 的解决办法

我们在使用linux系统编译安装报错unrecognized command line option “-std=c++11” 无法识别的命令行选项“-std=c++11” 的提示。出现这个编译错误的原因在g++ gcc 版本不够高。
解决方法:
更新gcc版本默认一般情况是4.4.7版本的gcc
命令查看:

#gcc -v

更新方法

1、下载压缩包进行编译安装

http://ftp.gnu.org/gnu/gcc/
这里是官网提供的下载地址
image.png

下面开始下载解压

wget  
tar -jxvf gcc-4.8.2.tar.bz2

image.png

2、下载供编译需求的依赖项

这个神奇的脚本文件会帮我们下载、配置、安装依赖库,可以节约我们大量的时间和精力。

cd gcc-4.8.0
./contrib/download_prerequisites

3、建立一个文件夹

mkdir gcc-build-4.8.2
cd gcc-build-4.8.2

4、生成Makefile文件

../configure -enable-checking=release -enable-languages=c,c++ -disable-multilib

5、编译安装

make && make intsall

6、检验
image.png

尝试写一个C++11特性的程序段 tryCpp11.cc,使用了shared_ptr

 1 //tryCpp11.cc 
 2 #include <iostream> 
 3 #include <memory> 
 4  
 5 int main() 
 6 { 
 7     std::shared_ptr<int> pInt(new int(5)); 
 8     std::cout << *pInt << std::endl; 
 9     return 0;
 10 }

验证文件:

g++ -std=c++11 -o tryCpp11 tryCpp11.cc
./tryCpp11

 

Filed under: Linux Comments Off
22Jan/19

h3c wa2620i fit ap新设备初始化处理

Posted by Nick Xu

今天新到一台2620i发现无法正常使用, 连上控制台发现存储空间都没格式化, 所以最基础的application都没有, 需要初始化处理一下.

接入console线,通电后在控制台按ctrl+b进入bootware菜单

按9进入Storage Device Operation菜单, 再按1进入Display All Available Nonvolatile Storage Device(s)查看所有存储设备

============================================================================

|Note:the operating device is flash                                        |

|NO.  Device Name  File System  Total Size  Available Space                |

|1    flash        ---          ---         ---                            |

|0    Exit                                                                 |

============================================================================

如果出现类似上面的内容, 即flash这一行里面的file system, totalsize等都是空的, 那就需要先格式化存储, 如果已经显示出空间等信息则不需要格式化操作

按0回到上级菜单, 按CTRL+F开始格式化存储,等待格式化完后,开始下载更新固件

下载更新固件:

首先下载3cdaemon作为TFTP服务器, 然后把ap和电脑接入到同一局域名, 我把电脑ip设置为192.168.10.5, 把wa2600a_fit.bin放到3cdaemon的上传下载目录

在bootware主菜单按3进入Enter Ethernet SubMenu菜单, 再按5进入网络设置,文件名我输入的是wa2600a_fit.bin, ip我使用默认的, 即服务端192.168.10.5和客户端192.168.10.4

设置好后, 按2开始更新主程序, 待更新完即可正常使用.

Tagged as: , , Comments Off
8Jan/19

NodeMCU8266驱动8×8点阵max7219

Posted by Nick Xu

Since the awesome devsaurus recently fixed important SPI issues (#50 is from Christmas 2014!) in NodeMCU it’s now possible to run MAX7219 8×8 LED matrix displays with an ESP8266 and the NodeMCU firmware.

Connect NodeMCU with MAX7219 display

According to the pin layout for the NodeMCU dev kit v1 and v2 (see comparison) you need to connect the MAX7212 as follows:

MAX7219 ESP8266 NodeMCU devkit
VCC +3.3V 3V3
GND GND GND
DIN HSPID/HMOSI D7
CS HSPICS/HCS D8
CLK HSPICLK/HSCLK D5

In the second column I listed commonly found names on pin layouts and schemas. The values in the third column signify the actual pin numbers as printed on the NodeMCU development boards.

Drawing on the MAX7219

Writing and drawing on those 8×8 matrix displays took some getting used to for me. Interestingly the fact that you can rotate them whichever way you like made it more difficult rather than less difficult. There is a way to identify pin 1 but eventually it doesn’t really matter. However, you need to understand how “characters” are represented.

In font files (e.g. cp437.h) you’ll usually see one character per line as an array of 8 hex values. This could look as follows for ‘0’ (zero) :

{ 0x3E, 0x7F, 0x71, 0x59, 0x4D, 0x7F, 0x3E, 0x00 }

MAX7219 8x8 LED matrix displaying 0x3EThere is one byte per column of the 8×8 matrix i.e. 8 bytes. One byte signifies which of the 8 LEDs in a column should be turned on. 0x3E in binary form is 00111110 and every bit represents the state of one LED. So, the first LED and the last two will be OFF while the others will be ON. Again, the notion of “first” and “last” of course depends on the orientation of the matrix but as long as all characters are defined consistently it doesn’t matter. 0x3E on the matrix will look as shown on the left.

 

Moody, cycling through smilys

Moody is a simple program that declares 3 smilys and an array which contains all of them. It shows how to initialize NodeMCU SPI for the MAX7219 8×8 LED matrix and how to cycle through the smilys array and display one face after the other in a loop.

Showtime, please

 

 

Filed under: 单片机 Comments Off
3Jan/19

PWM波控制舵机总结

Posted by Nick Xu

一、关于舵机:

舵机(英文叫Servo):它由直流电机、减速齿轮组、传感器和控制电路组成的一套自动控制系统。通过发送信号,指定输出轴旋转角度。舵机一般而言都有最大旋转角度(比如180度。)与普通直流电机的区别主要在,直流电机是一圈圈转动的,舵机只能在一定角度内转动,不能一圈圈转(数字舵机可以在舵机模式和电机模式中切换,没有这个问题)。普通直流电机无法反馈转动的角度信息,而舵机可以。用途也不同,普通直流电机一般是整圈转动做动力用,舵机是控制某物体转动一定角度用(比如机器人的关节)。

舵机的形状和大小多的让人眼花缭乱,大致可以分为下面这几种(如图所示)

最右边的是常见的标准舵机,中间两个小的是微型舵机,左边魁梧的那个是大扭力舵机。图上这几种舵机都是三线控制。
制作机器人常用的舵机有下面几种,而且每种的固定方式也不同,如果从一个型号换成一个型号,整个机械结构都需要重新设计。
第一种是MG995,优点是价格便宜,金属齿轮,耐用度也不错。缺点是扭力比较小,所以负载不能太大,如果做双足机器人之类的这款舵机不是很合适,因为腿部受力太大。做做普通的六足,或者机械手还是不错的。

第二种是SR 403,这款舵机是网友xqi2因MG995做双足机器人抖动太厉害,摸索找到的,经过测试。制作双足机器人不错~~~至少不抖了。优点是扭力大,全金属齿轮,价格也还算便宜。缺点嘛。。。做工很山寨。。。其他缺点等待反馈

第三种就是传说中的数字舵机AX12+,这个是久经考验的机器人专用舵机。除了价格高,使用RS485串口通信(控制板就得换数字舵机专用控制板),其他都是优点。
下图是一个普通模拟舵机的分解图,其组成部分主要有齿轮组、电机、电位器、电机控制板、壳体这几大部分。
电机控制板主要是用来驱动电机和接受电位器反馈回来的信息。电机嘛,动力的来源了,这个不用太多解释。电位器这里的作用主要是通过其旋转后产生的电阻的变化,把信号发送回电机控制板,使其判断输出轴角度是否输出正确。齿轮组的作用主要是力量的放大,使小功率电机产生大扭矩。
舵机底壳拆开后就可以看到,主要是电机与控制板
控制板拿起来后下方是与控制板连接的电位器
从顶部来看电机与电位器,与电机齿轮直接相连的为第一级放大齿轮。
经过一级齿轮放大后,再经过二、三、四级放大齿轮,最后再通过输出轴输出。
通过上面两图可以很清晰的看到,本舵机是4级齿轮放大机构,就是通过这么一层层的把小的力量放大,使得这么一个小小的电机能有15KG的扭力。

二、舵机控制方法:

舵机的伺服系统由可变宽度的脉冲来进行控制,控制线是用来传送脉冲的。脉冲的参数有最小值,最大值,和频率。一般而言,舵机的基准信号都是周期为20ms,宽度为1.5ms。这个基准信号定义的位置为中间位置。舵机有最大转动角度,中间位置的定义就是从这个位置到最大角度与最小角度的量完全一样。最重要的一点是,不同舵机的最大转动角度可能不相同,但是其中间位置的脉冲宽度是一定的,那就是1.5ms。如下图:
角度是由来自控制线的持续的脉冲所产生。这种控制方法叫做脉冲调制。脉冲的长短决定舵机转动多大角度。例如:1.5毫秒脉冲会到转动到中间位置(对于180°舵机来说,就是90°位置)。当控制系统发出指令,让舵机移动到某一位置,并让他保持这个角度,这时外力的影响不会让他角度产生变化,但是这个是由上限的,上限就是他的最大扭力。除非控制系统不停的发出脉冲稳定舵机的角度,舵机的角度不会一直不变。
当舵机接收到一个小于1.5ms的脉冲,输出轴会以中间位置为标准,逆时针旋转一定角度。接收到的脉冲大于1.5ms情况相反。不同品牌,甚至同一品牌的不同舵机,都会有不同的最大值和最小值。一般而言,最小脉冲为1ms,最大脉冲为2ms。如下图:

三、小总结:

首先是舵机的引线,一般为三线控制(没有接触过不是三线的),红色为电源,棕色为地,黄色为信号。控制舵机的时候,需要不断的给PWM波才能使得舵机在某个角度有扭矩。

Filed under: 单片机 Comments Off
28Dec/18

H3C fit ap同一个SSID下, 设置每个AP的不同VLAN

Posted by Nick Xu

1
2
3
4
5
6
7
8
9
10
11
12
13
14
system-view
vlan 12
quit
interface Vlan-interface 1
undo ip address
quit
interface Vlan-interface 12
ip address 192.168.168.249 255.255.255.0
interface GigabitEthernet 1/0/1
port link-type hybrid
port hybrid vlan 12 untagged
wlan ac ip 192.168.168.100
wlan management-interface Vlan-interface 12
port hybrid pvid vlan 12
Tagged as: , Comments Off
10Dec/18

端口tagged和untagged详解

Posted by Nick Xu

情况列举 Switch收发 Switch对标记的处理 remark
Access (接收) Tagged = PVID 不接收 注:部分高端产品可能接收。
Access (接收) Tagged =/ PVID 不接收 注:部分高端产品可能接收。
Access (接收) Untagged 接收 增加tag=PVID 从PC

Access (发送) Tagged = PVID 转发 删除tag
Access (发送) Tagged =/ PVID 不转发 不处理
Access (发送) Untagged 无此情况 无此情况 无此情况

Trunk (接收) Tagged = PVID 接收 不修改tag
Trunk (接收) Tagged =/ PVID 接收 不修改tag
Trunk (接收) Untagged 接收 增加tag=PVID
Trunk (发送) Tagged = PVID If Passing then 转发 删除tag
Trunk (发送) Tagged =/ PVID If Passing then 转发 不修改tag
Trunk (发送) Untagged 无此情况 无此情况 无此情况(注)

Hybrid (接收) Tagged = PVID 接收 不修改tag 对端是trunk
Hybrid (接收) Tagged =/ PVID 接收 不修改tag 对端是trunk
Hybrid (接收) Untagged 接收 增加tag=PVID 类Trunk
Hybrid (发送) Tagged = PVID Tag 和 untag 中列出的vlan可以passing 看Tag项和untag项
Hybrid (发送) Tagged =/ PVID Tag 和 untag 中列出的vlan可以passing 看Tag项和untag项
Hybrid (发送) Untagged 无此情况 无此情况 无此情况(注)
我来解释一下
收报文:
Acess端口1、收到一个报文,判断是否有VLAN信息:如果没有则打上端口的PVID,并进行交换转发,如果有则直接丢弃(缺省)
发报文:
Acess端口: 1、将报文的VLAN信息剥离,直接发送出去
收报文:
trunk端口: 1、收到一个报文,判断是否有VLAN信息:如果没有则打上端口的PVID,并进行交换转发,如果有判断该trunk端口是否允许该 VLAN的数据进入:如果可以则转发,否则丢弃
发报文:
trunk端口: 1、比较端口的PVID和将要发送报文的VLAN信息,如果两者相等则剥离VLAN信息,再发送,如果不相等则直接发送
收报文:
hybrid端口: 1、收到一个报文
2、判断是否有VLAN信息:如果没有则打上端口的PVID,并进行交换转发,如果有则判断该hybrid端口是否允许该VLAN的数据进入:如果可以则转发,否则丢弃
发报文:
hybrid端口:1、判断该VLAN在本端口的属性(disp interface 即可看到该端口对哪些VLAN是untag, 哪些VLAN是tag)
2、如果是untag则剥离VLAN信息,再发送,如果是tag则直接发送

先呈请一下上面的几个帖子的术语:

Tag为IEEE802.1Q协议定义的VLAN的标记在数据帧中的标示;
ACCESS端口,TRUNK端口是厂家对某一种端口的叫法,并非IEEE802.1Q协议的标准定义;

这个数据交换的过程比较复杂,如果想解释的话,首先要了解一下几个IEEE802.1Q协议的定理;
1、下面是定义的各种端口类型对各种数据帧的处理方法;

  Tagged数据帧 Untagged数据帧
in out in out
Tagged端口 原样接收 原样发送 按端口PVID打TAG标记 按照PVID打TAG标记
Untagged端口 丢弃 去掉TAG标记 按端口PVID打TAG标记 原样发送

2、所谓的Untagged Port和tagged Port不是讲述物理端口的状态,而是讲诉物理端口所拥有的某一个VID的状态,所以一个物理端口可以在某一个VID上是Untagged Port,在另一个VID上是tagged Port;

3、一个物理端口只能拥有一个PVID,当一个物理端口拥有了一个PVID的时候,必定会拥有和PVID的TAG等同的VID,而且在这个VID上,这个物理端口必定是Untagged Port;

4、PVID的作用只是在交换机从外部接受到Untagged 数据帧的时候给数据帧添加TAG标记用的,在交换机内部转发数据的时候PVID不起任何作用;

5、拥有和TAG标记一致的VID的物理端口,不论是否在这个VID上是Untagged Port或者tagged Port,都可以接受来自交换机内部的标记了这个TAG标记的tagged 数据帧;

6、拥有和TAG标记一致的VID的物理端口,只有在这个VID上是tagged Port,才可以接受来自交换机外部的标记了这个TAG标记的tagged 数据帧;

以下是神州数码对命令的定义(各个厂家对命令的定义可能不一定一致,但是都必须遵循上面的定理):

1、Trunk端口就是在一个物理端口上增加这个交换机所有VLAN的VID标示,并且除了和这个物理端口PVID标示一致的VID标示为Untagged Port外,在其他的VID上都是Tagged Port;

2、Access端口就是指拥有一个和PVID标记相同的VID的物理端口,在这个VID上,遵循定理一定为untagged Port;

在了解了以上的基础理论之后,我们在来看一下楼主的问题:

一个数据包从PC机发出经过ACCESS端口->TRUNK端口->TRunk->ACCESS->PC数据包发生了怎么样的变化?

我们先把上述的描述变换为IEEE802.1Q的标准描述:

一个数据包从PC机发出经过(Untagged 数据帧)

ACCESS端口(PVID定义为100,VID=100=Untagged Port)->

TRUNK端口(PVID定义为1〈出厂配置,没有更改〉,VID=1=Untagged Port,VID=100=tagged Port)->

另一个交换机的TRunk端口(PVID定义为1〈出厂配置,没有更改〉,VID=1=Untagged Port,VID=100=tagged Port)->

另一个交换机的ACCESS端口(PVID定义为100,VID=100=Untagged Port)->

PC数据包发生了怎么样的变化?(Untagged 数据帧)

首先假设两台交换机刚刚开机(MAC地址表为空)从PC机发出的数据帧进入交换机的ACCESS端口以后,会按照这个端口的PVID打100的Tag标记,根据交换机的转发原理,交换机会把这个数据帧转发给VID=100的所有端口(除了进口以外),这个过程叫做VLAN Flood;参照上面的定理1;

由于Trunk端口拥有VID=100,所以才可接受这个标记Tag为100的tagged数据帧;参照上面的定理5;

由于Trunk端口在VID=100上为tagged Port,所以在发送数据帧出交换机的时候,不改变Tagged数据帧的结构;参照上面的定理1;

到了另一个交换机的Trunk端口的时候,由于Trunk端口拥有VID=100,所以才可接受这个标记Tag为100的tagged数据帧;参照上面的定理6;

另一个交换机的Trunk端口,接收到标记tag为100的tagged数据帧,并不作任何的更改;参照上面的定理1;

另一个交换机收到到标记tag为100的tagged数据帧,根据交换机的转发原理,交换机会把这个数据帧转发给VID=100的所有端口(除了进口以外);参照交换机交换原理(受到一个未知目的MAC数据帧);

这样另一个交换机的ACCESS端口就可以收到标记tag为100的tagged数据帧;参照上面的定理5;

另一个交换机的ACCESS端口在发出标记tag为100的tagged数据帧的时候,会去掉TAG标记,转发untagged数据帧给PC;参照上面的定理1;

这样PC机就收到了这个数据;

读 深入理解华为交换机的hybrid端口模式

Tag,untag以及交换机的各种端口模式是网络工程技术人员调试交换机时接触最多的概念了,然而笔者发现在实际工作中技术人员往往对这些概念似懂非懂,笔者根据自己的理解再结合一个案例,试图向大家阐明这些概念

untag就是普通的ethernet报文,普通PC机的网卡是可以识别这样的报文进行通讯;
tag报文结构的变化是在源mac地址和目的mac地址之后,加上了4bytes的vlan信息,也就是vlan tag头;一般来说这样的报文普通PC机的网卡是不能识别的

带802.1Q的帧是在标准以太网帧上插入了4个字节的标识。其中包含:
2个字节的协议标识符(TPID),当前置0x8100的固定值,表明该帧带有802.1Q的标记信息。
2个字节的标记控制信息(TCI),包含了三个域。
Priority域,占3bits,表示报文的优先级,取值0到7,7为最高优先级,0为最低优先级。该域被802.1p采用。
规范格式指示符(CFI)域,占1bit,0表示规范格式,应用于以太网;1表示非规范格式,应用于Token Ring。
VLAN ID域,占12bit,用于标示VLAN的归属。

以太网端口的三种链路类型:Access、Hybrid和Trunk:
Access类型的端口只能属于1个VLAN,一般用于连接计算机的端口;
Trunk类型的端口可以允许多个VLAN通过,可以接收和发送多个VLAN的报文,一般用于交换机之间连接的端口;
Hybrid类型的端口可以允许多个VLAN通过,可以接收和发送多个VLAN的报文,可以用于交换机之间连接,也可以用于连接用户的计算机。
Hybrid端口和Trunk端口在接收数据时,处理方法是一样的,唯一不同之处在于发送数据时:Hybrid端口可以允许多个VLAN的报文发送时不打标签,而Trunk端口只允许缺省VLAN的报文发送时不打标签。

在这里大家要理解端口的缺省VLAN这个概念
Access端口只属于1个VLAN,所以它的缺省VLAN就是它所在的VLAN,不用设置;
Hybrid端口和Trunk端口属于多个VLAN,所以需要设置缺省VLAN ID。缺省情况下,Hybrid端口和Trunk端口的缺省VLAN为VLAN 1
如果设置了端口的缺省VLAN ID,当端口接收到不带VLAN Tag的报文后,则将报文转发到属于缺省VLAN的端口;当端口发送带有VLAN Tag的报文时,如果该报文的VLAN ID与端口缺省的VLAN ID相同,则系统将去掉报文的VLAN Tag,然后再发送该报文。

注:对于华为交换机缺省VLAN被称为“Pvid Vlan”, 对于思科交换机缺省VLAN被称为“Native Vlan”

交换机接口出入数据处理过程:

Acess端口收报文:
收到一个报文,判断是否有VLAN信息:如果没有则打上端口的PVID,并进行交换转发,如果有则直接丢弃(缺省)

Acess端口发报文:
将报文的VLAN信息剥离,直接发送出去
trunk端口收报文:
收到一个报文,判断是否有VLAN信息:如果没有则打上端口的PVID,并进行交换转发,如果有判断该trunk端口是否允许该 VLAN的数据进入:如果可以则转发,否则丢弃

trunk端口发报文:
比较端口的PVID和将要发送报文的VLAN信息,如果两者相等则剥离VLAN信息,再发送,如果不相等则直接发送

hybrid端口收报文:
收到一个报文,判断是否有VLAN信息:如果没有则打上端口的PVID,并进行交换转发,如果有则判断该hybrid端口是否允许该VLAN的数据进入:如果可以则转发,否则丢弃(此时端口上的untag配置是不用考虑的,untag配置只对发送报文时起作用)

hybrid端口发报文:
1、判断该VLAN在本端口的属性(disp interface 即可看到该端口对哪些VLAN是untag, 哪些VLAN是tag)
2、如果是untag则剥离VLAN信息,再发送,如果是tag则直接发送

Filed under: Linux, 嵌入式 Comments Off
7Dec/18

(苹果Mac OSX系统)绿联USB无法连接网络解决方案

Posted by Nick Xu

检测驱动是否装载:

sudo kextload /Library/Extensions/AX88772.kext
1
note:如果已装载成功,则执行成功,否则显示错误提示。

操作如下:
step1:关机重启立马按住command+R,等待进入language界面。
step2:按需求选择后,在工具栏打开终端。
step3:输入csrutil status查看当前状态,状态为enabled。
step4:输入csrutil disable,关闭System Integrity Protection。
step5:重启电脑。
再次重新装载,如若查看还未装载成功,请下载最新驱动AX88179。
---------------------
作者:shannon-Li
来源:CSDN
原文:https://blog.csdn.net/weixin_42555131/article/details/81843315
版权声明:本文为博主原创文章,转载请附上博文链接!

Filed under: Mac OS Comments Off
2Nov/18

MySQL的binlog日志

Posted by Nick Xu

binlog 基本认识
MySQL的二进制日志可以说是MySQL最重要的日志了,它记录了所有的DDL和DML(除了数据查询语句)语句,以事件形式记录,还包含语句所执行的消耗的时间,MySQL的二进制日志是事务安全型的。

一般来说开启二进制日志大概会有1%的性能损耗(参见MySQL官方中文手册 5.1.24版)。二进制有两个最重要的使用场景:
其一:MySQL Replication在Master端开启binlog,Mster把它的二进制日志传递给slaves来达到master-slave数据一致的目的。
其二:自然就是数据恢复了,通过使用mysqlbinlog工具来使恢复数据。

二进制日志包括两类文件:二进制日志索引文件(文件名后缀为.index)用于记录所有的二进制文件,二进制日志文件(文件名后缀为.00000*)记录数据库所有的DDL和DML(除了数据查询语句)语句事件。
一、开启binlog日志:
vi编辑打开mysql配置文件
# vi /usr/local/mysql/etc/my.cnf
在[mysqld] 区块
设置/添加 log-bin=mysql-bin 确认是打开状态(值 mysql-bin 是日志的基本名或前缀名);

重启mysqld服务使配置生效
# pkill mysqld
# /usr/local/mysql/bin/mysqld_safe --user=mysql &
二、也可登录mysql服务器,通过mysql的变量配置表,查看二进制日志是否已开启 单词:variable[ˈvɛriəbəl] 变量

登录服务器
# /usr/local/mysql/bin/mysql -uroot -p123456

mysql> show variables like 'log_%';
+----------------------------------------+---------------------------------------+
| Variable_name | Value |
+----------------------------------------+---------------------------------------+
| log_bin | ON | ------> ON表示已经开启binlog日志
| log_bin_basename | /usr/local/mysql/data/mysql-bin |
| log_bin_index | /usr/local/mysql/data/mysql-bin.index |
| log_bin_trust_function_creators | OFF |
| log_bin_use_v1_row_events | OFF |
| log_error | /usr/local/mysql/data/martin.err |
| log_output | FILE |
| log_queries_not_using_indexes | OFF |
| log_slave_updates | OFF |
| log_slow_admin_statements | OFF |
| log_slow_slave_statements | OFF |
| log_throttle_queries_not_using_indexes | 0 |
| log_warnings | 1 |
+----------------------------------------+---------------------------------------+

三、常用binlog日志操作命令
1.查看所有binlog日志列表
mysql> show master logs;

2.查看master状态,即最后(最新)一个binlog日志的编号名称,及其最后一个操作事件pos结束点(Position)值
mysql> show master status;

3.刷新log日志,自此刻开始产生一个新编号的binlog日志文件
mysql> flush logs;
注:每当mysqld服务重启时,会自动执行此命令,刷新binlog日志;在mysqldump备份数据时加 -F 选项也会刷新binlog日志;

4.重置(清空)所有binlog日志
mysql> reset master;
四、查看某个binlog日志内容,常用有两种方式:

1.使用mysqlbinlog自带查看命令法:
注: binlog是二进制文件,普通文件查看器cat more vi等都无法打开,必须使用自带的 mysqlbinlog 命令查看
binlog日志与数据库文件在同目录中(我的环境配置安装是选择在/usr/local/mysql/data中)
在MySQL5.5以下版本使用mysqlbinlog命令时如果报错,就加上 “--no-defaults”选项

# /usr/local/mysql/bin/mysqlbinlog /usr/local/mysql/data/mysql-bin.000013
下面截取一个片段分析:

...............................................................................
# at 552
#131128 17:50:46 server id 1 end_log_pos 665 Query thread_id=11 exec_time=0 error_code=0 ---->执行时间:17:50:46;pos点:665
SET TIMESTAMP=1385632246/*!*/;
update zyyshop.stu set name='李四' where id=4 ---->执行的SQL
/*!*/;
# at 665
#131128 17:50:46 server id 1 end_log_pos 692 Xid = 1454 ---->执行时间:17:50:46;pos点:692
...............................................................................

注: server id 1 数据库主机的服务号;
end_log_pos 665 pos点
thread_id=11 线程号
2.上面这种办法读取出binlog日志的全文内容较多,不容易分辨查看pos点信息,这里介绍一种更为方便的查询命令:

mysql> show binlog events [IN 'log_name'] [FROM pos] [LIMIT [offset,] row_count];

选项解析:
IN 'log_name' 指定要查询的binlog文件名(不指定就是第一个binlog文件)
FROM pos 指定从哪个pos起始点开始查起(不指定就是从整个文件首个pos点开始算)
LIMIT [offset,] 偏移量(不指定就是0)
row_count 查询总条数(不指定就是所有行)

截取部分查询结果:
*************************** 20. row ***************************
Log_name: mysql-bin.000021 ----------------------------------------------> 查询的binlog日志文件名
Pos: 11197 ----------------------------------------------------------> pos起始点:
Event_type: Query ----------------------------------------------------------> 事件类型:Query
Server_id: 1 --------------------------------------------------------------> 标识是由哪台服务器执行的
End_log_pos: 11308 ----------------------------------------------------------> pos结束点:11308(即:下行的pos起始点)
Info: use `zyyshop`; INSERT INTO `team2` VALUES (0,345,'asdf8er5') ---> 执行的sql语句
*************************** 21. row ***************************
Log_name: mysql-bin.000021
Pos: 11308 ----------------------------------------------------------> pos起始点:11308(即:上行的pos结束点)
Event_type: Query
Server_id: 1
End_log_pos: 11417
Info: use `zyyshop`; /*!40000 ALTER TABLE `team2` ENABLE KEYS */
*************************** 22. row ***************************
Log_name: mysql-bin.000021
Pos: 11417
Event_type: Query
Server_id: 1
End_log_pos: 11510
Info: use `zyyshop`; DROP TABLE IF EXISTS `type`

这条语句可以将指定的binlog日志文件,分成有效事件行的方式返回,并可使用limit指定pos点的起始偏移,查询条数;

A.查询第一个(最早)的binlog日志:
mysql> show binlog events\G;

B.指定查询 mysql-bin.000021 这个文件:
mysql> show binlog events in 'mysql-bin.000021'\G;

C.指定查询 mysql-bin.000021 这个文件,从pos点:8224开始查起:
mysql> show binlog events in 'mysql-bin.000021' from 8224\G;

D.指定查询 mysql-bin.000021 这个文件,从pos点:8224开始查起,查询10条
mysql> show binlog events in 'mysql-bin.000021' from 8224 limit 10\G;

E.指定查询 mysql-bin.000021 这个文件,从pos点:8224开始查起,偏移2行,查询10条
mysql> show binlog events in 'mysql-bin.000021' from 8224 limit 2,10\G;
五、恢复binlog日志实验(zyyshop是数据库)
1.假设现在是凌晨4:00,我的计划任务开始执行一次完整的数据库备份:

将zyyshop数据库备份到 /root/BAK.zyyshop.sql 文件中:
# /usr/local/mysql/bin/mysqldump -uroot -p123456 -lF --log-error=/root/myDump.err -B zyyshop > /root/BAK.zyyshop.sql
......

大约过了若干分钟,备份完成了,我不用担心数据丢失了,因为我有备份了,嘎嘎~~~

由于我使用了-F选项,当备份工作刚开始时系统会刷新log日志,产生新的binlog日志来记录备份之后的数据库“增删改”操作,查看一下:
mysql> show master status;
+------------------+----------+--------------+------------------+
| File | Position | Binlog_Do_DB | Binlog_Ignore_DB |
+------------------+----------+--------------+------------------+
| mysql-bin.000023 | 120 | | |
+------------------+----------+--------------+------------------+
也就是说, mysql-bin.000023 是用来记录4:00之后对数据库的所有“增删改”操作。
2.早9:00上班了,业务的需求会对数据库进行各种“增删改”操作~~~~~~~
@ 比如:创建一个学生表并插入、修改了数据等等:
CREATE TABLE IF NOT EXISTS `tt` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`name` varchar(16) NOT NULL,
`sex` enum('m','w') NOT NULL DEFAULT 'm',
`age` tinyint(3) unsigned NOT NULL,
`classid` char(6) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
导入实验数据
mysql> insert into zyyshop.tt(`name`,`sex`,`age`,`classid`) values('yiyi','w',20,'cls1'),('xiaoer','m',22,'cls3'),('zhangsan','w',21,'cls5'),('lisi','m',20,'cls4'),('wangwu','w',26,'cls6');
查看数据
mysql> select * from zyyshop.tt;
+----+----------+-----+-----+---------+
| id | name | sex | age | classid |
+----+----------+-----+-----+---------+
| 1 | yiyi | w | 20 | cls1 |
| 2 | xiaoer | m | 22 | cls3 |
| 3 | zhangsan | w | 21 | cls5 |
| 4 | lisi | m | 20 | cls4 |
| 5 | wangwu | w | 26 | cls6 |
+----+----------+-----+-----+---------+
中午时分又执行了修改数据操作
mysql> update zyyshop.tt set name='李四' where id=4;
mysql> update zyyshop.tt set name='小二' where id=2;

修改后的结果:
mysql> select * from zyyshop.tt;
+----+----------+-----+-----+---------+
| id | name | sex | age | classid |
+----+----------+-----+-----+---------+
| 1 | yiyi | w | 20 | cls1 |
| 2 | 小二 | m | 22 | cls3 |
| 3 | zhangsan | w | 21 | cls5 |
| 4 | 李四 | m | 20 | cls4 |
| 5 | wangwu | w | 26 | cls6 |
+----+----------+-----+-----+---------+
假设此时是下午18:00,莫名地执行了一条悲催的SQL语句,整个数据库都没了:
mysql> drop database zyyshop;
3.此刻杯具了,别慌!先仔细查看最后一个binlog日志,并记录下关键的pos点,到底是哪个pos点的操作导致了数据库的破坏(通常在最后几步);

备份一下最后一个binlog日志文件:
# ll /usr/local/mysql/data | grep mysql-bin
# cp -v /usr/local/mysql/data/mysql-bin.000023 /root/

此时执行一次刷新日志索引操作,重新开始新的binlog日志记录文件,理论说 mysql-bin.000023 这个文件不会再有后续写入了(便于我们分析原因及查找pos点),以后所有数据库操作都会写入到下一个日志文件;
mysql> flush logs;
mysql> show master status;

4.读取binlog日志,分析问题
方式一:使用mysqlbinlog读取binlog日志:
# /usr/local/mysql/bin/mysqlbinlog /usr/local/mysql/data/mysql-bin.000023

方式二:登录服务器,并查看(推荐):
mysql> show binlog events in 'mysql-bin.000023';

以下为末尾片段:
+------------------+------+------------+-----------+-------------+------------------------------------------------------------+
| Log_name | Pos | Event_type | Server_id | End_log_pos | Info |
+------------------+------+------------+-----------+-------------+------------------------------------------------------------+
| mysql-bin.000023 | 922 | Xid | 1 | 953 | COMMIT /* xid=3820 */ |
| mysql-bin.000023 | 953 | Query | 1 | 1038 | BEGIN |
| mysql-bin.000023 | 1038 | Query | 1 | 1164 | use `zyyshop`; update zyyshop.tt set name='李四' where id=4|
| mysql-bin.000023 | 1164 | Xid | 1 | 1195 | COMMIT /* xid=3822 */ |
| mysql-bin.000023 | 1195 | Query | 1 | 1280 | BEGIN |
| mysql-bin.000023 | 1280 | Query | 1 | 1406 | use `zyyshop`; update zyyshop.tt set name='小二' where id=2|
| mysql-bin.000023 | 1406 | Xid | 1 | 1437 | COMMIT /* xid=3823 */ |
| mysql-bin.000023 | 1437 | Query | 1 | 1538 | drop database zyyshop |
+------------------+------+------------+-----------+-------------+------------------------------------------------------------+

通过分析,造成数据库破坏的pos点区间是介于 1437--1538 之间,只要恢复到1437前就可。
5.现在把凌晨备份的数据恢复:

# /usr/local/mysql/bin/mysql -uroot -p123456 -v < /root/BAK.zyyshop.sql;

注: 至此截至当日凌晨(4:00)前的备份数据都恢复了。
但今天一整天(4:00--18:00)的数据肿么办呢?就得从前文提到的 mysql-bin.000023 新日志做文章了......
6.从binlog日志恢复数据

恢复语法格式:
# mysqlbinlog mysql-bin.0000xx | mysql -u用户名 -p密码 数据库名

常用选项:
--start-position=953 起始pos点
--stop-position=1437 结束pos点
--start-datetime="2013-11-29 13:18:54" 起始时间点
--stop-datetime="2013-11-29 13:21:53" 结束时间点
--database=zyyshop 指定只恢复zyyshop数据库(一台主机上往往有多个数据库,只限本地log日志)

不常用选项:
-u --user=name Connect to the remote server as username.连接到远程主机的用户名
-p --password[=name] Password to connect to remote server.连接到远程主机的密码
-h --host=name Get the binlog from server.从远程主机上获取binlog日志
--read-from-remote-server Read binary logs from a MySQL server.从某个MySQL服务器上读取binlog日志

小结:实际是将读出的binlog日志内容,通过管道符传递给mysql命令。这些命令、文件尽量写成绝对路径;

A.完全恢复(本例不靠谱,因为最后那条 drop database zyyshop 也在日志里,必须想办法把这条破坏语句排除掉,做部分恢复)
# /usr/local/mysql/bin/mysqlbinlog /usr/local/mysql/data/mysql-bin.000021 | /usr/local/mysql/bin/mysql -uroot -p123456 -v zyyshop

B.指定pos结束点恢复(部分恢复):
@ --stop-position=953 pos结束点
注:此pos结束点介于“导入实验数据”与更新“name='李四'”之间,这样可以恢复到更改“name='李四'”之前的“导入测试数据”
# /usr/local/mysql/bin/mysqlbinlog --stop-position=953 --database=zyyshop /usr/local/mysql/data/mysql-bin.000023 | /usr/local/mysql/bin/mysql -uroot -p123456 -v zyyshop

在另一终端登录查看结果(成功恢复了):
mysql> select * from zyyshop.tt;
+----+----------+-----+-----+---------+
| id | name | sex | age | classid |
+----+----------+-----+-----+---------+
| 1 | yiyi | w | 20 | cls1 |
| 2 | xiaoer | m | 22 | cls3 |
| 3 | zhangsan | w | 21 | cls5 |
| 4 | lisi | m | 20 | cls4 |
| 5 | wangwu | w | 26 | cls6 |
+----+----------+-----+-----+---------+

C.指定pso点区间恢复(部分恢复):
更新 name='李四' 这条数据,日志区间是Pos[1038] --> End_log_pos[1164],按事务区间是:Pos[953] --> End_log_pos[1195];

更新 name='小二' 这条数据,日志区间是Pos[1280] --> End_log_pos[1406],按事务区间是:Pos[1195] --> End_log_pos[1437];

c1.单独恢复 name='李四' 这步操作,可这样:
# /usr/local/mysql/bin/mysqlbinlog --start-position=1038 --stop-position=1164 --database=zyyshop /usr/local/mysql/data/mysql-bin.000023 | /usr/local/mysql/bin/mysql -uroot -p123456 -v zyyshop

也可以按事务区间单独恢复,如下:
# /usr/local/mysql/bin/mysqlbinlog --start-position=953 --stop-position=1195 --database=zyyshop /usr/local/mysql/data/mysql-bin.000023 | /usr/local/mysql/bin/mysql -uroot -p123456 -v zyyshop
c2.单独恢复 name='小二' 这步操作,可这样:
# /usr/local/mysql/bin/mysqlbinlog --start-position=1280 --stop-position=1406 --database=zyyshop /usr/local/mysql/data/mysql-bin.000023 | /usr/local/mysql/bin/mysql -uroot -p123456 -v zyyshop

也可以按事务区间单独恢复,如下:
# /usr/local/mysql/bin/mysqlbinlog --start-position=1195 --stop-position=1437 --database=zyyshop /usr/local/mysql/data/mysql-bin.000023 | /usr/local/mysql/bin/mysql -uroot -p123456 -v zyyshop
c3.将 name='李四'、name='小二' 多步操作一起恢复,需要按事务区间,可这样:
# /usr/local/mysql/bin/mysqlbinlog --start-position=953 --stop-position=1437 --database=zyyshop /usr/local/mysql/data/mysql-bin.000023 | /usr/local/mysql/bin/mysql -uroot -p123456 -v zyyshop
D.在另一终端登录查看目前结果(两名称也恢复了):
mysql> select * from zyyshop.tt;
+----+----------+-----+-----+---------+
| id | name | sex | age | classid |
+----+----------+-----+-----+---------+
| 1 | yiyi | w | 20 | cls1 |
| 2 | 小二 | m | 22 | cls3 |
| 3 | zhangsan | w | 21 | cls5 |
| 4 | 李四 | m | 20 | cls4 |
| 5 | wangwu | w | 26 | cls6 |
+----+----------+-----+-----+---------+

E.也可指定时间区间恢复(部分恢复):除了用pos点的办法进行恢复,也可以通过指定时间区间进行恢复,按时间恢复需要用mysqlbinlog命令读取binlog日志内容,找时间节点。
比如,我把刚恢复的tt表删除掉,再用时间区间点恢复
mysql> drop table tt;

@ --start-datetime="2013-11-29 13:18:54" 起始时间点
@ --stop-datetime="2013-11-29 13:21:53" 结束时间点

# /usr/local/mysql/bin/mysqlbinlog --start-datetime="2013-11-29 13:18:54" --stop-datetime="2013-11-29 13:21:53" --database=zyyshop /usr/local/mysql/data/mysql-bin.000021 | /usr/local/mysql/bin/mysql -uroot -p123456 -v zyyshop

总结:所谓恢复,就是让mysql将保存在binlog日志中指定段落区间的sql语句逐个重新执行一次而已。

Filed under: Linux Comments Off
23Oct/18

关于 “does not contain bitcode.”的错误解决办法

Posted by Nick Xu

Xcode7运行项目时出现了如下的错误:

does not contain bitcode. You must rebuild it with bitcode enabled
(Xcode setting ENABLE_BITCODE), obtain an updated library from the
vendor, or disable bitcode for this target. for architecture armv7

关于什么bitcode的错误,那bitcode是个什么鬼呢?

什么是 bitcode ?

通俗解释:在线版安卓ART模式。

Apple 官方文档– App Distribution Guide – App Thinning (iOS, watchOS) 是这样定义的:

Bitcode is an intermediate representation of a compiled program. Apps you upload to iTunes Connect that contain bitcode will be compiled and linked on the App Store. Including bitcode will allow Apple to re-optimize your app binary in the future without the need to submit a new version of your app to the store.

翻译过来就是:

bitcode 是被编译程序的一种中间形式的代码。包含 bitcode 配置的程序将会在 App Store 上被编译和链接。 bitcode 允许苹果在后期重新优化我们程序的二进制文件,而不需要我们重新提交一个新的版本到 App Store 上。

在 Xcode简介— What’s New in Xcode-New Features in Xcode 7 中这样描述:

Bitcode. When you archive for submission to the App Store, Xcode will compile your app into an intermediate representation. The App Store will then compile the bitcode down into the 64 or 32 bit executables as necessary.

也就是

当我们提交程序到 App Store上时, Xcode 会将程序编译为一个中间表现形式( bitcode )。然后 App store 会再将这个 bitcode 编译为可执行的64位或32位程序。

请看这里 http://blog.csdn.net/soindy/article/details/48518717

如果你的应用也准备启用 Bitcode 编译机制,就需要注意以下几点:

Xcode 7默认开启 Bitcode ,如果应用开启 Bitcode,那么其集成的其他第三方库也需要是 Bitcode编译的包才能真正进行 Bitcode 编译

开启 Bitcode 编译后,编译产生的 .app 体积会变大(中间代码,不是用户下载的包),且 .dSYM文件不能用来崩溃日志的符号化(用户下载的包是 Apple 服务重新编译产生的,有产生新的符号文件)

通过 Archive 方式上传 AppStore 的包,可以在Xcode的Organizer工具中下载对应安装包的新的符号文件

出现的问题原因是什么呢?

原来是某些二进制库不支持bitcode.而Xcode默认是要支持bitcode的,而且如果支持的话,其中所有的二进制库和framework都必须包含bitcode.

怎么样解决呢?

我们可以直接将bitcode直接关掉就可以了。target —> Built Seeting —>搜索 bitcode —>将Yes置为No
---------------------
作者:ismilesky
来源:CSDN
原文:https://blog.csdn.net/ismilesky/article/details/50721365
版权声明:本文为博主原创文章,转载请附上博文链接!

Filed under: Mac OS Comments Off
23Oct/18

如何隐藏SDK中(.a库之间的冲突)符号 iOS/C/C++

Posted by Nick Xu

一、 问题引入

在当下的开发中,应用的功能做的越来越复杂,工程也越来越大,所以为了

尽可能缩短开发周期,不可避免的会用到许多第三方库,随之而来的也会遇到好

多问题。比如,程序调用函数funa,funa函数从在于两个库liba.a,libb.a中,

并且程序执行需要连接这两个库,那么程序执行时是调用liba.a中funa还是调用

的libb.a中的funa呢?

其实这个取决于链接时的顺序,比如先链接的liba.a,这个时候通过liba.a的导出符号表就可以找到funa在liba.a中定义,并加入符号表中;链接libb.a的

时候发现符号表已经存在funa,就不会再次更新符号表,所以调用的始终是liba.a中的funa函数。

这里的调用严重的依赖于链接库加载的顺序,很大程度上会导致混论。作为SDK的提供者,我们尤其要避免这点。

正常我们使用的库中包含了好多符号信息,如图1所示:

图1

这些符号信息有以下几个弊端:

1、增大了库的体积;

2、隐蔽性较差;

3、容易带来冲突。在开发过程中第三点带来的问题尤其严重,特别是当我们提供的SDK用到第

三方库的时候(因为使用我们SDK的客户也有可能用到跟我们一样的第三方库)。

二、 解决办法

1、对第三方库处理  (C/C++)

下面继续以x264(下文以libx264.a带过)为例说明如何编译第三方的库。

没有隐藏符号的第三方库如“图1”所示,函数前面会带有external的标示。

在最终对外发布的SDK中_x264_predict_16x16_dc_c还是打着external的标签,

及对外可见。如图2所示:

图2

隐藏符号后,在libx264.a中,原先打上external标签的函数,会以private external标识。如图3所示:

图3

那么如何才能得到我们想要的、打上private external标签的库呢,有两种方

法可以做到。

1)对每个函数加属性__attribute__((visibility(“hidden”))) void funa_hidden()

{

printf(“hidden symbol\n”);

}

void funa_visible()

{

printf(“exported symbol”);

}

这样做的好处是可以根据需要对每个函数做定制处理。但若我们用到的三方库代码量大,这种方法就是费时费力了。

2)编译库时统一处理

利用gcc的扩展属性,编译库时加上-fvisibility=hidden。

a)静态库

gcc –static –o libtest.a –fvisibility=hidden –c test.c

b)动态库

gcc –dynamic –o libtest.so –fvisility=hidden –c test.c,其中dynamic为clang的写法,大部分gcc写法为shared。

上边两种方法只处理了c/c++,因为语法问题,汇编需要做特殊里,但也是在函数头加属性,但它的属性写法为.private_extern。.macro function name, export=0, align=2

.macro endfunc

ELF .size \name, .

- \name

FUNC .endfunc

.purgem endfunc

.endm

.text

.align \align

.if \export

.global EXTERN_ASM\name

ELF .type EXTERN_ASM\name, %function

FUNC .func EXTERN_ASM\name

EXTERN_ASM\name:

.private_extern

EXTERN_ASM\name

.else

ELF .type

FUNC .func    \name:   .endif

.endm

\name, %function

\name

因为需要处理的汇编文件较少,所以对汇编采用了直接编辑源文件的方法。

其实个人觉得也应该能在编译时做统一处理,有兴趣的可以自己找一下方法。

2、对xcode工程的处理 (iOS)

对xcode工程处理相对直观、简单了许多。只需在工程的设置里做如下处理。

1)打开工程设置,跳转到build setting页面;

2)搜索hidden;

3)将Symbols Hidden by Default设置Yes;

图4

其实通过观察编译的过程可以发现,通过上述设置,苹果最终将其转化为步

骤1的命令进行编译。编译的结果也是在库里加了private external而已。

3、符号剥离

最后一步,也是最关键的一步,就是真正将步骤1或步骤2中打上private

external标签的函数做最终的处理,把它们从要发布的库里剥离。

1)首先设置prelink

在target的build setting里搜索prelink,将Perform Single-Object Prelink置为

Yes,然后把该工程需要的库都直接拖到Prelink libraries中。如图5所示:

图5

2)设置 post process

将Deployment Postprocessing置为Yes。如图6所示:

图6

3)设置剥离方式

将Strip Style设置为Non-Global Symbols。如图7所示:

图7

到目前为止,所有的设置都已经完成,接下来编译。有兴趣的同学可以观察

一下编译的过程,会发现通过设置prelink,xcode会将库里所有的目标文件根据

你支持的architecture分类打包,如libxxx-armv7-master.o/libxxx-arm64-master.o,

最后一步执行Strip命令将所有需要隐藏的符号剥离。

作者:atme
链接:https://www.jianshu.com/p/761fca990325
來源:简书
简书著作权归作者所有,任何形式的转载都请联系作者获得授权并注明出处。

Filed under: Mac OS Comments Off
site
site