Author: 云厉

新浪云SAE支持直接安装 ThinkPHP3.2 啦

ThinkPHP3.2 核心内置了对SAE平台的支持(采用了应用模式的方式),
具有自己的独创特性,能够最大程度的使用ThinkPHP的标准特性,让开发人员感受不到SAE和普通环境的差别。

了解更多:http://document.thinkphp.cn/manual_3_2/sae.html

1、在应用仓库直接安装:

http://sae.sina.com.cn/?m=apps&a=detail&aid=166

2、点击右上角安装框架

输入自己的喜欢域名主体(创建后可以在后台绑定自己的域名)

目前 SAE 最新版 ThinkPHP 是 3.2.3,
并且使用的是ThinkPHP官方提供的远程安装包,代码和官方保持同步

秒懂系列|Apache保护文件和目录及来源IP限制实现

访问网站目录时 Apache 默认配置为可列出目录/文件列表,即当你访问http://localhost 时会列出相关的目录和文件列表;有时我们需要控制某个目录或某个文件不被访问,比如常见的WIKI系统,文档数据(一般是txt文件)就必须不能被访问,否则将出现安全隐患;
我们可以通过修改 Apache 配置文件来实现禁止列出目录/文件列表(当然,你也可以通过 .htaccess 来实现)

一、禁止访问某些文件/目录

1、增加Files选项来控制,比如要不允许访问 .yaml 扩展名的文件,保护配置文件:

<Files ~ ".yaml$">
    Order allow,deny
    Deny from all
</Files>

2、禁止访问某些指定的目录:(可以用 来进行正则匹配)

<Directory ~ "^/home/www/sites/(.+/)*[0-9]{3}">
    Order allow,deny
    Deny from all
</Directory>

3、通过文件匹配来进行禁止,比如禁止所有针对图片的访问:

<FilesMatch .(?i:gif|jpe?g|png)$>
    Order allow,deny
    Deny from all
</FilesMatch>

4、针对URL相对路径的禁止访问,比如dokuwiki系统就必须保护下列目录:

<Location ~ "^/wiki/(data|conf|bin|inc)">
    Order allow,deny
    Deny from all
</Location>

5、针对代理方式禁止对某些目标的访问( 可以用来正则匹配),比如拒绝通过代理访问 979137.com:

<Proxy https://979137.com/*>
    Order allow,deny
    Deny from all
</Proxy>

二、禁止某些IP访问/只允许某些IP访问

1、如果要控制禁止某些非法IP访问,在Directory选项控制:

<Directory "/home/www/sites/">
    Order allow,deny
    Allow from all
    Deny from 10.0.0.1 #阻止一个IP
    Deny from 192.168.0.0/24 #阻止一个IP段
</Directory>

2、只允许某些IP访问,适合比如就允许内部或者合作公司访问:

<Directory "/home/www/sites/">
    Order deny,allow
    Deny from all
    All from example.com #允许某个域名
    All from 10.0.0.1 #允许一个iP
    All from 10.0.0.1 10.0.0.2 #允许多个iP
    Allow from 10.1.0.0/255.255.0.0 #允许一个IP段,掩码对
    All from 10.0.1 192.168 #允许一个IP段,后面不填写
    All from 192.168.0.0/24 #允许一个IP段,网络号
</Directory>

不依赖扩展,PHP生成二维码类

PHP QR Code 是一个PHP二维码生成类库,利用它可以轻松生成二维码,注:请确保你的PHP支持GD2
项目地址:http://phpqrcode.sourceforge.net

  • 该类库原生不支持生成固定大小的二维码图片的(默认是根据二维码数据内容的长度而变化大小)
  • 该类库不支持生成带有icon的二维码

我对该类库进行了修改,同时封装了一个QRcode类,简化操作的同时,实现了上述两个功能:

<?php
/**
 * 二维码服务.
 *
 * @author 979137@qq.com
 * @copyright ©2015, Sina App Engine.
 * @version $Id$
 */
class SaeQRcode {
    private $errMsg = 'success';
    private $errNum = 0;

