php-fpm 配置指南

简介

FPM(FastCGI 进程管理器)用于替换 PHP FastCGI 的大部分附加功能,管理PHP进程池。用于接受处理来自WEB服务器(nginx)的请求.

PHP-FPM会创建一个master进程用于管理work进程(fork或killwork进程)和转发请求。work进程会竞争的接受请求,接收成功后,解析FastCGI协议执行脚本,处理完成,等待下一个请求(同步阻塞IO)。

PHP-FPM 对于高并发的处理能力不强。高并发业务推荐使用swoole 或 workman相关生态。

配置项说明 官方文档

全局常用配置

#PID 文件的位置
pid = run/php-fpm.pid
#错误日志目录
error_log = log/php-fpm.log
#日志等级
log_level = warning
#用于设定平滑重启的间隔时间。这么做有助于解决加速器中共享内存的使用问题,可用单位:s(秒),m(分),h(小时)或者 d(天)
emergency_restart_interval = 60s
#如果子进程在 emergency_restart_interval 设定的时间内收到该参数设定进程数量的 SIGSEGV 或者 SIGBUS退出信息号,则FPM会重新启动;
emergency_restart_threshold = 30
#子进程接受主进程复用信号的超时时间
process_control_timeout = 5s
#设置 FPM 在后台运行。设置“no”将 FPM 保持在前台运行用于调试
daemonize = yes

emergency_restart 配置解释,在60s内收到30个子进程的SIGSEGV或SIGBUS退出信号,则重启fpm

process_control_timeout场景,例如当父进程发送终止信号时,子进程正在处理某些事情的时候。十秒的时间,他们会有一个更好的机会完成任务并且优雅地退出

进程池相关配置

进程池监听配置

#进程池名称
[yangliuan]
#设置进程池接受 FastCGI 请求的地址,可用格式为:'ip:port','port','/path/to/unix/socket',每个进程池都要设置
listen = /dev/shm/php-cgi.sock
listen.backlog = -1
listen.allowed_clients = 127.0.0.1
listen.owner = yangliuan
listen.group = yangliuan
listen.mode = 0666
user = yangliuan
group = yangliuan

TCP Socket 链接方式127.0.0.1:9000 ,可以跨机器通讯 ,使用 Nginx 做负载均衡做 PHP 服务器集群,应用服务器只需要安装php就可以了

Unix Socket 链接方式 /dev/shm/php-cgi.sock,只能本机通讯 ,Nginx 和 FPM 必须都在同一台服务器上,速度比较快而且消耗资源少(差距不会太大 0.1% ~ 5% 的差别),这种情况做集群的话,应用服务器需要同时安装nginx和php

/dev/shm 是linux tmpfs文件系统,数据存储和读取直接使用内存,速度快

对应nginx监听方式

fastcgi_pass 127.0.0.1:9000;
fastcgi_pass unix:/dev/shm/php-cgi.sock;

进程池模式

fpm 的运行模式有三种:ondemand 按需创建dynamic 动态创建static 固定数量

ondemand

ondemand 初始化时不会创建待命的进程。并且会在空闲时将进程销毁,请求进来时再开启。是一种比较 节省内存 的 FPM 运行方式,不过因为其频繁创建和销毁进程,性能表现不佳。

应用场景:一般是在共享的 VPS 上使用。内存小的机器上。

相关参数

#秒数,多久之后结束空闲进程;默认是 10 秒,超过 10 秒即销毁
pm.process_idle_timeout = 10s
#最大并存进程数,超过此值将不再创建
pm.max_children = 50
#每个进程最多处理多少个请求,超过此值将自动销毁
pm.max_requests = 1000

dynamic

动态创建,这个是默认选项,也是比较灵活的选项。兼顾稳定和快速响应。因为一直保证有「空闲进程」可供使用,所以 dynamic 的配置,相比 ondemand 进程要同时创建,响应速度还是比较快的。然而在还是避免不了频繁创建和销毁进程对系统造成的消耗

应用场景:一台服务器上跑多个php项目,平常访问量不多。

相关配置:

#FPM 启动时创建的进程数
pm.start_servers = 10
#最大并存进程数,超过此值将不再创建
pm.max_children = 50
#空闲进程数最小值,如果空闲进程小于此值,则创建新的子进程
pm.min_spare_servers = 10
#空闲进程数最大值,如果空闲进程大于此值,则进行清理
pm.max_spare_servers = 40
#每个进程最多处理多少个请求,超过此值将自动销毁
pm.max_requests = 1000

