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的虚拟内存,因此多个进程中会有数据处于相同虚拟地址 ,但虚拟内存只是系统的内存管理的一种技术,目的是使进程认为自己有一段连续的地址空间,方便分配与数据管理,他不是“实际”的,进程中的数据实际存在于内存对应的物理地址

<?php
//获取当期那进程id
$pid = posix_getpid();
echo "my pid: {$pid}", PHP_EOL;
$childNum = 5;
global $a;
$a = 1;
for ($i = 1; $i <= $childNum; ++$i) {
//fork一个进程
$pid = pcntl_fork();
//创建失败
if (-1 === $pid) {
echo "failt to fork!", PHP_EOL;
exit;
} elseif( $pid == 0 ) {
//当前进程pid
$mypid = posix_getpid();
//父进程pid
$parentpid = posix_getppid();
//子进程中操作父进程变量并显示结果
$a++;
echo "I'm the {$i}th child and my pid:{$mypid},parentpid:{$parentpid} a={$a}", PHP_EOL;
sleep($childNum);
exit;
} elseif ($pid > 0) {
//parent code
echo "fork the {$i}th child,pid:{$pid} a={$a}", PHP_EOL;
}
}

代码运行结果,全局变量a的值始终为2说明,无法修改父进程的值s

my pid: 37018
fork the 1th child,pid:37019 a=1
I'm the 1th child and my pid:37019,parentpid:37018  a=2
fork the 2th child,pid:37020 a=1
I'm the 2th child and my pid:37020,parentpid:37018  a=2
fork the 3th child,pid:37021 a=1
I'm the 3th child and my pid:37021,parentpid:37018  a=2
fork the 4th child,pid:37022 a=1
I'm the 4th child and my pid:37022,parentpid:37018  a=2
fork the 5th child,pid:37023 a=1
I'm the 5th child and my pid:37023,parentpid:37018  a=2

进程的执行顺序

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

fork函数在执行时,会复制父进程的代码,当分支较多时,代码比较庞大,每次fork时都要将所有代码复制一次,如此代码冗余造成的空间浪费不可忽视。可以将多于代码封装起来,在需要时使用exec函数族调用执行。

孤儿进程

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

僵尸进程

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

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

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

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

进程同步

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

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

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

参考

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 的值设置成对应的隔离级别

隔离级别演示案例

virtualbox 网络设置

参考

简介 virtual有四种网络模式

1.NAT Network Address Translation 网络地址转换

NAT模式是最简单的实现虚拟机上网的方式

Guest访问网络的所有数据都是由主机提供的,Guest并不真实存在于网络中,主机与网络中的任何机器都不能查看和访问到Guest的存在。

Guest可以访问主机能访问到的所有网络,但是对于主机以及主机网络上的其他机器,Guest又是不可见的,甚至主机也访问不到Guest。

  • 虚拟机与主机的关系:只能单向访问,虚拟机可以通过网络访问到主机,主机无法通过网络访问到虚拟机。
  • 虚拟机与网络中其他主机的关系:只能单向访问,虚拟机可以访问到网络中其他主机,其他主机不能通过网络访问到虚拟机。
  • 虚拟机与虚拟机的关系:相互不能访问,虚拟机与虚拟机各自完全独立,相互间无法通过网络访问彼此。

2.Bridged Adapter 网桥模式

使用桥接模式的虚拟机真实存在于网络中相当于一天独立的电脑

它是通过主机网卡,架设了一条桥,直接连入到网络中了。因此,它使得虚拟机能被分配到一个网络中独立的IP,所有网络功能完全和在网络中的真实机器一样。

网桥模式下的虚拟机,相当于真实存在于网络环境中的电脑.

  • 虚拟机与主机的关系:可以相互访问,因为虚拟机在真实网络段中有独立IP,主机与虚拟机处于同一网络段中,彼此可以通过各自IP相互访问。
  • 虚拟机于网络中其他主机的关系:可以相互访问,同样因为虚拟机在真实网络段中有独立IP,虚拟机与所有网络其他主机处于同一网络段中,彼此可以通过各自IP相互访问。
  • 虚拟机与虚拟机的关系:可以相互访问,原因同上。