    //二维码配置参数
    public $data    = '';
    public $level   = 'M';
    public $width   = 200;
    public $height  = 200;
    public $margin  = 0;
    public $icon    = '';
    public $saveUrl = '';

    //生成的二维码文件
    private $code   = '';

    /**
     * 生成二维码图片 
     * 
     * @desc
     * 
     * @access public
     * @return void
     * @exception none
     */
    public function build() {
        static $qrcode = false;
        if (!$qrcode) {
            include(__DIR__.'/phpqrcode.php');
            $qrcode = true;
        }
        if (trim($this->data) == '') {
            $this->errNum = -1;
            $this->errMsg = 'data cannot be empty!';
            return false;
        } elseif (!in_array($this->level, array('L','M','Q','H'))) {
            $this->errNum = -2;
            $this->errMsg = 'level optional values: L, M, Q, H';
            return false;
        } elseif (!is_numeric($this->width) || !is_numeric($this->height)) {
            $this->errNum = -3;
            $this->errMsg = 'width and height parameter error';
            return false;
        }
        $this->code = $this->$saveUrl . md5((microtime(true)*10000).uniqid(time())) . '.png';
        try {
            defined('QRCODE_IMG_W') or define('QRCODE_IMG_W', $this->width);
            defined('QRCODE_IMG_H') or define('QRCODE_IMG_H', $this->height);
            QRcode::png($this->data, $this->code, $this->level, 3, $this->margin);
        } catch(Exception $e) {
            $this->errNum = -4;
            $this->errMsg = $e->getMessage();
            return false;
        }
        if (trim($this->icon) != '') {
            return $this->iconCover() ? $this->code : false;
        }
        return $this->code;
    }

    /**
     * icon覆盖
     * 
     * @desc
     * 
     * @access public
     * @return boolean
     * @exception none
     */
    public function iconCover() {
        if (!is_file($this->code) || $this->fileType($this->code) != 'png') {
            $this->errNum = -10;
            $this->errMsg = 'QRcode file does not exist or file type is not supported(Only allow PNG)';
            return false;
        }
        //远程icon,先下载到本地
        if (filter_var($this->icon, FILTER_VALIDATE_URL)) {
            //TODO..
        }
        if (!is_file($this->icon) || !in_array($this->fileType($this->icon), array('png','jpg','gif'))) {
            $this->errNum = -11;
            $this->errMsg = 'icon file does not exist or file type is not supported(Only allow PNG,JPG,GIF)';
            return false;
        }
        $codeData = file_get_contents($this->code);
        $iconData = file_get_contents($this->icon);
        $code = imagecreatefromstring($codeData);
        $icon = imagecreatefromstring($iconData);
        list($code_w, $code_h) = array(imagesx($code), imagesy($code));
        list($icon_w, $icon_h) = array(imagesx($icon), imagesy($icon));
        //目标宽高(等比例缩小)
        $icon_code_w = $code_w / 5;
        $scale = $icon_w / $icon_code_w;
        $icon_code_h = $icon_h / $scale;
        //目标XY坐标(将icon置于二维码正中间)
        $dst_x = ($code_w - $icon_code_w) / 2;
        $dst_y = ($code_h - $icon_code_h) / 2;
        imagecopyresampled($code, $icon, $dst_x, $dst_y, 0, 0, $icon_code_w, $icon_code_h, $icon_w, $icon_h);
        return imagepng($code, $this->code);
    }

    /**
     * 取二进制文件头快速准确判断文件类型
     * 
     * @desc
     * 
     * @access public
     * @params $file 要判断的文件,支持相对和绝对路径
     * @return void
     * @exception none
     */
    public function fileType($file) {
        $filepath = realpath($file);
        $filetype = array(
            7790=>'exe', 7784=>'midi',
            8075=>'zip', 8297=>'rar',
            7173=>'gif', 6677=>'bmp', 13780=>'png', 255216=>'jpg'
        );
        if (!($fp = @fopen($filepath, 'rb'))) return false;
        $bin = fread($fp, 2);
        fclose($fp);
        $str_info = @unpack('C2chars', $bin);
        $str_code = intval($str_info['chars1'].$str_info['chars2']);
        return isset($filetype[$str_code]) ? $filetype[$str_code] : false;
    }