dynamic模式 进程数量公式

处理请求中的进程数量 + pm.max_spare_servers =  pm.max_children

超过最大进程数后,新来的请求将会阻塞等待,php-fpm日志会出现 server reached pm.max_children setting (n), consider raising it 警告日志。n为配置的pm.max_children最大进程数值

static

固定进程数量是性能最好,省去了创建和销毁进程的开销,资源利用率最高的运行方式,一般在要求单机性能最高的时候使用

应用场景:PHP 服务器集群,希望每台机器都能物尽其用;本地lnmp开发环境,保证占用内存固定

相关配置:

#FPM 启动时创建的进程数,并且会一直保持这个数
pm.max_children = 50
#每个进程最多处理多少个请求,超过此值将自动销毁
pm.max_requests = 1000

该模式需要计算评估一般场景下,每个进程占用多少内存,以及每台机器的总内存。然后根据情况配置。

使用ab压力测试工具或jmeter工具,对访问频率较高的页面和请求进行的正常流量情况下的压力测试。

然后使用如下命令进行统计,查看内存使用情况,单个php进程占用进程数

ps -eo size,pid,user,command --sort -size | awk '{ hr=$1/1024 ; printf("%13.2f Mb ",hr) } { for ( x=4 ; x<=NF ; x++ ) { printf("%s ",$x) } print "" }' | grep php-fpm

要求不严谨的情况下,可以根据经验和项目的业务场景的代码对每个进程数进行粗略估算,例如一个常用接口查询请求下占用多少内存,然后在计算。

最大进程数量计算公式

pm.max_children = 可用内存 / 每个进程暂用内存大小

可用内存不是本机所有内存,要除去其他程序运行,例如nginx,mysql,等其它服务的内存。

调试期间,要在生产环境中实战观察,一般建议使用 80% 的内存使用率,留 20% 给内存泄露的空间和其他软件运行。

最后是 pm.max_requests 值,需要我们观察应用是否有 内存泄漏。现代的 PHP 程序,尤其是 Laravel ,会依赖于非常多的扩展包,这些扩展包代码质量参差不齐,多少会出现内存泄漏的问题。如果内存泄露不严重,那么把值设置高一点,让单个进程在存活期间多处理一些请求,如果内存泄露比较严重,应当酌情设置低一点。否则会出现系统内存不够用,然后去使用 Swap 物理内存 的窘境。

如果代码质量非常高,不会出现发生内存泄露的情况,可以将其设置为 pm.max_requests=0 可以避免过高的 PM 开销

请求的生命周期设置

#设置单个请求的超时中止时间。该选项可能会对 php.ini 设置中的 'max_execution_time' 因为某些特殊原因没有中止运行的脚本有用
request_terminate_timeout = 600

非分片上传大文件时,需要用到

请求慢日志设置

#当一个请求该设置的超时时间后,就会将对应的 PHP 调用堆栈信息完整写入到慢日志中,0关闭
request_slowlog_timeout = 60
#慢日志路径
slowlog = var/log/slow.log
#慢日志记录脚本堆栈的深度
request_slowlog_trace_depth = 20

文件打开限制

#设置文件打开描述符的 rlimit 限制
rlimit_files = 51200
#设置核心 rlimit 最大限制值。可用值:'unlimited',0 或者正整数
rlimit_core = 0

worker进程开启日志记录

catch_workers_output = yes

参考

为高性能优化 PHP-FPM

PHP-FPM 调优:使用 ‘pm static’ 来最大化你的服务器负载能力

LX3 Laravel 性能优化入门

supervisor安装使用

centos

安装

方式一

yum update

yum install -y supervisor

//yum安装会自动创建systemd脚本,可用systemd管理
systemctl enable supervisord //开机自启

systemctl disable supervisord //禁用开机自启

systemctl start supervisord//启动supervisor

systemctl reload supervisord //重新加载配置

systemctl stop supervisord //停止supervisor

方式二

yum update

yum install -y python-setuptools

easy_install supervisor

echo_supervisord_conf >/etc/supervisord.conf //创建配置文件

需要自己配置systemd脚本

ubuntu

sudo apt-get install supervisor