3.Internal 内网模式

虚拟机与外网完全断开,只实现虚拟机于虚拟机之间的内部网络模式。

  • 虚拟机与主机的关系:不能相互访问,彼此不属于同一个网络,无法相互访问。
  • 虚拟机与网络中其他主机的关系:不能相互访问,理由同上。
  • 虚拟机与虚拟机的关系:可以相互访问,前提是在设置网络时,两台虚拟机设置同一网络名称.

4.Host-only Adapter 主机模式

主机模式,这是一种比较复杂的模式,需要有比较扎实的网络基础知识才能玩转。可以说前面几种模式所实现的功能,在这种模式下,通过虚拟机及网卡的设置都可以被实现。

我们可以理解为Guest在主机中模拟出一张专供虚拟机使用的网卡,所有虚拟机都是连接到该网卡上的,我们可以通过设置这张网卡来实现上网及其他很多功能,比如(网卡共享、网卡桥接等)。

  • 虚拟机与主机的关系:默认不能相互访问,双方不属于同一IP段,host-only网卡默认IP段为192.168.56.X 子网掩码为255.255.255.0,后面的虚拟机被分配到的也都是这个网段。通过网卡共享、网卡桥接等,可以实现虚拟机于主机相互访问。
  • 虚拟机与网络主机的关系:默认不能相互访问,原因同上,通过设置,可以实现相互访问。
  • 虚拟机与虚拟机的关系:默认可以相互访问,都是同处于一个网段

对比表格

NATBridged AdapterInternalHost-only Adapter
虚拟机-主机 O O X 默认不能需设置
主机-虚拟机 X O X 默认不能需设置
虚拟机-其它主机 O O X 默认不能需设置
其 它主机-虚拟机 X O X 默认不能需设置
虚拟机之间 X O 同网络O O

配置需求

需要模拟集群环境配置宿主机可以访问虚拟机,虚拟机之间可以相互访问

方案一

使用桥接模式,什么问题都能解决.缺点使用DHCP获取的动态ip,更换网络环境,需要修改应用配置,如mysql,redis连接.如果使用静态id,更换网络环境时可能造成ip冲突

方案二

Mysql 开启远程访问 给指定数据库创建用户并授权

Mysql 8.0

#创建用户 db_user 换成root表示root用户
MySQL [(none)]> create user db_user@'%' identified by 'db_pass';

#授权  db_name 用*替代表示访问所有数据库
MySQL [(none)]> grant all privileges on db_name.* to db_user@'%' with grant option; 

#退出数据库控制台,特别注意有分号
MySQL [(none)]> exit;

Mysql 8.0以下

MySQL [(none)]> grant all privileges on db_name.* to db_user@'%' identified by 'db_pass';
 
#授权语句,特别注意有分号
MySQL [(none)]> flush privileges;

#退出数据库控制台,特别注意有分号
MySQL [(none)]> exit;

创建用户

create user 'username'@'%' identified by 'password';
username 用户名 %所有ip password 密码

授权所有数据库

grant all privileges on *.* to 'username'@'%';

flush privileges;

10x程序员工作法总结

本文为极客时间专栏总结

以终为始

概述:确定好最终目标(要看到真正的目标).不要把别人交代的工作当成最终目标.what are we going?

发现和规划

人类创造事物都需要经过两次,第一次是在头脑中的创造,也就是智力(想象)创造(Mental/First Creation),第二次才是付诸实践,也就是实际(物理)构建(Physical/Second Creation)

软件开发流程

软件开发中的终,做一个对用户有价值的软件,梳理完整的开发发布流程,提前预演发现潜在问题

DoD完成的定义(Definition)

任务分解