    /**
     * 获取错误信息 
     * 
     * @desc
     * 
     * @access public
     * @return string
     * @exception none
     */
    public function errmsg() {
        $ret = $this->errMsg;
        $this->errMsg = 'Success';
        return $ret;
    }

    /**
     * 获取错误码 
     * 
     * @desc
     * 
     * @access public
     * @return int
     * @exception none
     */
    public function errno() {
        $ret = $this->errNum;
        $this->errNum = 0;
        return $ret;
    }
}

调用示例:

<?php
//二维码名片,格式参考:http://en.wikipedia.org/wiki/VCard
$vCard  = 'BEGIN:VCARD'.PHP_EOL;
$vCard .= 'VERSION:4.0'.PHP_EOL;
$vCard .= 'FN:倒流'.PHP_EOL;
$vCard .= 'ORG:SINA Inc'.PHP_EOL;
$vCard .= 'TITLE:攻城师'.PHP_EOL;
$vCard .= 'TEL;WORK;VOICE:(010)62676155'.PHP_EOL;
$vCard .= 'TEL;HOME;VOICE:(010)88889999'.PHP_EOL;
$vCard .= 'TEL;TYPE=cell:18600005940'.PHP_EOL;
$vCard .= 'ADR;TYPE=work;LABEL="Office":理想国际大厦17层;北四环西路58号;海淀区;北京市;中国;100089'.PHP_EOL;
$vCard .= 'EMAIL:979137@qq.com'.PHP_EOL;
$vCard .= 'END:VCARD';
//注:不同的扫描工具解码方式不一样,所以不是所有的二维码扫描工具都能唤起相关的功能
$types  = array(
    'vCard'   => $vCard,
    'url'     => 'http://sae.sina.com.cn',
    'tel'     => 'tel:18600005940',
    'smsto'   => 'smsto:18600005940:晚上继续嗨皮',
    'mailto'  => 'mailto:979137@qq.com?subject='.urlencode('恭喜发财').'&body='.urlencode('红包拿来'),
    'skype'   => 'skype:'.urlencode('Skype用户名').'?call',
    'chinese' => '中文二维码内容',
);

$qr = new QRcode();
//设置二维码生成参数
//二维码内容数据
$qr->data   = $types['vCard'];
//校正级别(容错率):L(7%)、M(15%)、Q(25%)、H(30%),了解:http://baike.baidu.com/view/4144600.htm
$qr->level  = 'L';
//二维码宽高(包含间距),为保证二维码更易识别,请尽量保持二维码为正方形,即长宽大致相等,默认200*200
$qr->width  = 300;
$qr->height = 300;
//二维码图片边缘间距值,值越大,间距越宽,可自由调整,默认0
$qr->margin = 1;
//在二维码正中间放置icon,默认为空,即不放置,支持绝对与相对地址
$qr->icon   = __DIR__ . '/logo.png';
$qr->icon   = 'logo.png';
//图片保存路径
$qr->saveUrl = SAE_TMP_PATH;
//生成二维码图片,成功返回文件绝对地址,失败返回false
$file = $qr->build();
if (!$file) {
    var_dump($qr->errno(), $qr->errmsg());
    exit;
}

//直接输出图片
//header('Content-Type: image/png');
//exit(file_get_contents($file));

//根据实际需求,可上传至Storage(这里以SAE为例)
$name = 'test/'.pathinfo($file, PATHINFO_BASENAME);
$domain = 'public';
$st = new SaeStorage();
$st->upload($domain, $name, $file);
$url = sprintf('http://%s-%s.stor.sinaapp.com/%s', $_SERVER['HTTP_APPNAME'], $domain, $name);
echo '<img src="'.$url.'">';

云厉技术博客

PHP数组转XML函数:array2xml