service supervisor status|start|stop|enable|disable

常用命令

supervisord -c /etc/supervisord.conf //指定配置文件启动

supervisorctl stop programxxx  //停止某一个进程

supervisorctl start programxxx //启动某一个进程

supervisorctl restart programxxx //重启某个进程

supervisorctl status  //查看进程状态

supervisorctl stop groupworker //重启所有属于名为 groupworker 这个分组的进程(start,restart 同理)

supervisorctl stop all //停止全部进程,注:start、restart、stop 都不会载入最新的配置文件

supervisorctl reload //载入最新配置文件,停止原有进程并按新的配置启动所有进程

supervisorctl update //根据最新的配置文件,启动新配置或有改动的进程,配置没有改动的进程不会受影响而重启。

配置文件示例 (laravel项目为例)

#进程组名称
[program:laravel-worker-queue]
#进程名称
process_name=%(program_name)s_%(process_num)02d
#程序执行命令
command=/php-path/bin/php /www/www.youdomain.com/current/artisan queue:work redis --sleep=3 --tries=3 
#supervisor启动后自动启动
autostart=true 
#退出后自动重启
autorestart=true
#程序运行用户
user=www-data 
#supervisor启动进程数量
numprocs=5
#如果为true,则将进程的 stderr 输出发送回其 stdout 文件描述符上的 supervisord(在 UNIX shell 术语中,这相当于执行 /the/program 2>&1)
redirect_stderr=true
#日志大小
stdout_logfile_maxbytes=10MB
#日志数量
stdout_logfile_backups=20
#日志目录
stdout_logfile=/www/www.youdomain.com/current/storage/logs/worker.log

REDIS配置详解





Redis必须是以文件路径作为第一个参数开始
redis-server /path/to/redis.conf

需要内存大小时,可以指定通常以1k 5GB 4M的形式出现,以此类推:
1k => 1000字节
1kb => 1024字节
1m => 1000000字节
1mb => 1024 * 1024字节
1g => 1000000000字节
1gb => 1024 * 1024 * 1024字节
单位不区分大小写,因此1GB 1Gb 1gB都是相同的

==INCLUDES==========================================================
在此处包含一个或多个其他配置文件。如果你这很有用有一个标准模板,可以转到所有Redis服务器,但也需要自定义一些每服务器设置。包含文件可以包括the other files,所以明智地使用它。
通知选项“include”不会被命令“CONFIG REWRITE”重写
来自admin或Redis Sentinel。由于Redis总是使用最后一次处理line作为配置指令的值,你最好把include包括在内在此文件的开头,以避免在运行时覆盖配置更改。
include /path/to/local.conf
include /path/to/other.conf

==MODULES===========================================================启动时加载模块。
如果服务器无法加载模块它会中止。可以使用多个loadmodule指令。
loadmodule /path/to/my_module.so
loadmodule /path/to/other_module.so

==NETWORK==========================================================
如果未指定“bind”配置指令,则Redis将监听来自服务器上所有可用ip的连接。可以使用只听一个或多个选定的ip
bind 192.168.1.100 10.0.0.1 
bind 127.0.0.1 ::1
bind 0.0.0.0 监听所有
port 6379 监听端口

 
默认情况下启用保护模式。
只有当您确定希望其他主机的客户端连接到Redis 时,即使未配置任何身份验证,也不应使用“bind”指令明确列出特定的接口您应该禁用它.
开启远程访问需要将此选择项改为no
protected-mode yes

此参数确定了TCP连接中已完成队列(完成三次握手之后)的长度, 当然此值必须不大于Linux系统定义的/proc/sys/net/core/somaxconn值,默认是511,而Linux的默认参数值是128。当系统并发量大并且客户端速度缓慢的时候,可以将这二个参数一起参考设定。
tcp-backlog 511

Unix socket.
指定以redis unix socket方式运行的
unixsocket /tmp/redis.sock //路径
unixsocketperm 700 //权限建议 777


客户端空闲超时时间,0表示禁用
timeout 0


tcp keepalive参数。如果设置不为0,就使用配置tcp的SO_KEEPALIVE值,使用keepalive有两个好处:检测挂掉的对端。降低中间设备出问题而导致网络看似连接却已经与对端端口的问题。在Linux内核中,设置了keepalive,redis会定时给对端发送ack。检测到对端关闭需要两倍的设置值。
tcp-keepalive 300