概述:将复杂的(庞大的)任务,拆解成一个个可执行任务,工作分解得越细致,越容易掌控工作,软件开发中的模块化,组件化就是利用了分解的思想.How can we get there?

沟通反馈

概述:保证与他人沟通,保证各方信息及时交互.减少因为理解偏差造成的工作疏漏.同时保证我们能够准确接受外部信息,及时修正自己的错误.

自动化

概述:将繁琐重复的工作,交给程序以自动化的方式执行,提升执行效率和成功率.

Linux 软件管理常用命令

yum 和 rpm 软件管理

rpm命令 yum命令

yum下载软件包方法,应用场景,大型国企生产环境不允许开放外网

通过yum命令只下载rpm包不安装

yum 下载全量依赖 rpm 包及离线安装(终极解决方案

dnf软件管理

dnf命令

APT工具(新立得)

apt的全称是Advanced Packaging Tool是Linux系统下的一款安装包管理工具

保持依赖不更新

sudo apt-mark hold libqt5core5a libqt5dbus5 libqt5gui5 libqt5network5 libqt5qml5 libqt5qmlmodels5 libqt5quick5 libqt5widgets5

取消依赖不更新

sudo apt-mark unhold libqt5core5a libqt5dbus5 libqt5gui5 libqt5network5 libqt5qml5 libqt5qmlmodels5 libqt5quick5 libqt5widgets5

PPA管理

apt-key命令

sudo apt-key list
会出现这样:
pub   1024R/B455BEF0 2010-07-29
uid                  Launchpad clicompanion-nightlies
删除想删除的
sudo apt-key del B455BEF0

添加PPA源的命令为:
sudo add-apt-repository ppa:user/ppa-name
添加好更新一下: sudo apt-get update
删除命令格式则为:
sudo add-apt-repository -r ppa:user/ppa-name

或者
到源的目录:cd  /etc/apt/sources.list.d/ 可以看到关于源的文件,删除即可 

DPKG

dpkg 是Debian package的简写,为”Debian“ 操作系统 专门开发的套件管理系统,用于软件的安装,更新和移除。

所有源自”Debian”的Linux的发行版都使用 dpkg,   例如”Ubuntu”。

dpkg本身是一个底层的工具。上层的工具,如APT,被用于从远程获取软件包以及处理复杂的软件包关系。 

安装指定软件包

dpkg -i|--install <.deb file name> ... | -R|--recursive <directory> ...
ex:dpkg -i -R /dirname 安装目录下的所有deb包

解压软件包

dpkg --unpack <.deb file name> ... | -R|--recursive <directory> ...
ex:dpkg --unpack -R /dirname 解压目录下的deb包

重新配置和释放软件包

dpkg --configure <package> ... | -a|--pending
-a 将配置所有没有配置的软件包 

dpkg  --triggers-only <package> ... | -a|--pending

移除软件包

dpkg -r|--remove <package> ... | -a|--pending

移除软件包和配置

dpkg -P|--purge  <package> ... | -a|--pending

-V|–verify <软件包名> … 检查包的完整性

–get-selections [<表达式> …] 把已选中的软件包列表打印到标准输出

–set-selections 从标准输入里读出要选择的软件

–clear-selections 取消选中所有不必要的软件包

–update-avail <软件包文件> 替换现有可安装的软件包信息

–merge-avail <软件包文件> 把文件中的信息合并到系统中

–clear-avail 清除现有的软件包信息

–forget-old-unavail 忘却已被卸载的不可安装的软件包

-s|–status <软件包名> … 显示指定软件包的详细状态

-p|–print-avail <软件包名> … 显示可供安装的软件版本

-L|–listfiles <软件包名> … 列出属于指定软件包的文件 查看已安装文件的路径

dpkg -L libwebp-dev
/usr
/usr/include
/usr/include/webp
/usr/include/webp/decode.h
...

-l|–list [<表达式> …] 简明地列出软件包的状态

-S|–search <表达式> … 搜索含有指定文件的软件包

-C|–audit [<表达式> …] 检查是否有软件包残损

–yet-to-unpack 列出标记为待解压的软件包

–predep-package 列出待解压的预依赖

–add-architecture <体系结构> 添加 <体系结构> 到体系结构列表

–remove-architecture <体系结构> 从架构列表中移除 <体系结构>

–print-architecture 显示 dpkg 体系结构 指的是cpu的体系架构 例amd64

–print-foreign-architectures 显示已启用的异质体系结构

–assert-<特性> 对指定特性启用断言支持

–validate-<属性> <字符串> 验证一个 <属性>的 <字符串>

–compare-vesions <a> <关系> <b> 比较版本号 – 见下

–force-help 显示本强制选项的帮助信息

-Dh|–debug=help 显示有关出错调试的帮助信息
-?, –help 显示本帮助信息

–version 显示版本信息
Assert 特性: support-predepends, working-epoch, long-filenames, multi-conrep, multi-arch, versioned-provides.
可验证的属性:pkgname, archname, trigname, version.
调用 dpkg 并带参数 -b, –build, -c, –contents, -e, –control, -I, –info, -f, –field, -x, –extract, -X, –vextract, –ctrl-tarfile, –fsys-tarfile是针对归档文件的 (输入 dpkg-deb –help 获取帮助)
选项:

–admindir=<目录> 使用 <目录> 而非 /var/lib/dpkg

–root=<目录> 安装到另一个根目录下

–instdir=<目录> 改变安装目录的同时保持管理目录不变

–path-exclude=<表达式> 不要安装符合Shell表达式的路径

–path-include=<表达式> 在排除模式后再包含一个模式

-O|–selected-only 忽略没有被选中安装或升级的软件包

-E|–skip-same-version 忽略版本与已安装软件版本相同的软件包

-G|–refuse-downgrade 忽略版本早于已安装软件版本的的软件包

-B|–auto-deconfigure 就算会影响其他软件包,也要安装

–[no-]triggers 跳过或强制随之发生的触发器处理

–verify-format=<格式> 检查输出格式(‘rpm’被支持)

–no-debsig 不去尝试验证软件包的签名

–no-act|–dry-run|–simulate 仅报告要执行的操作 – 但是不执行

-D|–debug=<八进制数> 开启调试(参见 -Dhelp 或者 –debug=help)

–status-fd <n> 发送状态更新到文件描述符<n>

–status-logger=<命令> 发送状态更新到 <命令> 的标准输入

–log=<文件名> 将状态更新和操作信息到 <文件名>

–ignore-depends=<软件包>,… 忽略关于 <软件包> 的所有依赖关系

–force-… 忽视遇到的问题(参见 –force-help)

–no-force-…|–refuse-… 当遇到问题时中止运行

–abort-after <n> 累计遇到 <n> 个错误后中止


可供–compare-version 使用的比较运算符有:

lt le eq ne ge gt (如果版本号为空,那么就认为它先于任意版本号);

lt-nl le-nl ge-nl gt-nl (如果版本号为空,那么就认为它后于任意版本号);

< << <= = >= >> > (仅仅是为了与主控文件的语法兼容)

Snap 官方文档

Snap是Canonical推出的一种新的软件打包格式,彻底解决linux依赖性的问题,Snap 软件包拥有更加稳定和安全的特性。snap也就是安装snap软件包的命令。

安装snap

sudo apt install snap 

列出计算机上所有snap安装情况:

sudo snap list

在应用商店中查找snap:

sudo snap find <软件包名>

安装Snap软件:

sudo snap install <snap软件包名>


更新Snap软件:

sudo snap refresh <snap软件包名>

更新所有的snap软件包:

sudo snap refresh

要将Snap还原到以前安装的版本:

sudo snap revert <snap软件包名>

卸载snap软件:

sudo snap remove <snap软件包名>

参考