linux学习.docx
- 文档编号:7882034
- 上传时间:2023-01-26
- 格式:DOCX
- 页数:16
- 大小:29.69KB
linux学习.docx
《linux学习.docx》由会员分享,可在线阅读,更多相关《linux学习.docx(16页珍藏版)》请在冰豆网上搜索。
linux学习
Linux内核体系结构
一个完整的可用操作系统包括:
硬件、操作系统、操作系统服务和用户应用程序。
Linux内核主要用途就是为了与计算机进行交互。
Linux内核源文件目录包括:
makefile文件、
2.1Linux内核模式
操作系统内核的结构模式主要分成整体式的单内核模式和层次式的微内核模式。
Linux0.11内核采用的是单内核模式。
在单内核模式的系统中,操作系统提供的服务流程:
应用程序使用指定的参数执行系统调用命令,是cpu从用户态切换到和心态,然后os根据具体的参数值调用特定的系统调用服务程序,而这些服务程序则根据需要调用在底层的一些支持函数用于完成特定功能。
完成任务后从和心态切换回用户态。
2.2Linux内核系统体系结构
Linux内核主要由5个模块构成:
进程调度模块、内存管理模块、文件系统模块、进程间通信模块和网络接口模块
2.3Linux内核文件分析:
boot:
里边含有汇编语言,功能是当计算机加电后引导内核启动,并将内核代码加载到内存中。
fs:
文件系统,采用多进程机制。
所有对文件系统数据的访问都需要先将数据读取到告诉缓冲区中。
nginx文档系列
目标:
配置nginx以及与fastcgi链接
一、配置nginx
1、nginx有一个主进程,多个工作进程。
主进程用于读取和评估配置并保持工作进程。
工作进程则做请求的实际处理。
nginx采用基本的事件模型和操作系统的依赖性机制来有效的分配工作进程之间的请求。
2、nginx是由模块组成的,这些模块在配置文件中有指定的指令。
指令分成简单指令和块指令。
简单指令包括名称和用空格分隔的参数以及用于结尾的英文分号(;);块指令和简单指令结构相同,但是它使用大括号来包围一系列说明来替代分号。
3、如果块指令在大括号中有其他指令,则称之为上下文(比如events,http,server和location)。
放在配置文件最外边的指令称之为主文。
“#”开头是注释
4、nginx和它的模块工作方式是在配置文件中定义的。
默认配置文件名称为nginx.conf。
放置在/usr/local/nginx/conf,/etc/nginx或者/usr/local/etc/nginx目录。
5、提供静态内容:
打开配置文件,可以将默认的几个包含是server块的例子注释掉。
然后开始一个新的server块。
各配置文件中的server块之间以监听的端口号和servername来区分。
例如:
/data/www文件夹下存放html模板文件;/data/images文件夹存放图片文件。
则配置文件的server块应该如下:
server{
location/{
root/data/www;
}
location/images/{
root/data;
}
}
加入nginx要相应http:
//localhost/images/aa.png的路由请求,nginx会发送/data/images/aa.png文件,如果没有该文件则发送404错误相应。
还有,如果响应http:
//localhost/some/aa.html请求,nginx会发送/data/www/some/aa.html文件
6、nginx的错误日志一般在/usr/local/nginx/logs或者/var/log/nginx目录中的access.log和error.log文件。
二、作为代理服务器:
1、nginx最常用的功能之一就是作为代理服务器,代理服务器是客户端和原始服务器之间的一个中间服务器。
为了从原始服务器获取内容,客户端发送请求给代理服务器,代理服务器再从原始服务器获取内容返回给客户端。
客户端必须专门配置转发代理来访问其他网站,如在浏览器中配置代理服务器地址和指定端口等。
2、反向代理:
客户端请求nginx服务器,然后nginx服务器去请求其他网站的数据(或者本机数据)后返回给客户端。
客户端无需设置任何配置,就好像客户端请求的nginx服务器就是原始服务器一样。
3、代理服务器通常分为:
转发代理(forwardproxy)服务器和反向代理服务器(reverseproxy)。
转发代理服务器通常又叫做代理服务器,我们平时所说的代理服务器就是转发代理服务器。
4、转发代理服务器的一个典型应用就是为处于防火墙后的内部客户端提供访问外部internet;反向代理服务器的一个典型应用就是为处于防火墙后的服务器提供internet用户的访问。
转发代理服务器可以使用缓存缓解原始服务器的负载,提高相应速度;反向代理服务器可以为多个后端服务器提供负载均衡,或者为较慢的后端服务器提供缓存,还可以简单的将多个服务器映射到一个url空间。
5、一个简单的代理服务器配置例子:
QQ登录使用代理
在能上网的电脑上,配置nginx如下:
server{
listen8080;
resolver8.0.8.0;
location/{
proxy_pass:
//$_host$request_uri;
#allow127.0.0.1;
#denyall;
}
}
注意:
必须加上resolver,即必须给nginx指定dns;在qq登录的时候设置为代理服务器,端口8080即可
三、设置FastCGI代理
nginx可用于路由请求FastCGI服务器,FastCGI服务器运行各种不同的框架和编程语言。
nginx指令中,fastcgi_pass指令替代proxy_pass指令;并设置fastcgi_param参数传递给FastCGI服务器;SCRIPT_FILENAME参数用来确定脚本名称,QUERY_STRING参数用于传递请求参数。
以下就是一个简单的代码块。
server{
location/{
fastcgi_passlocalhost:
8089;
fastcgi_paramSCRIPT_FILENAME$doucment_root;
fastcgi_paramQUERY_STRING$query_string;
}
location~\.(gif|jpg|png)${
root/data/images;
}
}
用该设置的服务器会通过所有的请求,除了静态图像的请求到代理服务器,其他会通过FastCGI协议运行在localhost:
8089上。
PHP核心------zendengine
PHP:
一种解释语言
PHP经常会被定义成脚本语言或者解释型语言。
所谓的解释型语言就是使用这种语言写的程序不会被直接编译成机器语言(nativemachinelanguage),而是会被编译成一种中间代码;这种中间代码是不能直接在cup上执行的(cpu只执行机器语言),但是这种中间代码可以在使用机器语言(大多时候是c语言)编写的软件上运行。
使用机器语言编写的软件叫软件虚拟机(又叫进程虚拟机),它可以提供一个抽象的平台独立的程序来执行某种程序。
这种软件虚拟机是一个单独的进程,启动后vm会被创建;退出后vm虚拟机被销毁。
它的目的是提供一个平台独立的程序执行环境,它可以对底层硬件或者os进行抽象处理,从而保证应用程序在任何平台都是一致的行为执行。
基于软件虚拟机的语言比如:
java、python、c#、ruby、javascript等。
有的软件虚拟机可以把语言的中间代码部分转换成机器命令,这个过程称为“JIT编译”—Just-In-Time编译。
使用软件虚拟机便于开发,使用简单;基本都支持自动内存管理;无需进行底层数学运算(内存地址运算,防止内存溢出等);可以跨平台开发。
不使用软件虚拟机的语言,比如c语言,要根据不同平台运行不用的库函数。
但是可以精确的管理内存,大大减少cup周期(JIT就是为了此目的)。
Zend软件虚拟机:
PHP使用的虚拟机(目前为止主要有zend虚拟机,此为官方;还有hhvm)可以分成两大部分:
编译栈和执行栈
编译栈:
主要是识别php语言指令,把他们转换成中间形式。
在编译脚本中的PHP语法时会将PHP语法转换成多个OPCODE,一个接着一个组成一个包含OPCODE的数组
执行栈:
将中间形式的代码取到引擎执行,引擎一般用c或者汇编编写而成。
它具有高度优化、跨平台、运行时hookable等优点,每次发布新版本PHP都会对它做部分重写。
OPCODE:
OPCODE是一组指令集,这种指令集一般比对应的硬件指令集的数据类型和操作更高级,尽管他们每个指令执行结果都差不多。
我们可以假设ZendVM的一个OPCODE对应虚拟机的一个底层操作,那么ZendVM会有很多的OPCODE;随着php的发展,也引入了越来越多的OPCODE,OPCODE可以从PHP的源文件Zend/zend_vm_opcodes.h中看到。
PHP5.6中大约有167个OPCODE,虚拟机的执行栈可以执行167中不同操作
通常,OPCODE的名称是自描述性的,比如:
1、ZEND_ADD:
执行两个操作数的算术加法
2、ZEND_NEW:
创建一个PHP对象
3、ZEND_EXIT:
退出php
4、ZEND_FETCH_DIM_W:
给某个数组元素赋值
PHP内部使用zend_op这个结构体来表示OPCODE:
struct_zend_op{
opcode_handler_thandler;//一个函数指针,指向OPCODE对应的处理函数地址
znode_opop1;//操作数1
znode_opop2;//操作数2
ulongextended_value;//存放返回的信息,或者存放溢出的部分
uintlineno;
zend_ucharpcode;
zend_ucharop1_type;
zend_ucharop2_type;
zend_ucharresult_type;
}
可以通过设想一个简单的计算器来理解OPCODE:
这个计算器接受两个操作数,op1和op2;你请求他执行一个操作(handler),然后他返回一个结果(result),如果运算出现溢出则吧溢出的部分去掉舍弃(extended_value)。
OPCODE就是这些东西,不需要添加任何东西。
ZendVM的每个OPCODE工作方式都是完全一样的:
他们都有一个handler(这是一个函数指针,指向opcode对应的处理函数地址,这个函数就是一个c函数,比如add,执行一个基本的加法运算)。
每个handler最后都会执行两个c指令:
CHECK_EXCEPTION()和ZEND_VM_NEXT_OPCODE()。
第二个指令用于告诉执行器执行下一个OPCODE。
可以把整个过程简化成:
“执行操作1,执行操作2,执行操作,…,返回退出(returnandexit)”;最后一个OPOPCODE不会执行ZEND_VM_NEXT_OPCODE()这个指令,而是执行ZEND_VM_RETURN()返回1,表示执行完毕。
OPArray:
ZendVM的编译器将一个个的OPCODE放到OPArray中。
但是一个OPArray不仅仅包含一个个的OPCODE结构体,还包括一些统计信息,以及所有有利于OPCODE以最高效的方式执行所需要的信息:
执行器必须尽可能高效的执行OPCODE,只有这样PHP脚本才能花费尽可能少的时间。
总结:
zend_vm_def.h并非是一个有效的c文件,它描述了了每个OPCode的handler的特点(使用的是类c语言的自定义语法)
zend_vm_def.h在编译php源码时不会被处理
zend_vm_execute.h是zend_vm_gen.php解析zend_vm_def.h后输出的文件,它包含了符合c语法的代码,它是VMexecuter的心脏:
每个OPCode的专有handler函数都放在这个文件夹里
当从源码编译php时,php源码会提供一个默认的zend_vm_execute.h文件。
PHP虚拟机的executor是通过PHP语言自身生成的。
编译器优化
zend_vm_gen.php会在zend_vm_execute.h中生成一些中间代码。
最终c编译器会优化掉其中的中间代码。
因此可以向zend_vm_gen.php这个脚本传递一些参数来优化生成的代码。
比如:
1、执行zend_vm_gen.h的时候加入--without-specializer。
此时生成的执行器会比使用专有handler小十分之一,但是执行效率也会慢10%-15%。
2、另一个参数:
--with-vm-kind=CALL|SWITCH|GOTO。
CALL(函数调用)是默认值。
这个参数让zend_vm_gen.h生成不同流程控制的代码。
默认的参数CALL会让c编译器可以在大多数目标平台下编译出性能良好的本地机器代码。
也可以根据自己的平台以及c编译器的特性来选择使用CALL或者GOTO等参数。
executor中的跳转(jumps)
对于顺序执行(或者线性执行)语句生成的OPCode,它的handler函数会使用ZEND_VM_NEXT_OPCODE()宏。
该宏不能实现if或者循环的跳转。
实现if或者循环的跳转有一个特定的OPCode---ZEND_VM_SET_OPCODE()宏。
这个宏会告诉executor的主循环不要增加opline指针(这么做只会执行下一个OPCode),而是将opline跳转到一个新地址,这个地址保存了新的操作数。
新的操作数会继续执行同一个OPCode,知道跳转结束。
总结:
如果使用越多的php语言的动态特性,executor就需要做越多的工作来执行代码,代码的整体性能也就越差!
php语言的动态特性可以用OPCodecache来缓解。
结束
zend虚拟机的核心部分:
executor。
它是c语言或者编译语言写成的。
它将编译器把php代码生成的中间代码OPCodes真正执行。
它是php执行性能最关键的一步。
为什么程序员写的php代码会经编译器优化变成OPCode,再由executor优化执行OPCode成汇编代码?
性能。
c语言是唯一可以实现底层细节层面优化的语言,c语言可以直接被编译成目标机器的汇编指令,大多数的c编译器已经非常成熟了,很多都有40多年的历史。
php的虚拟机也经历了20多年的修改,重构和优化,手册上如果建议你怎么做,最好按照建议来,这样会极大提高php虚拟机的效率,生成更优化的代码。
为了优化某些底层部分的性能,我们甚至会阅读不同编译器编译executor所生成的汇编代码,从中寻找可以优化的地方,从而让编译器生成更好更优化的代码。
zend虚拟机一些关键部分甚至直接用汇编语言写成的。
深入理解PHPOPCode缓存原理
在上一篇深入理解zend引擎中提过OPCodecache(OPCode缓存)。
opcode缓存本质上是一个zend扩展。
OPCode缓存?
当解析器完成对脚本代码的分析后便将他们生成可以直接运行的中间代码,也成为操作码(OperateCode,opcode)。
opcode缓存的目的是避免重复编译,减少CPU和内存的开销。
如果动态内容的性能瓶颈不在CPU和内存,而在于I/O操作,比如数据库查询带来的磁盘开销,那么opcodecache的性能提升就非常有限了。
现在操作码缓冲器使用共享的内存进行存储,并且可以直接从中执行文件,而不用在执行前“反序列化”代码。
这将带来显著的性能提升,降低了整体服务器内存消耗。
为什么使用OPCode缓存
先看一个完整的web服务:
request——》php文件——》lexiconscan——》parse——》createopcode——》process
opcode——》response——》传回php——》传回client
zend引擎必须从文件系统读取php文件,然后扫描其词典、表达式、解析文件,然后创建opcode。
每一次请求php都会执行一遍该过程,如果php源码没有变化,那opcode也就不会变。
因此没有必要每次请求php都重新生成一遍opcode,结合web中无所不在的缓存机制,我们可以把opcode缓存下来。
常用的phpopcode缓存插件有:
opcache(php5.5集成了opcache),xcache、APC、eAccelerator等。
phpopcode原理
opcode是php脚本编译后的中间语言,就像Java的bytecode或者.net的msl。
看一个php代码:
php
echo‘helloworld’;
$a=1+1;
echo$a;
?
>
php的zend引擎执行该段代码应该分成四步:
1、scanning(lexing),将php脚本代码转换成语言片段(token)
2、parsing,将tokens转换成简单而有意义的表达式
3、compilation,将表达式编译成opcodes
4、executor,执行器执行opcodes,实现php脚本的功能
现有的cache比如APC可以使得php缓存opcodes,每次请求都可以不用重复前三步,从而能大幅提高php执行的效率。
lexing?
lexing就是编译原理中的词法分析的字典或者依据表。
zend/zend_language_scaner.c会根据zend/zend_language_scanner.l(lex文件),来输入php代码进行词法分析,得到一个个的‘词’,从php4.2开始提供一个函数token_get_all,这个函数能将一段php代码scanning成tokens。
简单的理解token成一个大数组,数组里边按照顺序装着关键字,变量常量,甚至空格都会原样装好。
但是php的操作符,语句标签等都会被转换成一个包含两部分的array:
token_id(也就是zend内部的对应码,比如echo对应码是316)和源码中原来的内容。
parsing?
parsing就是语法解析,将一个个的词儿变成一个个的表达式。
zend依然会调用相应的语法解析文件,生成如下的表达式:
1、echoaconstantstring
2、addtwonumberstogether
3、storetheresultofpriorexpressiontoavariable
4、echoavariable
compilation?
语法解析完成后,将tokens编译成一个个opcode,也就是一个个op_array,每个op_array都包含5个部分:
1、opcode的标识符,比如add,echo等
2、结果存放opcode的结果
3、操作数1给opcode的操作数
4、操作数2
5、扩展值一个整形,用于区别被重载的操作符
compilation即解析阶段,该阶段会为每个变量分配内存,如果调用函数则找到函数地址等等信息,然后放入opcode中等待executor执行。
如果使用的动态特性比较多,则会将该阶段的部分功能拿到执行器runtime执行;不过这样就大大降低了php执行效率。
PHP内核
1、生命周期和zend引擎
1、SAPI(ServerApplicationProgrammingInterface)是php具体应用的编程接口。
Php脚本要执行方式:
最常见的就是通过web服务器(Apache、Nginx),或者在命令行下,或者可以嵌入其他程序中。
2、不管那种执行方式,他们的工作流程都是一样的,都是以SAPI接口实现开始的。
只是不同的SAPI接口实现完成不同的实现方式。
例如Apache的mod_phpSAPI实现需要初始化Apache获取一些信息,在输出内容给apache。
。
。
其他SAPI实现也类似。
3、Php开始执行后会经过两个主要阶段:
处理请求之前的开始阶段和请求之后的结束阶段。
开始阶段:
1、模块初始化阶段MINIT:
Apache启动后整个生命周期内或者命令行程序整个执行过程,该过程只进行一次。
将php注册的所有扩展模块函数回调,即将回调函数进行初始化工作,开启模块。
2、模块激活阶段RINIT:
该过程发生在请求阶段。
例如通过url请求某个页面,则在每次请求之前都会进行模块激活。
请求到达后php初始化执行脚本的基本环境:
创建一个执行环境,包括保存php运行过程中变量名和值内容的符号表,以及当前所有函数以及类。
同时激活所有模块的函数
4、请求完成后就是结束阶段:
将激活的模块停用,然后web服务器退出或者命令行脚本执行完毕时关闭模块。
5、单进程SAPI生命周期:
CLI/CGI模式的php属于单进程的SAPI模式。
这类请求在处理一次请求后就关闭:
开始--请求开始--请求关闭--结束。
6、多进程(多线程)SAPI生命周期:
Apache一般会采用多进程模式,Apache启动后会fork处多个子进程,每个子进程都有独立内存空间,每个子进程都会经过开始和结束环节。
在子进程的整个生命周期中可能会处理多个请求。
7、zend引擎是php实现的核心。
为php提供基础设施:
php语法实现,脚本的编译运行环境、扩展机制以及内存管理等。
很多php扩展都是使用的zendAPI。
PHP优化
关于配置环境的优化:
1、pool
这里的池不是指数据库连接池之类的,而是指进程池。
PHP允许同时启动多个池,每个池使用不同的配置,各个池是完全独立的。
默认PHP只启动一个池
如果启用多个池,可以把请求分门别类的放到不同的池中,如果某一个请求出现拥堵,那么也只会影响该请求所在的池,从而控制了故障的波及范围。
2、listen
虽然nginx和PHP可以部署在不同的服务器,实际上多数人都会把它们放在一个服务器上。
如此就有两个选择:
一个是TCP,另一个是UnixSocket。
和TCP相比,UnixSocket省略了诸如三次握手之类的环节,所以相对更高效。
但是再使用UnixSocket时,因为没有TCP对应的可靠性保障机制,最好把backlog和somaxconn设置大些,保证高并发时的稳定。
因此,安装php-fpm时backlog一定要将默认-1重置,改成1024以上,最好是2的n次幂。
3、pm
nginx使用php-fpm的时候有两种进程管理方式:
static(静态)和dynamic(动态)。
动态模式一般先启动少量进程,然后按照实际的请求数来调整进程数,这样可以节省资源;但是一旦出现高并发的请求,系统将不得不忙着fork新进程。
相对的,静态模式根据pm.max_children这个参数生成指定的进程数,之后不会随着请求数的增加而增加;坏处是消耗了跟多的资源,但是面对高并发,不需要执行高昂的fork。
面对大流量的网站,除非服务器资源紧张,否则静态模式无疑是最佳选择。
4、pm.max_children
该参数只有在将php-fpm设置成static时才会起作用。
但是这个值应该是多大呢?
一般的,一个CPU在某一时刻只能处理一个请求,当请求数大于CPU个数时,CPU会划分时间片轮流执行。
因此从这个意义上讲,进程数应该等于CPU个数,这样就没有进程的上下文切换,损失也是最小的。
这个结论只适用于请求CPU密集型的情形,而对于一般的web请求,多半是I/O密集型。
因为当查询数据时CPU一般会有wait状态,因此将
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- linux 学习