<?php
 * 数组转XML函数
 * @author 979137@qq.com
 * @param array $arr 要转换的数组 
 * @param string $item 默认节点名称
 * @param object $xml XML节点对象
 * @return string XML格式的字符串
 */
function array2xml($arr, $item='item', $xml=NULL) {
    is_null($xml) && $xml = new \SimpleXMLElement('<xml></xml>');
    foreach ($arr as $key=>$val) {
        is_numeric($key) && $key = $item;
        if (is_array($val) || is_object($val)) {
            $child = $xml->addChild($key);
            array2xml($val, $item, $child);
        } elseif (is_numeric($val)) {
            $child = $xml->addChild($key, $val);
        } else {
            $child = $xml->addChild($key);
            $node = dom_import_simplexml($child);
            $_val = $node->ownerDocument->createCDATASection($val);
            $node->appendChild($_val);
        }
    }
    return $xml->asXML();
}

秒懂系列|Linux给普通用户添加sudo权限

[shiliang@localhost ~]$ sudo ifup eth0
[sudo] password for shiliang:
shiliang is not in the sudoers file. This incident will be reported.

提示用户 shiliang 不在sudoers文件里.我们要做的就是把 shiliang 添加到sudoers文件里,怎么做呢?

1.进入超级用户模式:

[shiliang@localhost ~]$ su -
Password:

2.添加sudoers文件写权限:

[root@localhost ~]# chmod u+w /etc/sudoers

3.添加 shiliang 到sudoers文件里

[root@localhost ~]# vim /etc/sudoers
root    ALL=(ALL)   ALL
shiliang  ALL=(ALL)   ALL

在 “root ALL=(ALL) ALL” 这行下添加 “shiliang ALL=(ALL) ALL”

4.撤消sudoers写权限. 记得撤消写权限

[root@localhost ~]# chmod u-w /etc/sudoers

通过以下4步,成功将 shiliang 这个用户添加到 sudoers 文件里,当然 shiliang 也获得了sudo权限.

在新浪云SAE上实现PHP锁机制,简单解决并发问题

很多时候需要让系统的某些操作串行化,如抢购(先到先得)、报名等,这个时候就要对这些操作来加上一把锁。(好比上厕所,需要挨个来,待你用完之后把门打开,别人再进去,保证厕所永远只有1个人, 上厕所的过程是串行化的)

也许通过文件(加锁创建一个文件/解锁再删掉,或直接使用flock函数) 或者 MySQL结合InnoDB引擎 去实现,方法也比较多。但个人感觉都不是很优秀,尤其在新浪云SAE这种不支持IO的PaaS上,文件锁的实现方式是不能用的。

在SAE,其实有非常多的方法、很简单的实现这个需求,这里写个利用 Counter 服务模拟锁的实现方法:

<?php

class Lock {

    const LKEY = 'GOLD_LOCK';

    static private $ct = NULL;

    //初始化操作,取得计数器实例,如果计数器不存在则创建
    static public function init() {
        is_null(self::$ct) && self::$ct = new SaeCounter();
        self::$ct->exists(self::LKEY) || self::$ct->create(self::LKEY);
    }   

    //加锁
    static public function add() {
        self::init();
        return self::$ct->set(self::LKEY, 1); 
    }   

    //开锁
    static public function del() {
        self::init();
        return self::$ct->set(self::LKEY, 0); 
    }   

    //获取锁状态
    static public function get() {
        self::init();
        return self::$ct->get(self::LKEY) > 0;
    }   
}

//1、判断是否已加锁
if (Lock::get()) {
    /*
     * 这里可以直接exit
     * 也可以while写个死循环结合sleep,实现类似小米抢购的那种等待状态,在服务器内部进行排队抢购
     * ....
     * ....
     */   
} 

//2、加锁
Lock::add();

/*
 * 这里就是你业务逻辑代码
 * ....
 * ....
 */

//3、开锁
Lock::del();

