All posts by hua

升级ios 10后系统出现卡顿的解决方法(苹果官方方法,亲测有效)

iphone 5s升级iOS 10后,有明显的卡顿。打客服后,提供如下方法,确实没卡顿现象了,但耗电感觉没什么变化。方法如下,简单的说就是用iTunes做恢复:

恢复系统会导致数据丢失,恢复之前请备份数据,恢复系统之前请打开设备-iCloud-关闭查找我的iPhone-注销iCloud账户,以免出现激活锁导致设备无法使用

备份方法:
打开 iTunes 并将设备连接到电脑。
如果出现信息询问设备密码或让您“信任此电脑”,请按屏幕上所示的步骤操作。
当您的 iPhone出现在 iTunes 中时,选择您的设备
现在只需点按“立即备份”。点击立即备份时请不要勾选加密,因为如果一旦忘记密码备份将永远无法恢复。
该过程结束后,您可以在 iTunes 的“偏好设置”>“设备”中查看您是否成功进行了备份。(如果您使用的是 Windows 版 iTunes,请在 iTunes 窗口顶部的菜单栏中选取“编辑”>“设备”。)您应该会在设备名称旁边看到 iTunes 创建该备份的日期和时间。

强制恢复方法:
您好,恢复系统操作方法:退出电脑杀毒软件,打开itunes软件,数据线连接手机与电脑,按住手机home键和开关机键,一直不要松开,设备出现数据线连接itunes图标,观察电脑itunes弹出“设备出现问题,需要更新或恢复”,松开手机按钮,点“恢复”-“恢复并更新”-“下一步“-”同意“,电脑itunes自动下载安装包后将自动恢复,此过程需要20分钟左右,途中手机会关机重启走进度,属正常现象。操作结束后设备弹出“hello”根据提示设置即可使用!



C++11——auto的用法

C++11中引入的auto主要有两种用途:自动类型推断和返回值占位。auto在C++98中的标识临时变量的语义,由于使用极少且多余,在C++11中已被删除。前后两个标准的auto,完全是两个概念。

1.自动类型推断

auto自动类型推断,用于从初始化表达式中推断出变量的数据类型。通过auto的自动类型推断,可以大大简化我们的编程工作。下面是一些使用auto的例子。

2.返回值占位

3.使用注意事项

(1).我们可以使用valatile,pointer(*),reference(&),rvalue reference(&&) 来修饰auto

(2).用auto声明的变量必须初始化

(3).auto不能与其他类型组合连用

(4).函数和模板参数不能被声明为auto

(5).定义在堆上的变量,使用了auto的表达式必须被初始化

(6).以为auto是一个占位符,并不是一个他自己的类型,因此不能用于类型转换或其他一些操作,如sizeof和typeid

(7).定义在一个auto序列的变量必须始终推导成同一类型

(8).auto不能自动推导成CV-qualifiers(constant & volatile qualifiers),除非被声明为引用类型

(9).auto会退化成指向数组的指针,除非被声明为引用



Linux网络编程笔记

概述

Linux网络编程是C/C++开发必须掌握的技能。

常用网络模型

select模型

epoll模型

epoll支持水平触发和边缘触发,理论上来说边缘触发性能更高,但是使用更加复杂,因为任何意外的丢失事件都会造成请求处理错误。Nginx就使用了epoll的边缘触发模型。

这里提一下水平触发和边缘触发就绪通知的区别,这两个词来源于计算机硬件设计。它们的区别是只要句柄满足某种状态,水平触发就会发出通知;而只有当句柄状态改变时,边缘触发才会发出通知。例如一个socket经过长时间等待后接收到一段100k的数据,两种触发方式都会向程序发出就绪通知。假设程序从这个socket中读取了50k数据,并再次调用监听函数,水平触发依然会发出就绪通知,而边缘触发会因为socket“有数据可读”这个状态没有发生变化而不发出通知且陷入长时间的等待。

因此在使用边缘触发的 api 时,要注意每次都要读到 socket返回 EWOULDBLOCK为止。 否则netstat 的recv-q会持续增加。

通常来说,et方式是比较危险的方式,如果要使用et方式,那么,应用程序应该 1、将socket设置为non-blocking方式 2、epoll_wait收到event后,read或write需要读到没有数据为止,write需要写到没有数据为止(对于non-blocking socket来说,EAGAIN通常是无数据可读,无数据可写的返回状态).

主要函数

socket(创建socket文件描述符)

int socket(int domain, int type,int protocol);

domain:指定协议族(AF_UNIX和AF_INET等),AF_UNIX只能够用于单一的Unix系统进程间通信,也称为本地socket,性能很好。AF_INET是针对Internet的,可以进行机器之间的通信,如常用的TCP和UDP等。