daemonize是用来指定redis是否要用守护线程的方式启动。 
daemonize 设置yes或者no区别
daemonize:yes:redis采用的是单进程多线程的模式。当redis.conf中选项daemonize设置成yes时,代表开启守护进程模式。在该模式下,redis会在后台运行,并将进程pid号写入至redis.conf选项pidfile设置的文件中,此时redis将一直运行,除非手动kill该进程。
daemonize:no: 当daemonize选项设置成no时,当前界面将进入redis的命令行界面,exit强制退出或者关闭连接工具(putty,xshell等)都会导致redis进程退出
daemonize yes


是否通过upstart或systemd管理守护进程。默认no没有服务监控,可选项有no ,upstart, systemd, auto
supervised no

pid文件在redis启动时创建,退出时删除。最佳实践为配置该项。
pidfile /var/run/redis_6379.pid

#配置日志级别。选项有debug, verbose, notice, warning
loglevel notice

日志名称。空字符串表示标准输出。注意如果redis配置为后台进程,标准输出中信息会发送到/dev/null
logfile /usr/local/redis/var/redis.log

是否启动系统日志记录。
syslog-enabled no

指定系统日志身份。
syslog-ident redis

指定syslog设备。必须是user或LOCAL0 ~ LOCAL7之一。
syslog-facility local0


设置数据库个数。默认数据库是 DB 0
可以通过SELECT where dbid is a number between 0 and 'databases'-1为每个连接使用不同的数据库。
databases 16

进程的基础知识

基本概念

程序是存储在硬盘上的编译生成的二级制可执行文件.不占用系统资源,是具体的.

进程是一个二进制程序(在内存中)的执行过程(运行实例).占用系统资源,是抽象的.

启动程序时,程序的文件会被加载到内存中,产生进程,结合系统分配的资源完成运行,程序关闭或退出时,进程会结束.

POSIX标准(可移植操作系统接口 Portable Operating System Interface),为了统一 类UNIX操作系统编程接口,方便跨平台编程和程序的可移植。参考:posix是什么都不知道,就别说你懂Linux了!

进程处理机制

1个单核CPU(或CPU的一个核心)在一个时间点只能处理一个进程.

我们用电脑同时运行多个程序,是因为操作系统的”多道程序设计”技术,内核控制CPU在多道进程间切换,它将CPU的整个生命周期划分为多个长度相同的时间片,在每个时间片内只处理一个进程。因为时间片很小,我们会感觉这些软件同时都在运行。这种分时间片实现的多任务系统,我们把它叫分时系统

CPU划分的时间片是微小的(比如纳秒),以及CPU的运算速度非常快,所以使用时感觉是同时运行多个程序.多核CPU在同时,多进程运行方面比单核CPU有优势.

假如内存中只有3个进程A,B,C,CPU时间片分配情况

多进程切换时。把当前任务状态先保存起来,把另一个任务的状态恢复,并把执行权交给它即可。俗称上下文切换。任务的状态就是一堆寄存器的值。要切换进程,只需要保存和恢复一堆寄存器的值即可。

进程的属性

OS内核能够区分进程并可获取进程属性,进程属性保存在名为进程控制块(Process Control Block)的中结构体中,内核为每个进程维护一个进程控制块,用于管理进程属性.

标识符

(1)进程标识符(Process Identifier)PID,32位非负无符号整形数据,进程的唯一标识,用来标识不同进程.

(2)父进程标识符(Parent Process Identifier)PPID,创建子进程的父进程对应的PID,在linux系统中,除init进程(编号为1)外,其余进程都有父进程. 吗

(3)用户标识符(User Identifier) UID ,标识创建这个进程的用户。PCB结构体中有euid概念(Effective User Identifier) ,即有效用户标识符,标识以有效权限发起进程的用户。例:用户yangliuan 以root权限发起进程,那么进程的uid对应的用户为yangliuan,进程的euid对应用户为root

(4)组标识符(Group Identifier)GID,标识创建进程的用户所属组。euid对应的组标识符为egid(Effective Group Identifier)

<?php
echo '进程标识[PID]:', posix_getpid(), PHP_EOL;
echo '父进程标识符[PPID]:', posix_getppid(), PHP_EOL;
echo '用户标识符[UID]:', posix_getuid(), PHP_EOL;
echo '有效用户标识符[EUID]:', posix_geteuid(), PHP_EOL;
echo '组标识符[GID]:', posix_getgid(), PHP_EOL;
echo '有效组标识符[EGID]:', posix_getegid(), PHP_EOL;