Counter是SAE为开发者提供的计数器服务,用来实现高并发情景下的计数功能。用户可以在控制面板或程序中创建计数器,通过API对计数器进行设置,加减和统计。
Counter简化了计数应用的开发.开发者可以轻松实现高并发情景下的计数功能,实现如兔年春晚投票,广告渠道访问计数等应用,同时可以使用Counter的统计功能轻松实现数据汇总。

你可以通过SAE的KVDB、Memcache 等服务,轻松实现串行,当然还有专门为队列而生的 TaskQueue,这里就不一一介绍了

Google 开源了 Material Design 图标,免费下载

Material Design Icons 是 Google Material Design 规范的官方开源图标集,将近 800 个图标。
图标分类涵盖:动作、告警、音频视频、通信、内容、设备、编辑器、文件、硬件、图像、地图、导航、通知、社交等等。

图标格式:

  • SVG 格式,24 像素和 48 像素。
  • SVG 和 CSS sprites
  • 适用于 Web 的 1x, 2x PNG 格式图标 icon
  • 适用于 iOS 的 1x, 2x, 3x PNG 格式图标
  • 所有图标的 Hi-dpi 版本 (hdpi, mdpi, xhdpi, xxhdpi, xxxhdpi) (PNG)

在线查看这些图标:https://material.io/guidelines/style/icons.html
打包下载:https://github.com/google/material-design-icons/releases

大型网站系统架构的演化

一个成熟的大型网站(如淘宝、京东等)的系统架构并不是开始设计就具备完整的高性能、高可用、安全等特性,
它总是随着用户量的增加,业务功能的扩展逐渐演变完善的,在这个过程中,开发模式、技术架构、设计思想也发生了很大的变化,就连技术人员也从几个人发展到一个部门甚至一条产品线。
所以成熟的系统架构是随业务扩展而完善出来的,并不是一蹴而就;不同业务特征的系统,会有各自的侧重点,
例如淘宝,要解决海量的商品信息的搜索、下单、支付,
例如腾讯,要解决数亿的用户实时消息传输,
例如百度,它要处理海量的搜索请求,他们都有各自的业务特性,系统架构也有所不同。
尽管如此我们也可以从这些不同的网站背景下,找出其中共用的技术,这些技术和手段可以广泛运行在大型网站系统的架构中,下面就通过介绍大型网站系统的演化过程,来认识这些技术和手段。

一、最开始的网站架构

最初的架构,应用程序、数据库、文件都部署在一台服务器上,如图:
云厉的博客

二、应用、数据、文件分离

随着业务的扩展,一台服务器已经不能满足性能需求,故将应用程序、数据库、文件各自部署在独立的服务器上,并且根据服务器的用途配置不同的硬件,达到最佳的性能效果。
云厉的博客

三、利用缓存改善网站性能

在硬件优化性能的同时,同时也通过软件进行性能优化,在大部分的网站系统中,都会利用缓存技术改善系统的性能,使用缓存主要源于热点数据的存在,大部分网站访问都遵循28原则(即80%的访问请求,最终落在20%的数据上),所以我们可以对热点数据进行缓存,减少这些数据的访问路径,提高用户体验。
云厉的博客

缓存实现常见的方式是本地缓存、分布式缓存。当然还有CDN、反向代理等,这个后面再讲。本地缓存,顾名思义是将数据缓存在应用服务器本地,可以存在内存中,也可以存在文件,OSCache就是常用的本地缓存组件。本地缓存的特点是速度快,但因为本地空间有限所以缓存数据量也有限。分布式缓存的特点是,可以缓存海量的数据,并且扩展非常容易,在门户类网站中常常被使用,速度按理没有本地缓存快,常用的分布式缓存是Memcached、Redis。

四、使用集群改善应用服务器性能

应用服务器作为网站的入口,会承担大量的请求,我们往往通过应用服务器集群来分担请求数。应用服务器前面部署负载均衡服务器调度用户请求,根据分发策略将请求分发到多个应用服务器节点。
云厉的博客

