全新启航 敬请期待
Linux 系统及应用问题分析排查工具
Linux服务器上经常遇到一些系统和应用上的问题,如何分析排查,需要利器,下面总结列表了一些常用工具、trace tool.
引用linux-performance-analysis-and-tools中图片,说明这些tool试用层次位置
OS系统命令
系统信息(RHEL/Fedora)
- uname -a 或 cat /proc/version #print system information
- Linux hadoopst2.cm6 2.6.18-164.el5 #1 SMP Tue Aug 18 15:51:48 EDT 2009 x86_64 x86_64 x86_64 GNU/Linux
- uptime
- 15:42:46 up 674 days, 6 min, 35 users, load average: 1.30, 5.97, 11.53
- cat /etc/redhat-release
- Red Hat Enterprise Linux Server release 5.4 (Tikanga)
- lsb_release
- LSB Version: :core-3.1-amd64:core-3.1-ia32:core-3.1-noarch:graphics-3.1-amd64:graphics-3.1-ia32:graphics-3.1-noarch
- cat /proc/cpuinfo
- cat /proc/meminfo
- lspci - list all PCI devices
- lsusb - list USB devices
- last, lastb - show listing of last logged in users
- lsmod — show the status of modules in the Linux Kernel
- modprobe - add and remove modules from the Linux Kernel
常用命令/工具
- ps
- To print a process tree: ps -ejH / ps axjf
- To get info about threads: ps -eLf / ps axms
- ulimit -a
- lsof - list open files, UNIX一切皆文件
- lsof -p PID
- rpm/yum
- rpm -qf FILE #文件所属rpm包
- rpm -ql RPM #rpm包含文件
- /var/log/yum.log #yum 更新包日志
- /etc/XXX #系统级程序配置目录, 如
- /etc/yum.repos.d/ yum源配置
- /var/log/XXX #日志目录, 如
- /var/log/cron #crontab日志,可以查看调度执行情况
- ntpd - Network Time Protocol (NTP) daemon,同步集群中机器时间
- squid - proxy caching server,集群WebUI的代理
系统监控
- mpstat - Report processors related statistics. 注意%sys %iowait值
- vmstat - Report virtual memory statistics
- iostat - Report Central Processing Unit (CPU) statistics and input/output statistics for devices and partitions.
- netstat - Print network connections, routing tables, interface statistics, masquerade connections, and multicast memberships
- netstat -atpn | grep PID
- ganglia - a scalable distributed monitoring system for high-performance computing systems such as clusters and Grids.
- sar/tsar - Collect, report, or save system activity information; tsar是淘宝自己改进的版本
- 定时采样(每分钟),可查历史记录(默认5分钟),可弥补ganglia显示更详细信息
- iftop - the “top” bandwidth consumers shown. iftop wiki
- iotop
- vmtouch, Portable file system cache diagnostics and control
网络相关
- telnet/nc IP PORT - 确认目标端口是否可访问,只ping通不一定端口可访问,可能防火墙等禁止
- ifconfig/ifup/ifdown - configure a network interface
- traceroute - print the route packets trace to network host
- nslookup - query Internet name servers interactively
- tcpdump - dump traffic on a network, 类似开源工具 wireshark, netsniff-ng, 更多工具比较
- lynx - a general purpose distributed information browser for the World Wide Web
- tcpcp - allows cooperating applications to pass ownership of TCP connection endpoints from one Linux host to another one.
程序/进程相关
静态信息
- ldconfig - configure dynamic linker run time bindings
- ldconfig -p | grep SO 查看so是否在link cache中
- ldd - print shared library dependencies, 查看exe或so依赖的so
- nm - list symbols from object files,可grep查找是否存在相关的symbol,是否Undefined.
- readelf - Displays information about ELF files. 可现实elf相关信息,如32/64位,适用的OS,处理器
动态信息
- gdb
- cat /proc/$PID/[cmdline|environ|limits|status|…] - 进程相关信息
- pstack - print a stack trace of a running process
- pmap - report memory map of a process
资料来源:
Shell脚本的调试方法
Shell脚本的调试方法
Shell提供了一些用于调试脚本的选项,如下所示:
-n
读一遍脚本中的命令但不执行,用于检查脚本中的语法错误
-v
一边执行脚本,一边将执行过的脚本命令打印到标准错误输出
-x
提供跟踪执行信息,将执行的每一条命令和结果依次打印出来
使用这些选项有三种方法,一是在命令行提供参数
1 | $ sh -x ./script.sh |
二是在脚本开头提供参数
1 | #! /bin/sh -x |
第三种方法是在脚本中用set命令启用或禁用参数
1 | #! /bin/sh |
set -x
和set +x
分别表示启用和禁用-x
参数,这样可以只对脚本中的某一段进行跟踪调试。
跟踪脚本的执行
你可以让bash打印出你脚本执行的过程中的所有语句。这很简单,只需要使用bash的-x选项就可以做到,下面让我们来看一下。
下面的这段脚本,先是输出一个问候语句,然后输出当前的时间:
1 | #!/bin/bash |
下面让我们使用-x选项来运行这段脚本:
1 | $ bash -x example_script.sh |
这时,我们可以看到,bash在运行前打印出了每一行命令。而且每行前面的+号表明了嵌套。这样的输出可以让你看到命令执行的顺序并可以让你知道整个脚本的行为。
在跟踪里输出行号
在一个很大的脚本中,你会看到很多很多的执行跟踪的输出,阅读起来非常费劲,所以,你可以在每一行前加上文件的行号,这会非常有用。要做到这样,你只需要设置下面的环境变量:
1 | export PS4='+${BASH_SOURCE}:${LINENO}:${FUNCNAME[0]}: ' |
让我们看看设置上了PS4这个环境变量后会是什么样的输出。
1 | $ bash -x example_script.sh |
调试部份的脚本
有些时候,你并不想调试整个脚本,你只要调试其中的一部份,那么,你可以在你想要调试的脚本之前,调用“set -x”,结束的时候调用“set +x”就可以了。如下面的脚本所示:
1 | #!/bin/bash |
让我们看看运行起来是啥样?
1 | $ ./example_script.sh |
注意:我们在运行脚本的时候,不需要使用bash -x了。
日志输出
跟踪日志有时候太多了,多得都受不了,而且,输出的内容很难阅读。一般来说,我们很多时候只关心于条件表达式,变量值,或是函数调用,或是循环等。。在这种情况下,log一些感兴趣的特定的信息,可能会更好。
使用log前,我们先写一个函数:
1 | _log() { |
于是,你就可以在你的脚本中如下使用:
1 | _log "Copying files..." |
我们可以看到,上面那个_log函数,需要检查一个_DEBUG 变量,只有这个变量是真,才会真正开发输出日志。这样,你就只需要控制这个开关,而不需要删除你的debug信息。
1 | $ _DEBUG=true ./example_script.sh |
参考原文作者:
陈皓
Python 中使用static,class,abstract方法
方法在Python中是如何工作的
方法就是一个函数,它作为一个类属性而存在,你可以用如下方式来声明、访问一个函数:
1 | >>> class Pizza(object): |
Python在告诉你,属性_get_size是类Pizza的一个未绑定方法。这是什么意思呢?很快我们就会知道答案:
1 | >>> Pizza.get_size() |
我们不能这么调用,因为它还没有绑定到Pizza类的任何实例上,它需要一个实例作为第一个参数传递进去(Python2必须是该类的实例,Python3中可以是任何东西),尝试一下:
1 | >>> Pizza.get_size(Pizza(42)) |
太棒了,现在用一个实例作为它的的第一个参数来调用,整个世界都清静了,如果我说这种调用方式还不是最方便的,你也会这么认为的;没错,现在每次调用这个方法的时候我们都不得不引用这个类,如果不知道哪个类是我们的对象,长期看来这种方式是行不通的。
那么Python为我们做了什么呢,它绑定了所有来自类_Pizza的方法以及该类的任何一个实例的方法。也就意味着现在属性get_size是Pizza的一个实例对象的绑定方法,这个方法的第一个参数就是该实例本身。
1 | >>> Pizza(42).get_size |
和我们预期的一样,现在不再需要提供任何参数给_get_size,因为它已经是绑定的,它的self参数会自动地设置给Pizza实例,下面代码是最好的证明:
1 | >>> m = Pizza(42).get_size |
更有甚者,你都没必要使用持有Pizza对象的引用了,因为该方法已经绑定到了这个对象,所以这个方法对它自己来说是已经足够了。
也许,如果你想知道这个绑定的方法是绑定在哪个对象上,下面这种手段就能得知:
1 | >>> m = Pizza(42).get_size |
显然,该对象仍然有一个引用存在,只要你愿意你还是可以把它找回来。
在Python3中,依附在类上的函数不再当作是未绑定的方法,而是把它当作一个简单地函数,如果有必要它会绑定到一个对象身上去,原则依然和Python2保持一致,但是模块更简洁:
1 | >>> class Pizza(object): |
静态方法
静态方法是一类特殊的方法,有时你可能需要写一个属于这个类的方法,但是这些代码完全不会使用到实例对象本身,例如:
1 | class Pizza(object): |
这个例子中,如果把_mix_ingredients作为非静态方法同样可以运行,但是它要提供self*参数,而这个参数在方法中根本不会被使用到。这里的@staticmethod*装饰器可以给我们带来一些好处:
- Python不再需要为Pizza对象实例初始化一个绑定方法,绑定方法同样是对象,但是创建他们需要成本,而静态方法就可以避免这些。
1 | >>> Pizza().cook is Pizza().cook |
- 可读性更好的代码,看到@staticmethod我们就知道这个方法并不需要依赖对象本身的状态。
- 可以在子类中被覆盖,如果是把mix_ingredients作为模块的顶层函数,那么继承自Pizza的子类就没法改变pizza的mix_ingredients了如果不覆盖cook的话。
类方法
话虽如此,什么是类方法呢?类方法不是绑定到对象上,而是绑定在类上的方法。
1 | >>> class Pizza(object): |
无论你用哪种方式访问这个方法,它总是绑定到了这个类身上,它的第一个参数是这个类本身(记住:类也是对象)。
什么时候使用这种方法呢?类方法通常在以下两种场景是非常有用的:
- 工厂方法:它用于创建类的实例,例如一些预处理。如果使用@staticmethod代替,那我们不得不硬编码Pizza类名在函数中,这使得任何继承Pizza的类都不能使用我们这个工厂方法给它自己用。
1 | class Pizza(object): |
- 调用静态类:如果你把一个静态方法拆分成多个静态方法,除非你使用类方法,否则你还是得硬编码类名。使用这种方式声明方法,Pizza类名明永远都不会在被直接引用,继承和方法覆盖都可以完美的工作。
1 | class Pizza(object): |
抽象方法
抽象方法是定义在基类中的一种方法,它没有提供任何实现,类似于Java中接口(Interface)里面的方法。
在Python中实现抽象方法最简单地方式是:
1 | class Pizza(object): |
任何继承自_Pizza的类必须覆盖实现方法get_radius,否则会抛出异常。
这种抽象方法的实现有它的弊端,如果你写一个类继承Pizza,但是忘记实现get_radius,异常只有在你真正使用的时候才会抛出来。
1 | >>> Pizza() |
还有一种方式可以让错误更早的触发,使用Python提供的abc模块,对象被初始化之后就可以抛出异常:
1 | import abc |
使用abc后,当你尝试初始化BasePizza或者任何子类的时候立马就会得到一个TypeError,而无需等到真正调用get_radius的时候才发现异常。
1 | >>> BasePizza() |
混合静态方法、类方法、抽象方法
当你开始构建类和继承结构时,混合使用这些装饰器的时候到了,所以这里列出了一些技巧。
记住,声明一个抽象的方法,不会固定方法的原型,这就意味着虽然你必须实现它,但是我可以用任何参数列表来实现:
1 | import abc |
这样是允许的,因为Calzone满足BasePizza对象所定义的接口需求。同样我们也可以用一个类方法或静态方法来实现:
1 | import abc |
这同样是正确的,因为它遵循抽象类BasePizza设定的契约。事实上get_ingredients方法并不需要知道返回结果是什么,结果是实现细节,不是契约条件。
因此,你不能强制抽象方法的实现是一个常规方法、或者是类方法还是静态方法,也没什么可争论的。从Python3开始(在Python2中不能如你期待的运行,见issue5867),在abstractmethod*方法上面使用@staticmethod和@classmethod*装饰器成为可能。
1 | import abc |
别误会了,如果你认为它会强制子类作为一个类方法来实现get_ingredients那你就错了,它仅仅表示你实现的get_ingredients在BasePizza中是一个类方法。
可以在抽象方法中做代码的实现?没错,Python与Java接口中的方法相反,你可以在抽象方法编写实现代码通过super()来调用它。(译注:在Java8中,接口也提供的默认方法,允许在接口中写方法的实现)
1 | import abc |
这个例子中,你构建的每个pizza都通过继承BasePizza的方式,你不得不覆盖get_ingredients方法,但是能够使用默认机制通过super()来获取ingredient列表。
感谢原文作者:
Python 多进程与多线程
今天电话面试被问到Python中多线程和多进程之间的关系和区别,说来丢人距上次使用Python时间久远,有些概念已经模糊了,检索查阅资料之后在此记录一下.
1 线程和进程的基本概念
现在的 PC 都是多核的,使用多线程能充分利用 CPU 来提供程序的执行效率。
1.1 线程
线程是一个基本的 CPU 执行单元。它必须依托于进程存活。一个线程是一个execution context(执行上下文),即一个 CPU 执行时所需要的一串指令。
1.2 进程
进程是指一个程序在给定数据集合上的一次执行过程,是系统进行资源分配和运行调用的独立单位。可以简单地理解为操作系统中正在执行的程序。也就说,每个应用程序都有一个自己的进程。
每一个进程启动时都会最先产生一个线程,即主线程。然后主线程会再创建其他的子线程。
1.3 两者的区别
- 线程必须在某个进程中执行。
- 一个进程可包含多个线程,其中有且只有一个主线程。
- 多线程共享同个地址空间、打开的文件以及其他资源。
- 多进程共享物理内存、磁盘、打印机以及其他资源。
1.4 线程的类型
线程的因作用可以划分为不同的类型。大致可分为:
- 主线程
- 子线程
- 守护线程(后台线程)
- 前台线程
2 Python 多线程
2.1 GIL
其他语言,CPU 是多核时是支持多个线程同时执行。但在 Python 中,无论是单核还是多核,同时只能由一个线程在执行。其根源是 GIL 的存在。
GIL 的全称是 Global Interpreter Lock(全局解释器锁),来源是 Python 设计之初的考虑,为了数据安全所做的决定。某个线程想要执行,必须先拿到 GIL,我们可以把 GIL 看作是“通行证”,并且在一个 Python 进程中,GIL 只有一个。拿不到通行证的线程,就不允许进入 CPU 执行。
而目前 Python 的解释器有多种,例如:
- CPython:CPython 是用C语言实现的 Python 解释器。 作为官方实现,它是最广泛使用的 Python 解释器。
- PyPy:PyPy 是用RPython实现的解释器。RPython 是 Python 的子集, 具有静态类型。这个解释器的特点是即时编译,支持多重后端(C, CLI, JVM)。PyPy 旨在提高性能,同时保持最大兼容性(参考 CPython 的实现)。
- Jython:Jython 是一个将 Python 代码编译成 Java 字节码的实现,运行在JVM (Java Virtual Machine) 上。另外,它可以像是用 Python 模块一样,导入 并使用任何Java类。
- IronPython:IronPython 是一个针对 .NET 框架的 Python 实现。它 可以用 Python 和 .NET framewor k的库,也能将 Python 代码暴露给 .NET 框架中的其他语言。
GIL 只在 CPython 中才有,而在 PyPy 和 Jython 中是没有 GIL 的。
每次释放 GIL锁,线程进行锁竞争、切换线程,会消耗资源。这就导致打印线程执行时长,会发现耗时更长的原因。
并且由于 GIL 锁存在,Python 里一个进程永远只能同时执行一个线程(拿到 GIL 的线程才能执行),这就是为什么在多核CPU上,Python 的多线程效率并不高的根本原因。
2.2 创建多线程
Python提供两个模块进行多线程的操作,分别是thread
和threading
,
前者是比较低级的模块,用于更底层的操作,一般应用级别的开发不常用。
- 方法1:直接使用
threading.Thread()
1 | import threading |
- 方法2:继承
threading.Thread
来自定义线程类,重写run
方法
1 | import threading |
2.3 线程合并
Join
函数执行顺序是逐个执行每个线程,执行完毕后继续往下执行。主线程结束后,子线程还在运行,join
函数使得主线程等到子线程结束时才退出。
1 | import threading |
2.4 线程同步与互斥锁
线程之间数据共享的。当多个线程对某一个共享数据进行操作时,就需要考虑到线程安全问题。threading
模块中定义了Lock 类,提供了互斥锁的功能来保证多线程情况下数据的正确性。
用法的基本步骤:
1 | #创建锁 |
其中,锁定方法acquire可以有一个超时时间的可选参数timeout。如果设定了timeout,则在超时后通过返回值可以判断是否得到了锁,从而可以进行一些其他的处理。
具体用法见示例代码:
1 | import threading |
2.5 可重入锁(递归锁)
为了满足在同一线程中多次请求同一资源的需求,Python 提供了可重入锁(RLock)。
RLock
内部维护着一个Lock
和一个counter
变量,counter 记录了 acquire 的次数,从而使得资源可以被多次 require。直到一个线程所有的 acquire 都被 release,其他的线程才能获得资源。
具体用法如下:
1 | #创建 RLock |
2.6 守护线程
如果希望主线程执行完毕之后,不管子线程是否执行完毕都随着主线程一起结束。我们可以使用setDaemon(bool)
函数,它跟join
函数是相反的。它的作用是设置子线程是否随主线程一起结束,必须在start()
之前调用,默认为False
。
2.7 定时器
如果需要规定函数在多少秒后执行某个操作,需要用到Timer
类。具体用法如下:
1 | from threading import Timer |
3 Python 多进程
3.1 创建多进程
Python 要进行多进程操作,需要用到muiltprocessing
库,其中的Process
类跟threading
模块的Thread
类很相似。所以直接看代码熟悉多进程。
- 方法1:直接使用
Process
, 代码如下:
1 | from multiprocessing import Process |
- 方法2:继承
Process
来自定义进程类,重写run
方法, 代码如下:
1 | from multiprocessing import Process |
3.2 多进程通信
进程之间不共享数据的。如果进程之间需要进行通信,则要用到Queue模块
或者Pipi模块
来实现。
- Queue
Queue 是多进程安全的队列,可以实现多进程之间的数据传递。它主要有两个函数,put
和get
。
put() 用以插入数据到队列中,put 还有两个可选参数:blocked 和 timeout。如果 blocked 为 True(默认值),并且 timeout 为正值,该方法会阻塞 timeout 指定的时间,直到该队列有剩余的空间。如果超时,会抛出 Queue.Full 异常。如果 blocked 为 False,但该 Queue 已满,会立即抛出 Queue.Full 异常。
get()可以从队列读取并且删除一个元素。同样,get 有两个可选参数:blocked 和 timeout。如果 blocked 为 True(默认值),并且 timeout 为正值,那么在等待时间内没有取到任何元素,会抛出 Queue.Empty 异常。如果blocked 为 False,有两种情况存在,如果 Queue 有一个值可用,则立即返回该值,否则,如果队列为空,则立即抛出 Queue.Empty 异常。
具体用法如下:
1 | from multiprocessing import Process, Queue |
- Pipe
Pipe的本质是进程之间的用管道数据传递,而不是数据共享,这和socket有点像。pipe() 返回两个连接对象分别表示管道的两端,每端都有send() 和recv()函数。
如果两个进程试图在同一时间的同一端进行读取和写入那么,这可能会损坏管道中的数据。
具体用法如下:
1 | from multiprocessing import Process, Pipe |
3.3 进程池
创建多个进程,我们不用傻傻地一个个去创建。我们可以使用Pool
模块来搞定。
Pool 常用的方法如下:
方法 | 含义 |
---|---|
apply() | 同步执行(串行) |
apply_async() | 异步执行(并行) |
terminate() | 立刻关闭进程池 |
join() | 主进程等待所有子进程执行完毕。必须在close或terminate()之后使用 |
close() | 等待所有进程结束后,才关闭进程池 |
具体用法见示例代码:
1 | from multiprocessing import Pool |
4 选择多线程还是多进程?
在这个问题上,首先要看下你的程序是属于哪种类型的。一般分为两种 CPU 密集型 和 I/O 密集型。
- CPU 密集型:程序比较偏重于计算,需要经常使用 CPU 来运算。例如科学计算的程序,机器学习的程序等。
- I/O 密集型:顾名思义就是程序需要频繁进行输入输出操作。爬虫程序就是典型的 I/O 密集型程序。
如果程序是属于 CPU 密集型,建议使用多进程。而多线程就更适合应用于 I/O 密集型程序。
感谢原文作者:
Linux进程间通信的几种方式
进程间通信也是Linux中一个相对基础的内容,网上检索学习回顾一番之后将其记录下来加深记忆,共同学习.
进程间通信概述
进程通信的目的
数据传输
一个进程需要将它的数据发送给另一个进程,发送的数据量在一个字节到几M字节之间
共享数据
多个进程想要操作共享数据,一个进程对共享数据
通知事件
一个进程需要向另一个或一组进程发送消息,通知它(它们)发生了某种事件(如进程终止时要通知父进程).
资源共享
多个进程之间共享同样的资源.为了作到这一点,需要内核提供锁和同步机制.
进程控制
有些进程希望完全控制另一个进程的执行(如Debug进程),此时控制进程希望能够拦截另一个进程的所有陷入和异常,并能够及时知道它的状态改变.
Linux 进程间通信(IPC)的发展
linux下的进程通信手段基本上是从Unix平台上的进程通信手段继承而来的.而对Unix发展做出重大贡献的两大主力AT&T的贝尔实验室及BSD(加州大学伯克利分校的伯克利软件发布中心)在进程间通信方面的侧重点有所不同.
前者对Unix早期的进程间通信手段进行了系统的改进和扩充,形成了“system V IPC”,通信进程局限在单个计算机内;
后者则跳过了该限制,形成了基于套接口(socket)的进程间通信机制.
Linux则把两者继承了下来
早期UNIX进程间通信
基于System V进程间通信
基于Socket进程间通信
POSIX进程间通信.
UNIX进程间通信方式包括:管道、FIFO、信号.
System V进程间通信方式包括:System V消息队列、System V信号灯、System V共享内存
POSIX进程间通信包括:posix消息队列、posix信号灯、posix共享内存.
由于Unix版本的多样性,电子电气工程协会(IEEE)开发了一个独立的Unix标准,这个新的ANSI Unix标准被称为计算机环境的可移植性操作系统界面(PSOIX).现有大部分Unix和流行版本都是遵循POSIX标准的,而Linux从一开始就遵循POSIX标准;
BSD并不是没有涉足单机内的进程间通信(socket本身就可以用于单机内的进程间通信).事实上,很多Unix版本的单机IPC留有BSD的痕迹,如4.4BSD支持的匿名内存映射、4.3+BSD对可靠信号语义的实现等等.
linux使用的进程间通信方式
1.管道(pipe),流管道(s_pipe)和有名管道(FIFO)
2.信号(signal)
3.消息队列
4.共享内存
5.信号量
6.套接字(socket)
管道( pipe )
管道这种通讯方式有两种限制,一是半双工的通信,数据只能单向流动,二是只能在具有亲缘关系的进程间使用.进程的亲缘关系通常是指父子进程关系.
流管道(s_pipe)
去除了第一种限制,可以双向传输.管道可用于具有亲缘关系进程间的通信,命名管道:name_pipe克服了管道没有名字的限制,因此,除具有管道所具有的功能外,它还允许无亲缘关系进程间的通信;
信号量( semophore )
信号量是一个计数器,可以用来控制多个进程对共享资源的访问.它常作为一种锁机制,防止某进程正在访问共享资源时,其他进程也访问该资源.因此,主要作为进程间以及同一进程内不同线程之间的同步手段.
信号是比较复杂的通信方式,用于通知接受进程有某种事件发生,除了用于进程间通信外,进程还可以发送信号给进程本身;linux除了支持Unix早期信号语义函数sigal外,还支持语义符合Posix.1标准的信号函数sigaction(实际上,该函数是基于BSD的,BSD为了实现可靠信号机制,又能够统一对外接口,用sigaction函数重新实现了signal函数);
消息队列( message queue )
消息队列是由消息的链表,存放在内核中并由消息队列标识符标识.消息队列克服了信号传递信息少、管道只能承载无格式字节流以及缓冲区大小受限等缺点.
消息队列是消息的链接表,包括Posix消息队列system V消息队列.有足够权限的进程可以向队列中添加消息,被赋予读权限的进程则可以读走队列中的消息.消息队列克服了信号承载信息量少,管道只能承载无格式字节流以及缓冲区大小受限等缺点.
信号 ( singal )
信号是一种比较复杂的通信方式,用于通知接收进程某个事件已经发生.
主要作为进程间以及同一进程不同线程之间的同步手段.
共享内存( shared memory )
共享内存就是映射一段能被其他进程所访问的内存,这段共享内存由一个进程创建,但多个进程都可以访问.共享内存是最快的 IPC 方式,它是针对其他进程间通信方式运行效率低而专门设计的.它往往与其他通信机制,如信号量,配合使用,来实现进程间的同步和通信.
使得多个进程可以访问同一块内存空间,是最快的可用IPC形式.是针对其他通信机制运行效率较低而设计的.往往与其它通信机制,如信号量结合使用,来达到进程间的同步及互斥.
套接字( socket )
套解口也是一种进程间通信机制,与其他通信机制不同的是,它可用于不同机器间的进程通信
更为一般的进程间通信机制,可用于不同机器之间的进程间通信.起初是由Unix系统的BSD分支开发出来的,但现在一般可以移植到其它类Unix系统上:Linux和System V的变种都支持套接字.
进程间通信各种方式效率比较
类型 | 无连接 | 可靠 | 流控制 | 优先级 |
---|---|---|---|---|
普通PIPE | N | Y | Y | N |
流PIPE | N | Y | Y | N |
命名PIPE(FIFO) | N | Y | Y | N |
消息队列 | N | Y | Y | Y |
信号量 | N | Y | Y | Y |
共享存储 | N | Y | Y | Y |
UNIX流SOCKET | N | Y | Y | N |
UNIX数据包SOCKET | N | Y | N | N |
注:无连接: 指无需调用某种形式的OPEN,就有发送消息的能力流控制;如果系统资源短缺或者不能接收更多消息,则发送进程能进行流量控制
各种通信方式的比较和优缺点
管道:速度慢,容量有限,只有父子进程能通讯
FIFO:任何进程间都能通讯,但速度慢
消息队列:容量受到系统限制,且要注意第一次读的时候,要考虑上一次没有读完数据的问题
信号量:不能传递复杂消息,只能用来同步
共享内存区:能够很容易控制容量,速度快,但要保持同步,比如一个进程在写的时候,另一个进程要注意读写的问题,相当于线程中的线程安全,当然,共享内存区同样可以用作线程间通讯,不过没这个必要,线程间本来就已经共享了同一进程内的一块内存
如果用户传递的信息较少或是需要通过信号来触发某些行为.前文提到的软中断信号机制不失为一种简捷有效的进程间通信方式.
但若是进程间要求传递的信息量比较大或者进程间存在交换数据的要求,那就需要考虑别的通信方式了.
无名管道简单方便.但局限于单向通信的工作方式.并且只能在创建它的进程及其子孙进程之间实现管道的共享:
有名管道虽然可以提供给任意关系的进程使用.但是由于其长期存在于系统之中,使用不当容易出错.所以普通用户一般不建议使用.
消息缓冲可以不再局限于父子进程,而允许任意进程通过共享消息队列来实现进程间通信,并由系统调用函数来实现消息发送和接收之间的同步,从而使得用户在使用消息缓冲进行通信时不再需要考虑同步问题,使用方便,但是信息的复制需要额外消耗CPU的时间,不适宜于信息量大或操作频繁的场合.
共享内存针对消息缓冲的缺点改而利用内存缓冲区直接交换信息,无须复制,快捷、信息量大是其优点.
但是共享内存的通信方式是通过将共享的内存缓冲区直接附加到进程的虚拟地址空间中来实现的,因此,这些进程之间的读写操作的同步问题操作系统无法实现.必须由各进程利用其他同步工具解决.另外,由于内存实体存在于计算机系统中,所以只能由处于同一个计算机系统中的诸进程共享.不方便网络通信.
共享内存块提供了在任意数量的进程之间进行高效双向通信的机制.每个使用者都可以读取写入数据,但是所有程序之间必须达成并遵守一定的协议,以防止诸如在读取信息之前覆写内存空间等竞争状态的出现.
不幸的是,Linux无法严格保证提供对共享内存块的独占访问,甚至是在您通过使用IPC_PRIVATE创建新的共享内存块的时候也不能保证访问的独占性. 同时,多个使用共享内存块的进程之间必须协调使用同一个键值.
感谢原文作者:
CLI创建并使用KVM
在虚拟机中搭建KVM实验环境并实现KVM虚拟机之间的网络通信及分布式网站架构.实验中直接使用命令行界面实现虚拟机中系统的安装和配置,免去了图形化界面操作的步骤.系统安装完成之后配置虚拟网卡,配置虚拟网段及实现远程连接,分布式网站中跳板机的设置及ip过滤,模拟了一个小型网站的架构后期可以使用docker来替代KVM以实现更好的实验效果.
整个实验从配置环境到安装虚拟机,配置软件环境,配置网络通信等方面实现了KVM虚拟机环境配置的需求.
1 | 1.检测是否支持KVM |