进程的状态

(1)就绪态

进程所需资源已经分配到位,只等待CPU,当可以使用CPU时,进程会立即变为运行态,内核会维护一个运行对列,用来装载所有就绪态的进程,当CPU空闲时,内核会从队列中选择一个进程,为其分配CPU

(2)运行态

进程处于此状态时会占用CPU,处于此状态的进程数量必定小于等于处理器数量,因为每个CPU在一个时间点只能运行一个进程

(3)睡眠态

此状态的进程不能占用CPU

不可中断睡眠态,是由外部I/O调用造成,等待外部I/O硬件设备响应,此状态不可中断,即我们常说的阻塞。举例进程向硬盘读写数据时,为了保证数据的一致性,在得到磁盘回复前,它是不能被其他进程或者中断打断的,这个时候的进程就处于不可中断状态

可终端睡眠态,进程对应的当前用户请求已处理完毕,暂时退出退出CPU,当用户再次发出请求,会立即被唤醒,这种状态被称为挂起,程序中常用的方法是sleep() (php为例) ,类比可以理解为汽车已点火,但是没有往前开。

(4)终止态

进程已运行完毕,此时进程不会被调度,也不再占用CPU

进程状态转换示意图

寄存器信息

寄存器的数量是有限的,cpu在进行进程切换时,会保存当前进程的数据,以边下次切换回来的时候从中断处继续进行。该过程称为cpu的上下文切换。在服务端多进程编程模型中,进程数和cpu核数该如何匹配

页表指针

百度百科 页表机制

程序运行时,系统会为其开辟一段虚拟内存,虚拟内存和物理内存映射时,各个虚拟内存中的地址相同的数据会被MMU(Memory Managenment 内存管理单元) 映射的到内存中的不同物理地址,PCB会存储虚拟地址和内存地址的对应关系

linux采用分页存储方式管理内存,进程载入到内存之前,系统将用户进程的逻辑空间分成若干个大小相等的片(称 页面或页)并编号,为进程分配内存时,以块为单位将进程中的若干页装入多个可以不相邻的物理块中,linux使用页面表来存储逻辑地址和物理地址的对应关系,页表的实质是一个结构体,每个进程的PCB中都有一个进项页表的指针。

进程组与会话

同一个进程组(process group)的进程 ,进程组由用户启动的进程创建,用户启动进程是进程组的领导进程(process group leader) ,进程组中的领导进程pid是识别进程组id,即pgid

会话(session)是进程组的集合,会话中的每一个进程组称为一个工作job,

进程控制

linux启动时创建一个init进程,进程pid为1,是所有进程的父进程,负责启动getty进程,设置进程运行级别 回收孤儿进程。

linux系统对进程的控制主要包含:进程创建,进程任务转变,进程同步,退出进程

1.创建进程

多道程序环境(多任务处理操作系统,可以同时运行多个程序)中需要创建进程的情况通常有4种:用户登录,作业调度,用户请求,应用请求。

当一个程序执行时,可能需要申请一些资源,如打开某个文件、请求某项服务 ,根据cpu运行的机制此时进程会进入睡眠态并放弃占用cpu,若要申请的资源与之后操作并不冲突,为了保障当前进程的持续进行(走完当前时间片),此时可以内存中在创建一个进程,让新的进程代替原进程执行资源申请的工作。

linux使用fork函数创建进程,系统会创建一个与原进程近乎相同的进程,之后父子进程都继续往下执行。如图

fork函数创建子进程

2.创建多个进程

fork函数创建进程时,系统会复制原程序,因此在通过父进程循环创建子进程时,要判断是不是父进程,只有父进程才能fork。

数据共享机制

子进程可以访问到与父进程完全相同的代码信息、数据信息和堆栈信息,在调用fork()函数时,遵循“读时共享写时复制”原则。

fork()函数创建子进程后,子进程获得父进程的数据空间,堆栈,页表,等副本,此时父子进程中变量的虚拟地址相同,虚拟地址对应的物理地址也相同,父子进程共享物理内存的页面信息,为了防止一方修改导致另一方出现访问异常,系统将页面信息标记为制度,fork()函数执行完毕。