常用的负载均衡技术硬件的有F5,价格比较贵,软件的有LVS、Nginx、HAProxy。LVS是四层负载均衡,根据目标地址和端口选择内部服务器,Nginx是七层负载均衡和HAProxy支持四层、七层负载均衡,可以根据报文内容选择内部服务器,因此LVS分发路径优于Nginx和HAProxy,性能要高些,而Nginx和HAProxy则更具配置性,如可以用来做动静分离(根据请求报文特征,选择静态资源服务器还是应用服务器)。

五、数据库读写分离和分库分表

随着用户量的增加,数据库成为最大的瓶颈,改善数据库性能常用的手段是进行读写分离以及分表,读写分离顾名思义就是将数据库分为读库和写库,通过主备功能实现数据同步。分库分表则分为水平切分和垂直切分,水平切换则是对一个数据库特大的表进行拆分,例如用户表。垂直切分则是根据业务不同来切换,如用户业务、商品业务相关的表放在不同的数据库中。
云厉的博客

六、使用CDN和反向代理提高网站性能

假如我们的服务器都部署在成都的机房,对于四川的用户来说访问是较快的,而对于北京的用户访问是较慢的,这是由于四川和北京分别属于电信和联通的不同发达地区,北京用户访问需要通过互联路由器经过较长的路径才能访问到成都的服务器,返回路径也一样,所以数据传输时间比较长。对于这种情况,常常使用CDN解决,CDN将数据内容缓存到运营商的机房,用户访问时先从最近的运营商获取数据,这样大大减少了网络访问的路径。比较专业的CDN运营商有蓝汛、网宿。

而反向代理,则是部署在网站的机房,当用户请求达到时首先访问反向代理服务器,反向代理服务器将缓存的数据返回给用户,如果没有没有缓存数据才会继续走应用服务器获取,也减少了获取数据的成本。反向代理有Squid,Nginx。
云厉的博客

七、使用分布式文件系统

用户一天天增加,业务量越来越大,产生的文件越来越多,单台的文件服务器已经不能满足需求。需要分布式的文件系统支撑。常用的分布式文件系统有NFS。
云厉的博客

八、使用NoSql和搜索引擎

对于海量数据的查询,我们使用nosql数据库加上搜索引擎可以达到更好的性能。并不是所有的数据都要放在关系型数据中。常用的NOSQL有mongodb和redis,搜索引擎有lucene。
云厉的博客

九、将应用服务器进行业务拆分

随着业务进一步扩展,应用程序变得非常臃肿,这时我们需要将应用程序进行业务拆分,如百度分为新闻、网页、图片等业务。每个业务应用负责相对独立的业务运作。业务之间通过消息进行通信或者同享数据库来实现。
云厉的博客

十、搭建分布式服务

这时我们发现各个业务应用都会使用到一些基本的业务服务,例如用户服务、订单服务、支付服务、安全服务,这些服务是支撑各业务应用的基本要素。我们将这些服务抽取出来利用分部式服务框架搭建分布式服务。淘宝的Dubbo是一个不错的选择。
云厉的博客

文章来源:http://www.cnblogs.com/leefreeman/p/3993449.html

不用OAuth授权,10行代码获取用户微博昵称

这种需求看似奇葩,其实很常见,比如你的应用或网站接入了微博登录,一般会在数据库维护用户绑定的微博唯一ID,但用户的可变信息一般不会存。
然后在一些专题页,用到了用户的微博头像和昵称,这时候如果用OAuth协议,需要先获取授权码,再获取token,再调用户信息接口 …

如果你觉得太麻烦,那就来试试笔者的方法。笔者的微博:http://weibo.com/1864997710 ,直接cURL:

curl 'http://weibo.com/1864997710' -v