type: 指定通讯协议,常用的有SOCK_STREAM,SOCK_DGRAM等。SOCK_STREAM就是TCP协议,提供按顺序的、可靠的、双向的、面向连接的比特流。SOCK_DGRAM就是UDP协议,提供定长的、不可靠的、无连接的通信。

protocol: 因为指定了type,一般设为0即可。

返回值:成功返回文件描述符。失败时返回-1,错误码见errno。

bind(绑定本地IP和端口)

int bind(int sockfd, struct sockaddr *addr, int addrlen);

sockfd: 是由socket函数创建的文件描述符。addrlen: 是sockaddr结构的长度。

addr: 指定需要绑定的本地IP和端口,结构定义如下。其中sin_family一般为AF_INET, sin_addr设置为INADDR_ANY或IP地址,sin_port为端口号。sin_zero[8]用0来填充,目的是让sockaddr_in结构的大小与sockaddr结构的大小一致。

返回值:成功返回0。失败时返回-1,错误码见errno。

listen(监听端口)

int listen(int sockfd,int backlog);

sockfd: 为bind后的文件描述符。

backlog: 设置请求队列的最大长度。当此队列满了之后,客户端会收到ECONNREFUSED错误。

返回值:成功返回0。失败时返回-1,错误码见errno。

accept(响应客户端的连接请求)

int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);

accept是阻塞的,与客户端建立连接后返回,客户端地址保存在addr里。在TCP三次握手的第三次握手时返回。

sockfd: 为listen后的文件描述符。

addr: 是建立连接的客户端地址指针,为传出参数。

返回值:成功返回文件描述符。失败时返回-1,错误码见errno。

connect(请求与服务端建立连接)

int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);

connect是客户端请求连接服务端的函数。调用时发起TCP三次握手的第一次握手,在TCP三次握手的第二次握手时返回。

sockfd: socket返回的文件描述符。

addr: 用于指定服务器端的连接信息,其中sin_add是服务端的地址。

addrlen: addr的长度。

返回值:成功返回0。失败时返回-1,错误码见errno。

read(读取数据)

ssize_t read(int fd, void *buf, size_t count);

任何类型的文件描述符都可能通过这个函数读取数据,当然包括socket。

fd: 文件描述符。

buf: 读取的数据。

count: 读取的数据长度。

返回值:成功返回读取的字节数,返回0表示已经读完,小于0表示有错误,错误码见errno。

write(写入数据)

ssize_t write(int fd, const void *buf, size_t count);

任何类型的文件描述符都可能通过这个函数写入数据,当然包括socket。

fd: 文件描述符。

buf: 写入的数据。

count: 写入的数据长度。

返回值:成功返回写入的字节数,小于0表示有错误,错误码见errno。

recv

send

常见错误码

  1. EINTR: 表示在写的时候出现了中断错误。
  2. EPIPE: 表示网络连接出现了问题(对方已经关闭了连接)。
  3. ECONNREST:

常用工具

netstat, ping, telnet, ss, nslookup, dig




Nginx工作原理

1.Nginx的模块与工作原理

Nginx由内核和模块组成,其中,内核的设计非常微小和简洁,完成的工作也非常简单,仅仅通过查找配置文件将客户端请求映射到一个location block(location是Nginx配置中的一个指令,用于URL匹配),而在这个location中所配置的每个指令将会启动不同的模块去完成相应的工作。

Nginx的模块从结构上分为核心模块、基础模块和第三方模块:

核心模块:HTTP模块、EVENT模块和MAIL模块

基础模块:HTTP Access模块、HTTP FastCGI模块、HTTP Proxy模块和HTTP Rewrite模块,

第三方模块:HTTP Upstream Request Hash模块、Notice模块和HTTP Access Key模块。

用户根据自己的需要开发的模块都属于第三方模块。正是有了这么多模块的支撑,Nginx的功能才会如此强大。

Nginx的模块从功能上分为如下三类。

Handlers(处理器模块)。此类模块直接处理请求,并进行输出内容和修改headers信息等操作。Handlers处理器模块一般只能有一个。

Filters (过滤器模块)。此类模块主要对其他处理器模块输出的内容进行修改操作,最后由Nginx输出。

Proxies (代理类模块)。此类模块是Nginx的HTTP Upstream之类的模块,这些模块主要与后端一些服务比如FastCGI等进行交互,实现服务代理和负载均衡等功能。

下图是Nginx模块常规的HTTP请求和响应的过程:
nginx-http-flow

2.Nginx的进程模型