之后父子进程都继续向下执行:此时子进程拥有与父进程相同的页表,若进程只需要进行数据访问,则到对应的物理地址中便能获取到数据,因为父子进程相同虚拟空间对应相同的物理地址,其访问机制如图。

读取时共享

若子进程要对数据段,堆栈中的数据进行修改, 系统会将待操作数据复制到内存中一块新的区域,修改副本数据为可写。之后子进程修改数据副本,因此父子进程可以保存各自的数据,父子进程中相同的虚拟地址对应内存中不同的物理地址。访问机制如图

写时复制

注意事项,同样的虚拟地址对应不通的物理地址,因为虚拟地址适合进程关联的,每个进程都有一段0~4G的虚拟内存,因此多个进程中会有数据处于相同虚拟地址 ,但虚拟内存只是系统的内存管理的一种技术,目的是使进程认为自己有一段连续的地址空间,方便分配与数据管理,他不是“实际”的,进程中的数据实际存在于内存对应的物理地址

进程的执行顺序

在linux系统中,子进程应该由父进程回收,但是当在子进程被创建后,他与父进程及其他进程共同竞争系统资源,所以父子进程执行顺序是不确定 ,终止的先后顺序也是不确定。(在没有人为控制的情况下,比如在父进程使用wait sleep)。

孤儿进程

父进程应该负责子进程的回收工作,但父子进程是异步运行的,若父进程在子进程退出之前退出,子进程就会变成孤儿进程,此时子进程会被init进程收养,之后init会替代原来的父进程完成状态收集工作。

僵尸进程

当进程调用了exit()函数之后,该进程并不是马上消失,而是留下一个称为僵尸进程的数据结构,僵尸进程是linux系统中另一种特殊进程,它几乎放弃了进程退出之前占用的所有内存,即没有可执行代码,也不能被调度,只能在进程列表中保留一个位置,记载进程的退出状态等信息供父进程收集。若父进程中没有回收子进程的代码,子进程将会一直处于僵尸态。

守护进程(daemon process) 后台进程

在后台运行的一种特殊进程,通常在系统启动时启动,并在系统关闭时终止。它们通常不与用户交互,而是在后台执行某些任务,例如监视文件系统或网络连接,或者执行定期任务。它们通常以超级用户(root)权限运行,以便可以执行需要特权的任务。原理,fork一个子进程,父进程退出与前台终端的交互,设置子进程为会话领导者。可以通过信号来控制启动关闭。

daemon希腊神话半人半精灵守护神

进程同步

在多道程序环境中,进程是并行执行的,父进程与子进程可能没有交集,各自独立执行,子进程的执行结果是父进程的下一步操作的先决条件,此时父进程必须等待子进程执行。我们把异步环境下的一组并发进程因相互制约而互相发送消息、互相合作、互相等待、使各个进程按一定的速度和顺序执行称为进程间的同步。

sleep()函数来控制进程的执行顺序,但这种方法是一种权益之计,系统中进程的执行顺序是由内核决定的,这种方法很难做到对进程精确控制

linux系统中提供了wait()函数 waitpid()函数(php中PCNTL提供了这两个函数)来获取进程状态,实现进程同步。调用wait()函数的进程

信号

软中断信号,本质是软件层次上对中断机制的一种模拟,用于提醒进程,某事件已经发生。kill -l查看系统中的信号

linux 详细信号列表及Linux信号和信号集

进程间通信( inter (国米) process communication IPC)

管道(piepline)

内核管理的缓冲区,形象的理解管道两端连接着两个进程 ,一个读取一个写入。linux系统中将管道视为文件

匿名管道( pipeline (管道) ),有亲缘关系的进程才可以使用,父子进程,兄弟进程。以字节流形式传输,需要约定好数据格式,缓存区为空或写满时会阻塞。shell终端 | 管道符号就是匿名管道,ls | wc

命名管道FIFO(first in first out),与匿名管道的区别,命名管道与系统中一个路径名关联,以文件的形式存在于文件系统中,进程可以通过FIFO路径名访问FIFO文件,实现进程间数据传输。遵循先进先出原则,缓存区为空或写满时会阻塞。

使用demo,待完善

php 命名管道函数 posix_mkfifo()

注意要和管道设计范式区分

How fast are Linux pipes anyway?