* About to connect() to weibo.com port 80 (#0)
* Trying 123.125.104.197… connected
> GET /1864997710 HTTP/1.1
> User-Agent: curl/7.22.0 (i686-pc-linux-gnu) libcurl/7.22.0 OpenSSL/1.0.1 zlib/1.2.3.4 libidn/1.23 librtmp/2.3
> Host: weibo.com
> Accept: */*
>
< HTTP/1.1 302 Moved Temporarily < Server: WeiBo < Date: Thu, 18 Sep 2014 02:02:22 GMT < Content-Type: text/html; charset=utf-8 < Transfer-Encoding: chunked < Connection: close < Expires: Sat, 26 Jul 1997 05:00:00 GMT < P3P: CP="CURa ADMa DEVa PSAo PSDo OUR BUS UNI PUR INT DEM STA PRE COM NAV OTC NOI DSP COR" < Cache-Control: no-cache, no-store < Pragma: no-cache < Location: http://passport.weibo.com/visitor/visitor?a=enter&url=http%3A%2F%2Fweibo.com%2F1864997710&_rand=1411005742.125 < DPOOL_HEADER: alice97 < Set-Cookie: TC-Page-G0=4e714161a27175839f5a8e7411c8b98c;Path=/ < LB_HEADER: alice27 < * Closing connection #0

看来直接抓取用户主页的形式不靠谱,因为未登录,微博给你重定向了。

庆幸的是,微博还有一个页面,是不需要登录,而且恰好有微博昵称。
就是访问某人的微博时产生的邀请注册页,页面中会有TA的微博昵称,于是就呵呵了

下面这个函数,可以帮你实现,无需授权无需登录,根据微博ID获取微博昵称

这样做,有什么好处?
1.少写很多OAuth相关代码
2.也降低了多次http的网络消耗
3.实时性(也可以根据需要缓存起来)

【命令】Linux lsof 命令

lsof, LiSt Opened Files, 列出打开的文件, 听起来很简单的样子. 但想*nix中很多其他工具一样, lsof把这件简单的事情做到了炉火纯青. 因为Unix认为”一切皆文件”, 那么”打开的文件”就不仅仅是传统意义上打开的文件了, 还可以是网络/Unix域套接字, 匿名/具名管道, 共享库文件, 目录文件, 设备文件等等. 很多场景下, 查看进程或系统打开的文件会给调试带来极大的帮助. 下面简单地介绍lsof常被使用的功能选项.

lsof : 简单地执行lsof会列出当前系统中所有被打开的文件, 但为了看到完整的信息, 通常需要具有root权限;
lsof -u dutor : 列出用户dutor打开的文件, 可指定多个用户, 默认是OR的关系;
lsof -c tair : 列出名称以tair开头的进程打开的文件, c for command, 可指定多个;
lsof -c /^t.*r$/ : 列出名称以t开头, r结尾的进程打开的文件;
lsof -p 12315 : 列出进程号为12315的进程打开的文件, 可指定多个;
lsof server.log : 列出打开server.log文件的进程, 可指明多个文件;
lsof . : 列出打开当前目录的进程;
lsof +D . : 递归地列出当前目录中被打开的文件, 当然也可以lsof | grep `pwd`;
lsof -i : 列出打开的套接字;
lsof -i tcp : 列出打开的tcp套接字;
lsof -i :5198 : 列出打开5198端口的进程;
lsof -i :ssh : 列出打开22端口的进程;
lsof -i tcp:5198 : 列出打开5198号tcp端口的进程;
lsof -U : 列出打开Unix域套接字的进程;
lsof -d 0-2 : 列出在0到2文件描述符上打开文件的进程;
lsof -d mem : 列出打开映射文件的进程;
lsof -d txt : 列出打开的可执行文件.
还有其他一些非常有用的选项, 可以对lsof的行为进行控制.

lsof -a: 上述功能性选项可以组合使用, 但默认采用OR逻辑列出, -a选项令lsof使用AND逻辑;
lsof -t: 只列出进程号, 可以借此得到特定的进程列表, 以方便对这些进程的自动处理, 比如kill `lsof -t -i :5198`会杀死所有打开5198端口的进程;
lsof -r [seconds]: -r选项可以让lsof以一定的时间间隔连续执行, 在监视文件/进程时会非常实用.

这只是一个实用的不完全选项的罗列, 如果你知道其他非常实用的选项, 还望留言分享之

微信公众号:程序员到架构师

最新文章

Return Top