在工作方式上,Nginx分为单工作进程和多工作进程两种模式。在单工作进程模式下,除主进程外,还有一个工作进程,工作进程是单线程的;在多工作进程模式下,每个工作进程包含多个线程。Nginx默认为单工作进程模式。

Nginx在启动后,会有一个master进程和多个worker进程。

master进程

主要用来管理worker进程,包含:接收来自外界的信号,向各worker进程发送信号,监控worker进程的运行状态,当worker进程退出后(异常情况下),会自动重新启动新的worker进程。

master进程充当整个进程组与用户的交互接口,同时对进程进行监护。它不需要处理网络事件,不负责业务的执行,只会通过管理worker进程来实现重启服务、平滑升级、更换日志文件、配置文件实时生效等功能。

我们要控制nginx,只需要通过kill向master进程发送信号就行了。比如kill -HUP pid,则是告诉nginx,从容地重启nginx,我们一般用这个信号来重启nginx,或重新加载配置,因为是从容地重启,因此服务是不中断的。master进程在接收到HUP信号后是怎么做的呢?首先master进程在接到信号后,会先重新加载配置文件,然后再启动新的worker进程,并向所有老的worker进程发送信号,告诉他们可以光荣退休了。新的worker在启动后,就开始接收新的请求,而老的worker在收到来自master的信号后,就不再接收新的请求,并且在当前进程中的所有未处理完的请求处理完成后,再退出。当然,直接给master进程发送信号,这是比较老的操作方式,nginx在0.8版本之后,引入了一系列命令行参数,来方便我们管理。比如,./nginx -s reload,就是来重启nginx,./nginx -s stop,就是来停止nginx的运行。如何做到的呢?我们还是拿reload来说,我们看到,执行命令时,我们是启动一个新的nginx进程,而新的nginx进程在解析到reload参数后,就知道我们的目的是控制nginx来重新加载配置文件了,它会向master进程发送信号,然后接下来的动作,就和我们直接向master进程发送信号一样了。

worker进程:

而基本的网络事件,则是放在worker进程中来处理了。多个worker进程之间是对等的,他们同等竞争来自客户端的请求,各进程互相之间是独立的。一个请求,只可能在一个worker进程中处理,一个worker进程,不可能处理其它进程的请求。worker进程的个数是可以设置的,一般我们会设置与机器cpu核数一致,这里面的原因与nginx的进程模型以及事件处理模型是分不开的。

worker进程之间是平等的,每个进程,处理请求的机会也是一样的。当我们提供80端口的http服务时,一个连接请求过来,每个进程都有可能处理这个连接,怎么做到的呢?首先,每个worker进程都是从master进程fork过来,在master进程里面,先建立好需要listen的socket(listenfd)之后,然后再fork出多个worker进程。所有worker进程的listenfd会在新连接到来时变得可读,为保证只有一个进程处理该连接,所有worker进程在注册listenfd读事件前抢accept_mutex,抢到互斥锁的那个进程注册listenfd读事件,在读事件里调用accept接受该连接。当一个worker进程在accept这个连接之后,就开始读取请求,解析请求,处理请求,产生数据后,再返回给客户端,最后才断开连接,这样一个完整的请求就是这样的了。我们可以看到,一个请求,完全由worker进程来处理,而且只在一个worker进程中处理。

nginx的进程模型,可以由下图来表示:
nginx-processor



程序的内存布局

,就是那些由编译器在需要的时候分配,在不需要的时候自动清除的变量的存储区。里面的变量通常是局部变量、函数参数等。在一个进程中,位于用户虚拟地址空间顶部的是用户栈,编译器用它来实现函数的调用。和堆一样,用户栈在程序执行期间可以动态地扩展和收缩。

,就是那些由 new 分配的内存块,他们的释放编译器不去管,由我们的应用程序去控制,一般一个 new 就要对应一个 delete。如果程序员没有释放掉,那么在程序结束后,操作系统会自动回收。堆可以动态地扩展和收缩。

自由存储区,就是那些由 malloc 等分配的内存块,他和堆是十分相似的,不过它是用 free 来结束自己的生命的。(这个不太对吧?外代码区呢?)

全局/静态存储区,全局变量和静态变量被分配到同一块内存中,在以前的 C 语言中,全局变量又分为初始化的和未初始化的(初始化的全局变量和静态变量在一块区域,未初始化的全局变量与静态变量在相邻的另一块区域,同时未被初始化的对象存储区可以通过 void* 来访问和操纵,程序结束后由系统自行释放),在 C++ 里面没有这个区分了,他们共同占用同一块内存区。

常量存储区,这是一块比较特殊的存储区,他们里面存放的是常量,不允许修改(当然,你要通过非正当手段也可以修改,而且方法很多)


memory-layout