SystemV IPC

每个内核的IPC结构(消息队列,信号量,共享内存)都用一个非负整数的标识符加以引用。

php的扩展 Semaphore函数 命名和c语言及系统函数命名基本一致。

消息队列(sysvmsg)

消息队列的实质是一个存放消息的链表,由内核维护。每个消息视为一条记录,消息包括一个长整形的类型字段和需要传递数据。由消息队列标识符标识,有读写权限的进程可以从队列读取消息,写入消息到队列。通过key来找到对应消息队列。

如何使用?注意事项。使用demo,待完善

信号量(semaphore)

多进程之间可能因为进程合作和资源共享而产生制约关系。

直接相互制约关系

两个进程通过管道通信,管道为空时,读进程无法从管道读取数据,进入阻塞;管道满时,写进程无法向管道写入数据,进入阻塞。类似这种需要进程间合作导致的制约关系称为直接相互制约。进程间有同步关系

间接相互制约关系

假设当前系统中只有一台打印机,当A进程占用打印机时,进程B也申请使用打印机。进程B就会进入阻塞,等待打印机释放。其它进程同理。类似这种因资源共享导致的制约关系称为间接相互制约关系。进程间有互斥关系

临界资源

同步和互斥存在的根源是系统中存在临界资源(硬件资源:内存,打印机,硬盘;软件:共享代码段,变量等)。为了避免多进程的并发执行造成的不一致性,临界资源在同一时刻只允许有限个进程对其进行访问或修改。

信号量,是专门用户解决进程间同步与互斥问题的一种通信机制,它与信号无关,也不同于管道,FIFO以及消息队列 ,一般不用于传输数据,包含一个变量(表示资源数量,类型为非负整型),修改信号量的原子操作P和V,该信号量下等待资源进程的队列。

使用步骤

1.创建信号量/集,或者获取系统中已有的信号量/集。

2.初始化信号量/集。

3.信号量的P,V操作根据请求修改信号量数量,P操作使信号量-1,V操作使信号量+1.

4.从系统中删除不需要的信号量。

如何使用?注意事项。使用demo,待完善

共享内存(Shared memory)

允许多个进程访问给定的同一块存储区域。一般情况下,每个进程的虚拟地址空间会与不同的物理地址进行映射(参考上文页表指针)。当使用共享内存进行通信时,系统会将同一段物理内存映射给不同的进程,映射关系示意图如下。

映射关系

系统中的物理内存和虚拟内存都通过页面(页表)来管理,为多个进程分配共享内存实际是为进程分配一个或多个物理页面。因此共享内存的大小必须是系统中页面大小的整数倍。

进程使用共享内存时,先将虚拟内存空间与共享内存进行映射,映射完成后,进程对虚拟地址的读写,就相当于直接对物理内存读写。通信完成后需要释放物理内存解除进程与共享内存的映射关系。

共享内存,因为是进程直接读写物理内存,了不同进程间多次读写的时间。共享内存本身不限制读写次序,但是开发人员应该自觉遵循读写规则,在写进程操作尚未完成时,不应该有进程从共享内存中读取数据。通常,共享内存和信号量一起使用,由信号量帮它实现读写操作的同步。

如何使用?注意事项。使用demo,待完善

参考

ubuntu 启动项管理工具

参考

启动软件图形化管理工具

单机show application 显示所有应用 搜索 startup application

可以通过 add remove edit 添加,删除,编辑来管理启动软件,该软件是ubuntu系统自带的

有些应用没有显示出来可以通过下列命令设置

sudo sed -i 's/NoDisplay=true/NoDisplay=false/g' /etc/xdg/autostart/*.desktop

执行之后多了好多软件

管理服务

Ubuntu或者Debian系统中update-rc.d命令,是用来更新系统启动项的脚本。这些脚本的链接位于/etc/rcN.d/目录,对应脚本位于/etc/init.d/目录。在了解update-rc.d命令之前,你需要知道的是有关Linux系统主要启动步骤,以及Ubuntu中运行级别的知识。

Linux系统主要启动步骤


读取 MBR 的信息,启动 Boot Manager。   
加载系统内核,启动 init 进程, init 进程是 Linux 的根进程,所有的系统进程都是它的子进程。   
init 进程读取 /etc/inittab 文件中的信息,并进入预设的运行级别。通常情况下/etc/rcS.d/ 目录下的启动脚本首先被执行,然后是/etc/rcN.d/ 目录。
根据 /etc/rcS.d/文件夹中对应的脚本启动 Xwindow 服务器 xorg,Xwindow 为 Linux 下的图形用户界面系统。
启动登录管理器,等待用户登录。

Ubuntu 中的运行级别

0 (关闭系统) 
1 (单用户模式,只允许root用户对系统进行维护。) 
2 到 5(多用户模式,其中3为字符界面,5为图形界面。) 
6 (重启系统)

切换运行级别

init [0123456Ss]
init 0 #命令关机; 
init 6 #命令重新启动

update-rc.d 的具体用法:

删除所有级别中的开机自启动 

update-rc.d -f <basename> remove 
-f: 强制删除所有符号链接 

basename就是要删除的程序的名称

删除boa的所有开机启动项:
sudo update-rc.d -f  boa remove

执行结果:

 Removing any system startup links for /etc/init.d/boa ...
   /etc/rc0.d/K20boa
   /etc/rc1.d/K20boa
   /etc/rc2.d/K20boa
   /etc/rc3.d/K20boa
   /etc/rc4.d/K20boa
   /etc/rc5.d/K20boa
   /etc/rc6.d/K20boa
添加boa开机启动项
sudo update-rc.d boa default

启动项终端图形管理界面

sudo apt-get install sysv-rc-conf

如果提示错误 Unable to locate package sysv-rc-conf,按如下方法处理

//1.编辑源文件
sudo gedit /etc/ap/sources.list
//2.将一下文本添加到文件中,如下图
deb http://archive.ubuntu.com/ubuntu/ trusty main universe restricted multiverse
//3.执行sudo apt update 更新后 重新安装

图形界面执行 sudo sysv-rc-conf

Mysql事务和隔离级别

事务

将一组SQL语句,封装到事务中(工作单元)执行,事务中的语句要么全部执行成功,要么全部执行失败!

Mysql中事务是由存储引擎层实现的,常用支持事务的存储引擎Innodb.

应用场景

对数据一致性要求非常高的业务场景,例如:经典转账场景,用户A转账100元给用户B, 如果执行失败,需要使A和B的账户余额恢复正常,否则会造成某一方账户余额错误

ACID原则

原子性 Atomicity

不可分割的最小独立工作单元,事务内的SQL被当做一个整体执行

一致性 Consistency

保证数据一致性.简单的讲就是事务内的SQL要么执行全部成功,要么全部失败,

隔离性 Isolation (隔离级别)

一个事务内的操作(执行的SQL)在最终提交以前,对其它事务是不可见的.

持久性 Durability

事务提交后,数据将永久存储

自动提交

Mysql默认的采用自动提交模式(AUTOCOMMIT),不显式开启事务(start transaction)时,每次执行的sql都会提交

查看事务是否自动开启 show variables status like ‘autocommit’



开启关闭 set autocommit = 0 (OFF) set autocommit = 1(ON)

隔离级别(事务的)

SQL标准提供了四种事务的隔离级别:

读未提交(read uncommitted)

事务中的(数据)变更,即使事务未提交,在其它事务中也可以读取.也称为脏读(dirty read)

应用场景:

读提交(read committed)

事务中的(数据)变更,在事务提交后,在其它事务中才可以被读取,也称为不可重复读,

执行同样的查询,两次可能会得到不同的结果

可重复读(repeatable read) MySQL默认隔离级别

事务在执行过程中读取的数据,总是跟事务启动时读取的到的数据保持一致,未提交变更对其它事务同样不可见.会出现幻读.事务A读取一行记录,此时事务B插入新的记录,事务A再次读取数据,先前读取的一行会出现重复.
Innodb通过MVCC解决了幻读.

串行化(serializable)

强制事务串行执行,写操作加写锁,读操作加读锁,出现写锁冲突是后一个事务必须等待前一个事务执行完成才能继续执行

对比

隔离级别脏读可能性不可重复读幻读可能性加锁读
READ UNCOMMITyesyesyes no
READ COMMITEDnoyesyesno
REPEATABLE READnonoyesno
SERIALIZABLEnononoyes

查看隔离级别指令

show variables like 'transaction_isolation';

配置的方式:

将mysql启动参数 transaction-isolation 的值设置成对应的隔离级别

隔离级别演示案例