<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>陈钢的博客</title>
	<atom:link href="http://blog.yikuyiku.com/index.php?feed=rss2" rel="self" type="application/rss+xml" />
	<link>http://blog.yikuyiku.com</link>
	<description>记录生活和技术的点滴，想起来就写点</description>
	<lastBuildDate>Thu, 03 May 2012 09:18:05 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.3.2</generator>
		<item>
		<title>【 转载 】组织形式的悖论及其解决</title>
		<link>http://blog.yikuyiku.com/?p=3081</link>
		<comments>http://blog.yikuyiku.com/?p=3081#comments</comments>
		<pubDate>Thu, 03 May 2012 09:10:33 +0000</pubDate>
		<dc:creator>chen</dc:creator>
				<category><![CDATA[备忘]]></category>
		<category><![CDATA[转载]]></category>

		<guid isPermaLink="false">http://blog.yikuyiku.com/?p=3081</guid>
		<description><![CDATA[德鲁克在《 21世纪的管理挑战 》中这样说到组织形式： 所谓的唯一一种恰当的组织形式是不存在的，而只能是多种多样的，每个组织形式都有其独特的优势、局限性以及特定的应用方式。我们认识到，组织形式不是绝对的，它是提高人们在一起工作的效率的工具。同样，一个特定的组织结构是与特定条件和特定时间内执行的特定任务相匹配的。 任何组织都要有能拍板的负责人，即所谓的“老板”，他可以做出最终的决策，可以要求其他人执行这些决策。⋯⋯在有些情况下组织需要深谋远虑，而在有些情况下则需要团队协作。 听起来是很有道理的权衡。然而这背后隐藏着一种险恶的可能性——如果它不是那么显而易见的话，把它简化描述为下面的形式会更容易看清： 大事我说了算，小事你说了算，事大事小我说了算。 对于经验更丰富、在企业中更受重视的高级管理者来说，这是一条很陡的斜坡，因为当他决定“拍板”时，其他人既没有能力也没有权力来批评他，甚至于没有正式的渠道来提出反馈说他的决定以及做出决定的时机与方式是否恰当。职业经理人在这种情况下也许能处理得更好，而从一线打拼起来的领导者很容易因此进入一言堂的状态。 因此运转良好的、兼容了团队协作与果断拍板的组织形式，必须有一种与之相应的反馈机制，使领导者知道其领导工作是否行之有效。重点在于，为了使这种反馈及时和有效，它不应该来自领导者的领导者，而是应该来自于领导行为的受者：被领导者。 一言以蔽之，领导者的绩效固然来自于企业之外，然而针对领导行为的反馈却应该来自于其下属。 设计这样的一种反馈机制需要两件要素。首先是共享上下文，拥有共同的上下文之后才能理解决策及其结果，因此诸如财务收支、经营目标、客户关系、企业战略、风险危机、人事变动之类的信息，不仅不能锁在小会议室里，而且要强迫地塞进所有人脑子里。然后是赋予优先级，领导者必须将其领导行为的改进视作最高优先级的工作，为此需要从激励方式上确保这一优先级——如果薪酬是唯一的激励方式，那么就意味着领导者的薪酬（至少部分地）由被领导者决定。 建立一种旨在帮助领导者改进并实质上限制领导者权力的反馈机制，这是第一件需要领导者发挥其深谋远虑来“拍板”的事。 @gigix写的，原文链接http://gigix.thoughtworkers.org/2012/5/3/organization-form。 © chen for 陈钢的博客, 2012. &#124; 原文链接 &#124; 备忘, 转载 Post tags: 转载]]></description>
			<content:encoded><![CDATA[<p>德鲁克在《 21世纪的管理挑战 》中这样说到组织形式：</p>
<blockquote><p>
    所谓的唯一一种恰当的组织形式是不存在的，而只能是多种多样的，每个组织形式都有其独特的优势、局限性以及特定的应用方式。我们认识到，组织形式不是绝对的，它是提高人们在一起工作的效率的工具。同样，一个特定的组织结构是与特定条件和特定时间内执行的特定任务相匹配的。<br />
    任何组织都要有能拍板的负责人，即所谓的“老板”，他可以做出最终的决策，可以要求其他人执行这些决策。⋯⋯在有些情况下组织需要深谋远虑，而在有些情况下则需要团队协作。
</p></blockquote>
<p>听起来是很有道理的权衡。然而这背后隐藏着一种险恶的可能性——如果它不是那么显而易见的话，把它简化描述为下面的形式会更容易看清：</p>
<blockquote><p>
    大事我说了算，小事你说了算，事大事小我说了算。
</p></blockquote>
<p>对于经验更丰富、在企业中更受重视的高级管理者来说，这是一条很陡的斜坡，因为当他决定“拍板”时，其他人既没有能力也没有权力来批评他，甚至于没有正式的渠道来提出反馈说他的决定以及做出决定的时机与方式是否恰当。职业经理人在这种情况下也许能处理得更好，而从一线打拼起来的领导者很容易因此进入一言堂的状态。</p>
<p>因此运转良好的、兼容了团队协作与果断拍板的组织形式，必须有一种与之相应的反馈机制，使领导者知道其领导工作是否行之有效。重点在于，为了使这种反馈及时和有效，它不应该来自领导者的领导者，而是应该来自于领导行为的受者：被领导者。</p>
<p>一言以蔽之，领导者的绩效固然来自于企业之外，然而针对领导行为的反馈却应该来自于其下属。</p>
<p>设计这样的一种反馈机制需要两件要素。首先是共享上下文，拥有共同的上下文之后才能理解决策及其结果，因此诸如财务收支、经营目标、客户关系、企业战略、风险危机、人事变动之类的信息，不仅不能锁在小会议室里，而且要强迫地塞进所有人脑子里。然后是赋予优先级，领导者必须将其领导行为的改进视作最高优先级的工作，为此需要从激励方式上确保这一优先级——如果薪酬是唯一的激励方式，那么就意味着领导者的薪酬（至少部分地）由被领导者决定。</p>
<p>建立一种旨在帮助领导者改进并实质上限制领导者权力的反馈机制，这是第一件需要领导者发挥其深谋远虑来“拍板”的事。</p>
<p>@gigix写的，原文链接<a href="http://gigix.thoughtworkers.org/2012/5/3/organization-form" title="http://gigix.thoughtworkers.org/2012/5/3/organization-form">http://gigix.thoughtworkers.org/2012/5/3/organization-form</a>。</p>
<hr />
<p><small>© chen for <a href="http://blog.yikuyiku.com">陈钢的博客</a>, 2012. |
<a href="http://blog.yikuyiku.com/?p=3081">原文链接</a> |
<a href="http://blog.yikuyiku.com/?p=3081#comments"></a> 
<a href="http://blog.yikuyiku.com/?cat=3" title="查看 备忘 中的全部文章" rel="category">备忘</a>, <a href="http://blog.yikuyiku.com/?cat=10" title="查看 转载 中的全部文章" rel="category">转载</a>
<br/>
Post tags: <a href="http://blog.yikuyiku.com/?tag=%e8%bd%ac%e8%bd%bd" rel="tag">转载</a>
<br/>
</small></p>]]></content:encoded>
			<wfw:commentRss>http://blog.yikuyiku.com/?feed=rss2&#038;p=3081</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>汉代诗歌</title>
		<link>http://blog.yikuyiku.com/?p=3075</link>
		<comments>http://blog.yikuyiku.com/?p=3075#comments</comments>
		<pubDate>Wed, 25 Apr 2012 23:22:04 +0000</pubDate>
		<dc:creator>chen</dc:creator>
				<category><![CDATA[读书]]></category>

		<guid isPermaLink="false">http://blog.yikuyiku.com/?p=3075</guid>
		<description><![CDATA[楚辞遗韵 我所思兮在泰山，欲往从之梁父艰，侧身望泪沾翰。 美人赠我金错刀，何以报之英琼瑶。 路远莫致倚逍遥，何为怀忧心烦劳。 —— 张衡《四愁诗》 大风起兮云飞扬，威加海内兮归故乡，安得猛士兮守四方！ —— 汉高祖《大风歌》 力拔山兮气盖世。 时不利兮骓（zhuī）不逝。 骓不逝兮可奈何！ 虞兮虞兮奈若何！ —— 项羽《垓下歌》 乐府民歌 男儿爱后妇，女子重前夫。 人生有新旧，贵贱不相逾。 多谢金吾子，私爱徒区区。 —— 辛延年《羽林郎》 上邪！ 我欲与君相知， 长命无绝衰。 山无陵， 江水为竭， 冬雷震震夏雨雪， 天地合， 乃敢与君绝！ —— 汉乐府《上邪》 五言萌芽 北方有佳人，绝世而独立。 一顾倾人城，再顾倾人国。 宁不知倾城与倾国？佳人难再得！ —— 李延年《李夫人歌》 行行重行行，与君生别离。 相去万余里，各在天一涯; 道路阻且长，会面安可知?　 胡马依北风，越鸟巢南枝。 相去日已远，衣带日已缓; 浮云蔽白日，游子不顾反。 思君令人老，岁月忽已晚。 弃捐勿复道，努力加餐饭! —— 古诗十九首 © chen for 陈钢的博客, 2012. &#124; 原文链接 &#124; [...]]]></description>
			<content:encoded><![CDATA[<h2>楚辞遗韵</h2>
<p>我所思兮在泰山，欲往从之梁父艰，侧身望泪沾翰。<br />
美人赠我金错刀，何以报之英琼瑶。<br />
路远莫致倚逍遥，何为怀忧心烦劳。<br />
—— 张衡《四愁诗》</p>
<p>大风起兮云飞扬，威加海内兮归故乡，安得猛士兮守四方！<br />
—— 汉高祖《大风歌》</p>
<p>力拔山兮气盖世。<br />
时不利兮骓（zhuī）不逝。<br />
骓不逝兮可奈何！<br />
虞兮虞兮奈若何！<br />
—— 项羽《垓下歌》</p>
<h2>乐府民歌</h2>
<p>男儿爱后妇，女子重前夫。<br />
人生有新旧，贵贱不相逾。<br />
多谢金吾子，私爱徒区区。<br />
—— 辛延年《羽林郎》</p>
<p>上邪！<br />
我欲与君相知，<br />
长命无绝衰。<br />
山无陵，<br />
江水为竭，<br />
冬雷震震夏雨雪，<br />
天地合，<br />
乃敢与君绝！<br />
—— 汉乐府《上邪》</p>
<h2>五言萌芽</h2>
<p>北方有佳人，绝世而独立。<br />
一顾倾人城，再顾倾人国。<br />
宁不知倾城与倾国？佳人难再得！<br />
—— 李延年《李夫人歌》</p>
<p>行行重行行，与君生别离。<br />
相去万余里，各在天一涯;<br />
道路阻且长，会面安可知?　<br />
胡马依北风，越鸟巢南枝。<br />
相去日已远，衣带日已缓;<br />
浮云蔽白日，游子不顾反。<br />
思君令人老，岁月忽已晚。<br />
弃捐勿复道，努力加餐饭!<br />
—— 古诗十九首</p>
<hr />
<p><small>© chen for <a href="http://blog.yikuyiku.com">陈钢的博客</a>, 2012. |
<a href="http://blog.yikuyiku.com/?p=3075">原文链接</a> |
<a href="http://blog.yikuyiku.com/?p=3075#comments"></a> 
<a href="http://blog.yikuyiku.com/?cat=9" title="查看 读书 中的全部文章" rel="category">读书</a>
<br/>
Post tags: 
<br/>
</small></p>]]></content:encoded>
			<wfw:commentRss>http://blog.yikuyiku.com/?feed=rss2&#038;p=3075</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>ffmpeg错误隐藏</title>
		<link>http://blog.yikuyiku.com/?p=3071</link>
		<comments>http://blog.yikuyiku.com/?p=3071#comments</comments>
		<pubDate>Tue, 24 Apr 2012 07:49:39 +0000</pubDate>
		<dc:creator>chen</dc:creator>
				<category><![CDATA[备忘]]></category>
		<category><![CDATA[ffmpeg]]></category>
		<category><![CDATA[视频]]></category>

		<guid isPermaLink="false">http://blog.yikuyiku.com/?p=3071</guid>
		<description><![CDATA[-ec guess_mvs+deblock guess_mvs 环内 deblock 环外 来自 @洪奎 相关推荐： 【 视频 】x264各类型帧的大小及编码耗时 © chen for 陈钢的博客, 2012. &#124; 原文链接 &#124; 备忘 Post tags: ffmpeg, 视频
相关推荐：<ol>
<li><a href='http://blog.yikuyiku.com/?p=1930' rel='bookmark' title='【 视频 】x264各类型帧的大小及编码耗时'>【 视频 】x264各类型帧的大小及编码耗时</a></li>
</ol>]]></description>
			<content:encoded><![CDATA[<p>-ec guess_mvs+deblock</p>
<p>guess_mvs 环内<br />
deblock 环外</p>
<p>来自 @洪奎</p>
<p>相关推荐：<ol>
<li><a href='http://blog.yikuyiku.com/?p=1930' rel='bookmark' title='【 视频 】x264各类型帧的大小及编码耗时'>【 视频 】x264各类型帧的大小及编码耗时</a></li>
</ol></p><hr />
<p><small>© chen for <a href="http://blog.yikuyiku.com">陈钢的博客</a>, 2012. |
<a href="http://blog.yikuyiku.com/?p=3071">原文链接</a> |
<a href="http://blog.yikuyiku.com/?p=3071#comments"></a> 
<a href="http://blog.yikuyiku.com/?cat=3" title="查看 备忘 中的全部文章" rel="category">备忘</a>
<br/>
Post tags: <a href="http://blog.yikuyiku.com/?tag=ffmpeg" rel="tag">ffmpeg</a>, <a href="http://blog.yikuyiku.com/?tag=%e8%a7%86%e9%a2%91" rel="tag">视频</a>
<br/>
</small></p>]]></content:encoded>
			<wfw:commentRss>http://blog.yikuyiku.com/?feed=rss2&#038;p=3071</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>ffmpeg滤镜简单例程</title>
		<link>http://blog.yikuyiku.com/?p=3059</link>
		<comments>http://blog.yikuyiku.com/?p=3059#comments</comments>
		<pubDate>Tue, 20 Mar 2012 01:58:57 +0000</pubDate>
		<dc:creator>chen</dc:creator>
				<category><![CDATA[备忘]]></category>
		<category><![CDATA[实践]]></category>
		<category><![CDATA[ffmpeg]]></category>
		<category><![CDATA[libavfilter]]></category>

		<guid isPermaLink="false">http://blog.yikuyiku.com/?p=3059</guid>
		<description><![CDATA[写了个滤镜，实现画面反相，哈哈。 最简化，挺能说明滤镜结构的，记一下。 #include "avfilter.h" static void draw_slice(AVFilterLink *inlink, int y, int h, int slice_dir) { AVFilterContext *ctx = inlink->dst; AVFilterLink *outlink = ctx->outputs[0]; AVFilterBufferRef *inpic = inlink ->cur_buf; AVFilterBufferRef *outpic = outlink->out_buf; uint8_t *inrow, *outrow; int i, j, plane; for (plane = 0; plane < 4 &#038;&#038; inpic->data[plane]; plane++) { inrow = inpic ->data[plane] + [...]
相关推荐：<ol>
<li><a href='http://blog.yikuyiku.com/?p=3023' rel='bookmark' title='【 翻译 】FFmpeg filter HOWTO'>【 翻译 】FFmpeg filter HOWTO</a></li>
</ol>]]></description>
			<content:encoded><![CDATA[<p>写了个滤镜，实现画面反相，哈哈。</p>
<p>最简化，挺能说明滤镜结构的，记一下。</p>
<pre class="brush: cpp">
#include "avfilter.h"

static void draw_slice(AVFilterLink *inlink, int y, int h, int slice_dir)
{
    AVFilterContext *ctx = inlink->dst;
    AVFilterLink *outlink = ctx->outputs[0];
    AVFilterBufferRef *inpic  = inlink ->cur_buf;
    AVFilterBufferRef *outpic = outlink->out_buf;
    uint8_t *inrow, *outrow;
    int i, j, plane;

    for (plane = 0; plane < 4 &#038;&#038; inpic->data[plane]; plane++) {
        inrow  = inpic ->data[plane] + y * inpic ->linesize[plane];
        outrow = outpic->data[plane] + y * outpic->linesize[plane];

        for (i = 0; i < h; i ++) {
            for (j = 0; j < inlink->w; j++)
            {
                outrow[j] = 255 - inrow[j];
            }
            inrow  += inpic ->linesize[plane];
            outrow += outpic->linesize[plane];
        }
    }   

    avfilter_draw_slice(outlink, y, h, slice_dir);
}

AVFilter avfilter_vf_tnegate = {
    .name          = "tnegate",
    .description   = NULL_IF_CONFIG_SMALL("tnegate"),
    .inputs    = (const AVFilterPad[]) {{ .name      = "default",
                                    .type            = AVMEDIA_TYPE_VIDEO,
                                    .draw_slice      = draw_slice,
                                    .min_perms       = AV_PERM_READ, },
                                  { .name = NULL}},
    .outputs   = (const AVFilterPad[]) {{ .name      = "default",
                                    .type            = AVMEDIA_TYPE_VIDEO, },
                                  { .name = NULL}},
};
</pre>
<p>加上priv数据的版本：</p>
<pre class="brush: cpp">
#include "avfilter.h"

typedef struct {
    int ts;
} My3dContext;

static void draw_slice(AVFilterLink *inlink, int y, int h, int slice_dir)
{
    AVFilterContext *ctx = inlink->dst;

    My3dContext * my3d = ctx->priv;
    my3d->ts++;
    av_log(ctx,AV_LOG_ERROR,"[%d]\n", my3d->ts);

    AVFilterLink *outlink = ctx->outputs[0];
    AVFilterBufferRef *inpic  = inlink ->cur_buf;
    AVFilterBufferRef *outpic = outlink->out_buf;
    uint8_t *inrow, *outrow;
    int i, j, plane;

    for (plane = 0; plane < 4 &#038;&#038; inpic->data[plane]; plane++) {
        inrow  = inpic ->data[plane] + y * inpic ->linesize[plane];
        outrow = outpic->data[plane] + y * outpic->linesize[plane];

        for (i = 0; i < h; i ++) {
            for (j = 0; j < inlink->w; j++)
            {
                outrow[j] = 255 - inrow[j];
            }
            inrow  += inpic ->linesize[plane];
            outrow += outpic->linesize[plane];
        }
    }   

    avfilter_draw_slice(outlink, y, h, slice_dir);
}

static int init(AVFilterContext *ctx, const char *args, void *opaque)
{
    My3dContext * my3d = ctx->priv;
    my3d->ts = 0;
    return 0;
}

AVFilter avfilter_vf_my3d = {
    .name          = "my3d",
    .description   = NULL_IF_CONFIG_SMALL("my3d"),
    .priv_size = sizeof(My3dContext),

    .init      = init,
    .inputs    = (const AVFilterPad[]) {{ .name      = "default",
                                    .type            = AVMEDIA_TYPE_VIDEO,
                                    .draw_slice      = draw_slice,
                                    .min_perms       = AV_PERM_READ, },
                                  { .name = NULL}},
    .outputs   = (const AVFilterPad[]) {{ .name      = "default",
                                    .type            = AVMEDIA_TYPE_VIDEO, },
                                  { .name = NULL}},
};
</pre>
<p>读取参数的版本（这样用-vf “hqdn3d,tnegate=arg1:arg2:arg3&#8243;）：</p>
<pre class="brush: cpp">
#include "avfilter.h"
#include "libavutil/avstring.h"
#include "libavutil/opt.h"

typedef struct {
    int ts;
    char * arg1;
    char * arg2;
    char * arg3;
} My3dContext;

static void draw_slice(AVFilterLink *inlink, int y, int h, int slice_dir)
{
    AVFilterContext *ctx = inlink->dst;

    My3dContext * my3d = ctx->priv;
    my3d->ts++;
    av_log(ctx,AV_LOG_ERROR,"[%d]\n", my3d->ts);
    av_log(ctx,AV_LOG_ERROR,"[%s][%s][%s]\n", my3d->arg1, my3d->arg2, my3d->arg3);

    AVFilterLink *outlink = ctx->outputs[0];
    AVFilterBufferRef *inpic  = inlink ->cur_buf;
    AVFilterBufferRef *outpic = outlink->out_buf;
    uint8_t *inrow, *outrow;
    int i, j, plane;

    for (plane = 0; plane < 4 &#038;&#038; inpic->data[plane]; plane++) {
        inrow  = inpic ->data[plane] + y * inpic ->linesize[plane];
        outrow = outpic->data[plane] + y * outpic->linesize[plane];

        for (i = 0; i < h; i ++) {
            for (j = 0; j < inlink->w; j++)
            {
                outrow[j] = 255 - inrow[j];
            }
            inrow  += inpic ->linesize[plane];
            outrow += outpic->linesize[plane];
        }
    }   

    avfilter_draw_slice(outlink, y, h, slice_dir);
}

static int init(AVFilterContext *ctx, const char *args, void *opaque)
{
    My3dContext * my3d = ctx->priv;

    char *args1 = av_strdup(args);
    char *expr, *bufptr = NULL;
    int ret = 0;

    my3d->ts = 0;

    if (expr = av_strtok(args1, ":", &#038;bufptr)) {
        av_free(my3d->arg1);
        if (!(my3d->arg1 = av_strdup(expr))) {
            ret = AVERROR(ENOMEM);
            goto end;
        }
    }
    if (expr = av_strtok(NULL, ":", &#038;bufptr)) {
        av_free(my3d->arg2);
        if (!(my3d->arg2 = av_strdup(expr))) {
            ret = AVERROR(ENOMEM);
            goto end;
        }
    }
    if (expr = av_strtok(NULL, ":", &#038;bufptr)) {
        av_free(my3d->arg3);
        if (!(my3d->arg3 = av_strdup(expr))) {
            ret = AVERROR(ENOMEM);
            goto end;
        }
    }
    if (bufptr &#038;&#038; (ret = av_set_options_string(my3d, bufptr, "=", ":")) < 0)
        goto end;

end:
    av_free(args1);
    return ret;
}

AVFilter avfilter_vf_tnegate = {
    .name          = "tnegate",
    .description   = NULL_IF_CONFIG_SMALL("tnegate"),
    .priv_size = sizeof(My3dContext),

    .init      = init,
    .inputs    = (const AVFilterPad[]) {{ .name      = "default",
                                    .type            = AVMEDIA_TYPE_VIDEO,
                                    .draw_slice      = draw_slice,
                                    .min_perms       = AV_PERM_READ, },
                                  { .name = NULL}},
    .outputs   = (const AVFilterPad[]) {{ .name      = "default",
                                    .type            = AVMEDIA_TYPE_VIDEO, },
                                  { .name = NULL}},
};
</pre>
<p>相关推荐：<ol>
<li><a href='http://blog.yikuyiku.com/?p=3023' rel='bookmark' title='【 翻译 】FFmpeg filter HOWTO'>【 翻译 】FFmpeg filter HOWTO</a></li>
</ol></p><hr />
<p><small>© chen for <a href="http://blog.yikuyiku.com">陈钢的博客</a>, 2012. |
<a href="http://blog.yikuyiku.com/?p=3059">原文链接</a> |
<a href="http://blog.yikuyiku.com/?p=3059#comments">% 条评论</a> 
<a href="http://blog.yikuyiku.com/?cat=3" title="查看 备忘 中的全部文章" rel="category">备忘</a>, <a href="http://blog.yikuyiku.com/?cat=4" title="查看 实践 中的全部文章" rel="category">实践</a>
<br/>
Post tags: <a href="http://blog.yikuyiku.com/?tag=ffmpeg" rel="tag">ffmpeg</a>, <a href="http://blog.yikuyiku.com/?tag=libavfilter" rel="tag">libavfilter</a>
<br/>
</small></p>]]></content:encoded>
			<wfw:commentRss>http://blog.yikuyiku.com/?feed=rss2&#038;p=3059</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Perl语言中的协程 ——“Coro”简介</title>
		<link>http://blog.yikuyiku.com/?p=3042</link>
		<comments>http://blog.yikuyiku.com/?p=3042#comments</comments>
		<pubDate>Mon, 27 Feb 2012 15:10:22 +0000</pubDate>
		<dc:creator>chen</dc:creator>
				<category><![CDATA[实践]]></category>
		<category><![CDATA[翻译]]></category>
		<category><![CDATA[Coro]]></category>
		<category><![CDATA[perl]]></category>
		<category><![CDATA[poe]]></category>
		<category><![CDATA[协程]]></category>
		<category><![CDATA[线程]]></category>
		<category><![CDATA[进程]]></category>

		<guid isPermaLink="false">http://blog.yikuyiku.com/?p=3042</guid>
		<description><![CDATA[进程、线程和协程 进程是什么应该大家都知道了。我们用ps命令看到的一行就是一个进程。它就是一个独立运行的程序，拥有自己独立的内存空间和信号处理器等一切一个程序该拥有的东西。 线程有点像是一个轻量级的进程。不同的是，线程没有自己独立的内存区域和命名空间，它们所有的东西都是共用的。这意味着如果某线程修改了一个变量，那么其它线程立刻就能看到这个变量的改变。 协程。协程这个词意味着线程之间要互相协作，特别是在CPU资源占用的方面。简单地说，一个CPU同时只能有一个线程可以占用，此时如果另一个线程想要CPU，那么正在运行的线程就必须把CPU让出来。后者可以显式地表示自己希望占用CPU，也可以用等待一个资源（信号量或者IO完成）的方式隐式地来表达。 因为线程比进程轻量，线程模型在脚本语言中是十分流行的（比如python或者ruby，perl里不大流行），而协程模型通常要比线程模型高效很多很多，通常可以达到惊人的数个数量级的效率提升。 Perl中的线程 Perl语言的线程一直没有达到成熟的状态。 Perl本身在线程和进程的术语使用上是略显混乱的。目前Perl的线程实现代码其实是它用于Windows平台的Unix进程模拟器的代码，所以实际上它们更像是进程而非线程。最明显的的问题是：在Perl的线程中变量“不是”共享的。 Coro给我们带来了“协程” 而Coro这个模块带给我们的就是比线程更加高效的协程模型。这样我们就再也不用忍受Perl那蹩脚的线程实现了。 Coro用法一瞥 像其它Perl的模块一样，想要用Coro，我们需要先use它： use Coro; 然后我们就可以用async方法来创建一个协程： async { print "hello\n"; }; async方法的第一个参数是一个语句块，我们也可以在后面继续写其它参数，那些参数会被放在变量@_中传给语句块。 但是如果你就这样把上面的代码保存成文件并执行了，你会发现屏幕上并没有输出&#8217;hello&#8217;。 让我们来解释一下这事。 在我们use Coro之后，我们的主程序就变成了一个协程，和其它协程一样，只不过在程序开始运行的时候它首先占用着CPU。所以虽然我们创建了一个协程，但它只是被放到队列中了，如果主程序不释放对CPU的控制，那么它就没有办法拿到CPU资源去执行它自己。 我们可以使用cede方法来主动释放自己对CPU的占用（在其它协程的实现中此方法通常叫做yield）： use Coro; async { print "hello\n"; }; cede; 这样屏幕上就会输出hello了。 更有趣的例子 我们来看一个更有趣的例子： use Coro; async { print "async 1\n"; cede; print "async 2\n"; }; print "main 1\n"; cede; print "main [...]
相关推荐：<ol>
<li><a href='http://blog.yikuyiku.com/?p=1595' rel='bookmark' title='【 Perl 】perl 5.12新特性中的yada yada操作符怎么用'>【 Perl 】perl 5.12新特性中的yada yada操作符怎么用</a></li>
<li><a href='http://blog.yikuyiku.com/?p=1640' rel='bookmark' title='【 HTML 】让新打开的_blank链接共用一个标签'>【 HTML 】让新打开的_blank链接共用一个标签</a></li>
<li><a href='http://blog.yikuyiku.com/?p=1726' rel='bookmark' title='【 Perl 】利用eval写出健壮的后台服务程序'>【 Perl 】利用eval写出健壮的后台服务程序</a></li>
<li><a href='http://blog.yikuyiku.com/?p=1259' rel='bookmark' title='《Advandced Perl Programming》第七章、POE(1)'>《Advandced Perl Programming》第七章、POE(1)</a></li>
<li><a href='http://blog.yikuyiku.com/?p=2005' rel='bookmark' title='欺骗Ipad让它播放High Profile的h264视频'>欺骗Ipad让它播放High Profile的h264视频</a></li>
</ol>]]></description>
			<content:encoded><![CDATA[<h2>进程、线程和协程</h2>
<p>进程是什么应该大家都知道了。我们用ps命令看到的一行就是一个进程。它就是一个独立运行的程序，拥有自己独立的内存空间和信号处理器等一切一个程序该拥有的东西。</p>
<p>线程有点像是一个轻量级的进程。不同的是，线程没有自己独立的内存区域和命名空间，它们所有的东西都是共用的。这意味着如果某线程修改了一个变量，那么其它线程立刻就能看到这个变量的改变。</p>
<p>协程。协程这个词意味着线程之间要互相协作，特别是在CPU资源占用的方面。简单地说，一个CPU同时只能有一个线程可以占用，此时如果另一个线程想要CPU，那么正在运行的线程就必须把CPU让出来。后者可以显式地表示自己希望占用CPU，也可以用等待一个资源（信号量或者IO完成）的方式隐式地来表达。</p>
<p>因为线程比进程轻量，线程模型在脚本语言中是十分流行的（比如python或者ruby，perl里不大流行），而协程模型通常要比线程模型高效很多很多，通常可以达到惊人的数个数量级的效率提升。 </p>
<p><br/></p>
<h2>Perl中的线程</h2>
<p>Perl语言的线程一直没有达到成熟的状态。</p>
<p>Perl本身在线程和进程的术语使用上是略显混乱的。目前Perl的线程实现代码其实是它用于Windows平台的Unix进程模拟器的代码，所以实际上它们更像是进程而非线程。最明显的的问题是：在Perl的线程中变量“不是”共享的。</p>
<p><br/></p>
<h2>Coro给我们带来了“协程”</h2>
<p>而Coro这个模块带给我们的就是比线程更加高效的协程模型。这样我们就再也不用忍受Perl那蹩脚的线程实现了。</p>
<p><br/></p>
<h2>Coro用法一瞥</h2>
<p>像其它Perl的模块一样，想要用Coro，我们需要先use它：</p>
<pre class="brush: perl">
use Coro;
</pre>
<p>然后我们就可以用async方法来创建一个协程：</p>
<pre class="brush: perl">
   async {
      print "hello\n";
   };
</pre>
<p>async方法的第一个参数是一个语句块，我们也可以在后面继续写其它参数，那些参数会被放在变量@_中传给语句块。</p>
<p>但是如果你就这样把上面的代码保存成文件并执行了，你会发现屏幕上并没有输出&#8217;hello&#8217;。</p>
<p>让我们来解释一下这事。</p>
<p>在我们use Coro之后，我们的主程序就变成了一个协程，和其它协程一样，只不过在程序开始运行的时候它首先占用着CPU。所以虽然我们创建了一个协程，但它只是被放到队列中了，如果主程序不释放对CPU的控制，那么它就没有办法拿到CPU资源去执行它自己。</p>
<p>我们可以使用cede方法来主动释放自己对CPU的占用（在其它协程的实现中此方法通常叫做yield）：</p>
<pre class="brush: perl">
   use Coro;

   async {
      print "hello\n";
   };  

   cede;
</pre>
<p>这样屏幕上就会输出hello了。</p>
<p><br/></p>
<h2>更有趣的例子</h2>
<p>我们来看一个更有趣的例子：</p>
<pre class="brush: perl">
   use Coro;

   async {
      print "async 1\n";
      cede;
      print "async 2\n";
   };

   print "main 1\n";
   cede;
   print "main 2\n";
   cede;
</pre>
<p>这段代码会输出：</p>
<pre class="brush: perl">
   main 1
   async 1
   main 2
   async 2
</pre>
<p>这个例子很好说明了Coro是如何在不用的协程之间协商CPU资源的，其是它带给我们的也就是多路复用的能力。</p>
<p><br/></p>
<h2>说一点点细节</h2>
<p>如果你还感兴趣的话，我们再说说更多细节。</p>
<p>我们传一段代码给async方法之后，它其实做了2个操作：首先创建一个协程，而后将其放入ready队列。</p>
<p>每当一个协程释放了CPU，Coro就会运行调度器（scheduler方法），这个方法会负责找到ready队列中下一个等待CPU资源协程，将它移除出队列并执行它。</p>
<p>cede方法也做了2件事：首先把当前协程推入ready队列，而后运行scheduler。这样同样也会起到释放CPU的效果。事实上，cede的实现完全可以是这样的：</p>
<pre class="brush: perl">
   sub my_cede {
      $Coro::current->ready;
      schedule;
   }
</pre>
<p>让我们简单说明一下，$Coro::current变量里总是存放着当前正在运行的协程。scheduler方法会调用Coro::schedule。</p>
<p>那么，如果在把自己放入ready队列之前就调用了scheduler会怎样呢？scheduler在ready队列中找到下一个协程弄出来运行。当前的这个协程呢，会sleep到有什么东西把它唤醒，如果有的话。</p>
<p><br/></p>
<h2>开始Coro之旅</h2>
<p>如果你看到这里都还没有放弃的话，那么就赶紧进入<a href="http://search.cpan.org/~mlehmann/Coro-6.07/Coro.pm" title="Coro">http://search.cpan.org/~mlehmann/Coro-6.07/Coro.pm</a>开始你的Coro之旅吧！</p>
<p>本文部分翻译自http://search.cpan.org/~mlehmann/Coro-6.07/Coro/Intro.pod，有删节和修改。</p>
<p>相关推荐：<ol>
<li><a href='http://blog.yikuyiku.com/?p=1595' rel='bookmark' title='【 Perl 】perl 5.12新特性中的yada yada操作符怎么用'>【 Perl 】perl 5.12新特性中的yada yada操作符怎么用</a></li>
<li><a href='http://blog.yikuyiku.com/?p=1640' rel='bookmark' title='【 HTML 】让新打开的_blank链接共用一个标签'>【 HTML 】让新打开的_blank链接共用一个标签</a></li>
<li><a href='http://blog.yikuyiku.com/?p=1726' rel='bookmark' title='【 Perl 】利用eval写出健壮的后台服务程序'>【 Perl 】利用eval写出健壮的后台服务程序</a></li>
<li><a href='http://blog.yikuyiku.com/?p=1259' rel='bookmark' title='《Advandced Perl Programming》第七章、POE(1)'>《Advandced Perl Programming》第七章、POE(1)</a></li>
<li><a href='http://blog.yikuyiku.com/?p=2005' rel='bookmark' title='欺骗Ipad让它播放High Profile的h264视频'>欺骗Ipad让它播放High Profile的h264视频</a></li>
</ol></p><hr />
<p><small>© chen for <a href="http://blog.yikuyiku.com">陈钢的博客</a>, 2012. |
<a href="http://blog.yikuyiku.com/?p=3042">原文链接</a> |
<a href="http://blog.yikuyiku.com/?p=3042#comments"></a> 
<a href="http://blog.yikuyiku.com/?cat=4" title="查看 实践 中的全部文章" rel="category">实践</a>, <a href="http://blog.yikuyiku.com/?cat=8" title="查看 翻译 中的全部文章" rel="category">翻译</a>
<br/>
Post tags: <a href="http://blog.yikuyiku.com/?tag=coro" rel="tag">Coro</a>, <a href="http://blog.yikuyiku.com/?tag=perl" rel="tag">perl</a>, <a href="http://blog.yikuyiku.com/?tag=poe" rel="tag">poe</a>, <a href="http://blog.yikuyiku.com/?tag=%e5%8d%8f%e7%a8%8b" rel="tag">协程</a>, <a href="http://blog.yikuyiku.com/?tag=%e7%ba%bf%e7%a8%8b" rel="tag">线程</a>, <a href="http://blog.yikuyiku.com/?tag=%e8%bf%9b%e7%a8%8b" rel="tag">进程</a>
<br/>
</small></p>]]></content:encoded>
			<wfw:commentRss>http://blog.yikuyiku.com/?feed=rss2&#038;p=3042</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>【 翻译 】FFmpeg filter HOWTO</title>
		<link>http://blog.yikuyiku.com/?p=3023</link>
		<comments>http://blog.yikuyiku.com/?p=3023#comments</comments>
		<pubDate>Wed, 22 Feb 2012 07:52:28 +0000</pubDate>
		<dc:creator>chen</dc:creator>
				<category><![CDATA[实践]]></category>
		<category><![CDATA[翻译]]></category>
		<category><![CDATA[ffmpeg]]></category>
		<category><![CDATA[libavfilter]]></category>
		<category><![CDATA[滤镜]]></category>

		<guid isPermaLink="false">http://blog.yikuyiku.com/?p=3023</guid>
		<description><![CDATA[定义一个滤镜 AVFilter 所有我们写的滤镜都要用一个AVFilter结构体讲给ffmpeg听。 这个结构体里描述了ffmpeg从哪个方法进入我们的滤镜。 这个结构体在libavfilter/avfilter.h里如下定义： typedef struct { char *name; ///< 滤镜名称 int priv_size; ///< 给滤镜分配的内存大小 int (*init)(AVFilterContext *ctx, const char *args, void *opaque); void (*uninit)(AVFilterContext *ctx); int (*query_formats)(AVFilterContext *ctx); const AVFilterPad *inputs; ///< 一系列输入 NULL terminated list of inputs. NULL if none const AVFilterPad *outputs; ///< 一系列输出 NULL terminated list of outputs. NULL if [...]]]></description>
			<content:encoded><![CDATA[<h2>定义一个滤镜</h2>
<p><strong>AVFilter</strong></p>
<p>所有我们写的滤镜都要用一个AVFilter结构体讲给ffmpeg听。 这个结构体里描述了ffmpeg从哪个方法进入我们的滤镜。 这个结构体在libavfilter/avfilter.h里如下定义：</p>
<pre class="brush: cpp">
typedef struct
{
    char *name;         ///< 滤镜名称

    int priv_size;      ///< 给滤镜分配的内存大小

    int (*init)(AVFilterContext *ctx, const char *args, void *opaque);
    void (*uninit)(AVFilterContext *ctx);

    int (*query_formats)(AVFilterContext *ctx);

    const AVFilterPad *inputs;  ///< 一系列输入 NULL terminated list of inputs. NULL if none
    const AVFilterPad *outputs; ///< 一系列输出 NULL terminated list of outputs. NULL if none
} AVFilter;
</pre>
<p>“query_formats”方法用于设置可以接受的输入图像格式和输出的图像格式（用于滤镜链分辨哪些滤镜可以组合在一起用）。</p>
<p><strong>AVFilterPad</strong></p>
<p>这个滤镜用于描述滤镜的输入输出，在libavfilter/avfilter.h中定义如下：</p>
<pre class="brush: cpp">
typedef struct AVFilterPad
{
    char *name;
    int type;

    int min_perms;
    int rej_perms;

    void (*start_frame)(AVFilterLink *link, AVFilterPicRef *picref);
    AVFilterPicRef *(*get_video_buffer)(AVFilterLink *link, int perms);
    void (*end_frame)(AVFilterLink *link);
    void (*draw_slice)(AVFilterLink *link, int y, int height);

    int (*request_frame)(AVFilterLink *link);

    int (*config_props)(AVFilterLink *link);
} AVFilterPad;
</pre>
<p>头文件里有十分具体的描述，这里大概解释如下：</p>
<p>输入输出pad都可以用的元素：<br />
name	pad的名字，所有的输入pad名字不能重复，所有的输出名字不能重复；<br />
type	此元素目前只能为“AV_PAD_VIDEO”值<br />
config_props	链接此pad的配置方法的函数指针</p>
<p>仅限输入pad使用的元素：<br />
min_perms	接受输入需要的最小权限<br />
rej_perms	不接受的输入权限<br />
start_frame	一帧传入时引用的方法的函数指针<br />
draw_slice	每个slice已经传入后引用的方法的函数指针<br />
end_frame	一帧完整结束后引用的方法的函数指针<br />
get_video_buffer	前一个滤镜调用，用以为一个图像请求内存</p>
<p>仅限输出pad使用的元素：<br />
request_frame 请求滤镜输出一帧</p>
<p><br/></p>
<h2>图像缓冲</h2>
<p><strong>引用计数</strong></p>
<p>滤镜系统使用引用计数。意味着存在一个buffer里面存放着图像数据，而所有的滤镜都各自保有一个指向这个buffer的引用。当每一个滤镜完事，它就释放自己的那个引用。这样当所有的引用都释放以后，滤镜系统就会自动把buffer释放掉。</p>
<p><strong>权限</strong></p>
<p>由于可能有多个滤镜都保有了buffer的指针，它们可能会同时操作buffer而造成冲突，因此ffmpeg引入了权限系统。<br />
大多数情况下，当一个滤镜准备输出一帧时，它调用滤镜链上下一个滤镜的一个方法来请求一个buffer。这指定了它对这个buffer需要的最低权限，不过可能实际被赋予的权限可能比要求的要高。<br />
在想要把buffer输出给另一个滤镜时，会新建一个新的指向这个图像的引用，可能是一个权限标记的子集。这个新的引用属于接受buffer的滤镜。<br />
举例说：一个丢帧的滤镜在输出最后一帧时，他可能在输出之后还是想要保持一个指向图像的引用，以确保没有其它滤镜同时修改这个buffer。为了达到这个目的，他可能请给自己求AV_PERM_READ|AV_PERM_WRITE|AV_PERM_PRESERVE权限，然后在给予其它滤镜的引用里去掉AV_PERM_WRITE权限。</p>
<p>可用的权限有：<br />
AV_PERM_READ	可以读取图像数据<br />
AV_PERM_WRITE	可以写入图像数据<br />
AV_PERM_PRESERVE	保证图像数据不会被其它滤镜修改，意味着不会有其它滤镜拿到AV_PERM_WRITE权限<br />
AV_PERM_REUSE	滤镜可能往一段buffer多次输出，但图像数据不得切换到不同的输出<br />
AV_PERM_REUSE2	滤镜可能往一段buffer多次输出，可能在不同的输出之间修改图像数据</p>
<p><br/></p>
<h2>滤镜链</h2>
<p>滤镜的输入输出用“AVFilterLink”结构体和其它滤镜相连接：</p>
<pre class="brush: cpp">
typedef struct AVFilterLink
{
    AVFilterContext *src;       ///< source filter
    unsigned int srcpad;        ///< index of the output pad on the source filter

    AVFilterContext *dst;       ///< dest filter
    unsigned int dstpad;        ///< index of the input pad on the dest filter

    int w;                      ///< agreed upon image width
    int h;                      ///< agreed upon image height
    enum PixelFormat format;    ///< agreed upon image colorspace

    AVFilterFormats *in_formats;    ///< formats supported by source filter
    AVFilterFormats *out_formats;   ///< formats supported by destination filter

    AVFilterPicRef *srcpic;

    AVFilterPicRef *cur_pic;
    AVFilterPicRef *outpic;
};
</pre>
<p>成员“src”和“dst”分别指出滤镜在链上的输入和输出的结束。“srcpad”指向链条上一个“源滤镜”的输出面的索引；类似的，“dstpad”指向目标滤镜的输入面的索引。<br />
成员“in_formats”指向“源滤镜”定义的它支持的格式，“out_formats”指向“目标滤镜”支持的格式。结构体“AVFilterFormats”用于存储支持格式的列表，它使用引用计数，跟踪它的引用（参见libavfilter/avfilter.h中关于AVFilterFormats结构体的注释，了解色度空间的协商机制是怎么工作的，以及为什么协商是必要的）。结果就是一个滤镜如果为它之前和之后的滤镜提供了指向相同支持格式的列表的指针，就意味着这个链条上的滤镜就只能使用相同的格式了。<br />
两个滤镜相连时，它们需要在它们处理的图像数据的尺寸和图像格式上达成一致。达成一致后，这些会作为参数存储在link结构体中。<br />
成员“srcpic”是滤镜系统内部使用的，不应该直接存取。<br />
成员“cur_pic”是给目标滤镜用的。当一个帧正在通过滤镜链时（开始于start_frame()，结束于end_frame），这个成员包含了目标滤镜对此帧的引用。<br />
成员“outpic”会在接下来一个小教程中详细介绍。</p>
<p><br/></p>
<h2>写一个简单的滤镜</h2>
<p><strong>默认的滤镜入口点</strong></p>
<p>因为大多数滤镜都只有一个输入一个输出，且每接受一个帧只输出一个帧，ffmpeg的滤镜系统提供了一系列默认的切入点以简化这种滤镜的开发，以下是切入点和默认实现的作用：<br />
request_frame()	从滤镜链中前一个滤镜那请求一个帧<br />
query_formats()	设置所有面上都支持的格式列表，这样别人就要按照这个列表来。默认包含大多数的YUV和RGB／BGR格式<br />
start_frame()	请求一个buffer来保存输出帧。一个指向此buffer的引用存储在hook到滤镜的输出的link的“outpic”成员中。下一个滤镜的start_frame()回调会被调用，传入一个此buffer的引用。<br />
end_frame()	调用下一个滤镜的end_frame()回调函数。释放指向输出link的“outpic”成员的引用，如果那成员被设置了（比如说使用了默认的start_frame()方法）。释放输入link的“cur_pic”引用<br />
get_video_buffer()	返回一个在要求的权限上加一个AV_PERM_READ权限的buffer<br />
config_props() on output pad	把输出的图像尺寸设置成和输入一样</p>
<p><strong>“vf_negate”滤镜</strong></p>
<p>介绍了数据结构和回调函数，让我们来看一个真实的滤镜。vf_negate滤镜的效果是反转视频中的色彩。它就一个输入一个输出，并且每个输入帧都输出一个帧。非常典型，可以使用滤镜系统那些默认的回调实现。</p>
<p>首先，让我们看一眼在“libavfilter/vf_negate.c”文件最下面的“AVFilter”结构体：</p>
<pre class="brush: cpp">
AVFilter avfilter_vf_negate =
{
    .name      = "negate",

    .priv_size = sizeof(NegContext),

    .query_formats = query_formats,

    .inputs    = (AVFilterPad[]) {{ .name            = "default",
                                    .type            = AV_PAD_VIDEO,
                                    .draw_slice      = draw_slice,
                                    .config_props    = config_props,
                                    .min_perms       = AV_PERM_READ, },
                                  { .name = NULL}},
    .outputs   = (AVFilterPad[]) {{ .name            = "default",
                                    .type            = AV_PAD_VIDEO, },
                                  { .name = NULL}},
};
</pre>
<p>可以看到滤镜的名字是“negate”，需要sizeof(NegContext)字节的空间存储上下文。在input和output列表的最后，都有一个name设置为NULL的pad。可以看出这个滤镜确实只有一个输入一个输出。如果你仔细观察pad的定义，你会发现好多回调函数已经被指定好了。因为我们这个滤镜很简单，所以大多数保持默认的就可以。</p>
<p>让我们看看它自己定义的回调函数。</p>
<p><strong>query_formats()</strong></p>
<pre class="brush: cpp">
static int query_formats(AVFilterContext *ctx)
{
    avfilter_set_common_formats(ctx,
        avfilter_make_format_list(10,
                PIX_FMT_YUV444P,  PIX_FMT_YUV422P,  PIX_FMT_YUV420P,
                PIX_FMT_YUV411P,  PIX_FMT_YUV410P,
                PIX_FMT_YUVJ444P, PIX_FMT_YUVJ422P, PIX_FMT_YUVJ420P,
                PIX_FMT_YUV440P,  PIX_FMT_YUVJ440P));
    return 0;
}
</pre>
<p>这个函数调用了avfilter_make_format_list()。这个方法第一个函数指定后面要列举多少个格式，后面就把格式列出来。返回是一个包含指定格式的AVFilterFormats结构体。把这个结构体传给avfilter_set_common_formats()方法把格式给设置上。如同你看到的，这个滤镜支持一堆YUV平面的色彩空间格式，包括JPEG YUV色彩空间（其中那些包含字母J的）。</p>
<p>config_props() on an input pad<br />
input填充的config_props()负责验证是否支持输入pad的属性，也负责更新滤镜的属性上下文。<br />
TODO: 快速解释一下YUV色彩空间，色读采样，YUV和JEPG YUV范围的不同。</p>
<p>让我们看看滤镜是怎么存储它的上下文的：</p>
<pre class="brush: cpp">
typedef struct
{
    int offY, offUV;
    int hsub, vsub;
} NegContext;
</pre>
<p>AVFilter结构体中的成员“priv_size”告诉滤镜系统它需要多少字节来存储这个结构体。成员“hsub”和“vsub”用于色度采样，成员“offY”和“offUV”用于YUV和JPEG间范围的不同。让我们看看这些在输入pad的config_props中是咋设置的：</p>
<pre class="brush: cpp">
static int config_props(AVFilterLink *link)
{
    NegContext *neg = link->dst->priv;

    avcodec_get_chroma_sub_sample(link->format, &#038;neg->hsub, &#038;neg->vsub);

    switch(link->format) {
    case PIX_FMT_YUVJ444P:
    case PIX_FMT_YUVJ422P:
    case PIX_FMT_YUVJ420P:
    case PIX_FMT_YUVJ440P:
        neg->offY  =
        neg->offUV = 0;
        break;
    default:
        neg->offY  = -4;
        neg->offUV = 1;
    }

    return 0;
}
</pre>
<p>它只是简单调用了avcodec_get_chroma_sub_sample()方法去得到色度采样的位移因子，然后把它们存到上下文中。然后它存储了一些JPEG YUV的亮度／色度范围不同的偏移补偿。返回0表明成功，因为没有这个滤镜无法处理的输入格式。</p>
<p><strong>draw_slice()</strong></p>
<p>最后，滤镜中最重要的方法，它实际处理图像，draw_slice()：</p>
<pre class="brush: cpp">
static void draw_slice(AVFilterLink *link, int y, int h)
{
    NegContext *neg = link->dst->priv;
    AVFilterPicRef *in  = link->cur_pic;
    AVFilterPicRef *out = link->dst->outputs[0]->outpic;
    uint8_t *inrow, *outrow;
    int i, j, plane;

    /* luma plane */
    inrow  = in-> data[0] + y * in-> linesize[0];
    outrow = out->data[0] + y * out->linesize[0];
    for(i = 0; i < h; i ++) {
        for(j = 0; j < link->w; j ++)
            outrow[j] = 255 - inrow[j] + neg->offY;
        inrow  += in-> linesize[0];
        outrow += out->linesize[0];
    }

    /* chroma planes */
    for(plane = 1; plane < 3; plane ++) {
        inrow  = in-> data[plane] + (y >> neg->vsub) * in-> linesize[plane];
        outrow = out->data[plane] + (y >> neg->vsub) * out->linesize[plane];

        for(i = 0; i < h >> neg->vsub; i ++) {
            for(j = 0; j < link->w >> neg->hsub; j ++)
                outrow[j] = 255 - inrow[j] + neg->offUV;
            inrow  += in-> linesize[plane];
            outrow += out->linesize[plane];
        }
    }

    avfilter_draw_slice(link->dst->outputs[0], y, h);
}
</pre>
<p>“y”参数是当前slice的顶部，“h”参数是slice的高度。在这个区域以外的图像被假设为是无意义的（可能在未来的一些滤镜中这个假设会被打破）。</p>
<p>变量“inrow”指向输入slice的第一行，“outrow”指向输出的第一行。然后，它先遍历每一行，然后在每一行中遍历每一个像素，用255去减像素值，加上在config_props()中为不同格式的范围做出的修正值。</p>
<p>然后它在色度平面上做了同样的事。注意宽度和高度是调整到合适色度采样的。</p>
<p>在图像修改结束后，调用calling avfilter_draw_slice()方法把slice送给下一个滤镜去处理。</p>
<h2>翻译自</h2>
<p>http://wiki.multimedia.cx/index.php?title=FFmpeg_filter_HOWTO</p>
<hr />
<p><small>© chen for <a href="http://blog.yikuyiku.com">陈钢的博客</a>, 2012. |
<a href="http://blog.yikuyiku.com/?p=3023">原文链接</a> |
<a href="http://blog.yikuyiku.com/?p=3023#comments"></a> 
<a href="http://blog.yikuyiku.com/?cat=4" title="查看 实践 中的全部文章" rel="category">实践</a>, <a href="http://blog.yikuyiku.com/?cat=8" title="查看 翻译 中的全部文章" rel="category">翻译</a>
<br/>
Post tags: <a href="http://blog.yikuyiku.com/?tag=ffmpeg" rel="tag">ffmpeg</a>, <a href="http://blog.yikuyiku.com/?tag=libavfilter" rel="tag">libavfilter</a>, <a href="http://blog.yikuyiku.com/?tag=%e6%bb%a4%e9%95%9c" rel="tag">滤镜</a>
<br/>
</small></p>]]></content:encoded>
			<wfw:commentRss>http://blog.yikuyiku.com/?feed=rss2&#038;p=3023</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>ffmpeg如何编译新加的滤镜</title>
		<link>http://blog.yikuyiku.com/?p=3020</link>
		<comments>http://blog.yikuyiku.com/?p=3020#comments</comments>
		<pubDate>Tue, 21 Feb 2012 08:19:04 +0000</pubDate>
		<dc:creator>chen</dc:creator>
				<category><![CDATA[实践]]></category>
		<category><![CDATA[ffmpeg]]></category>
		<category><![CDATA[滤镜]]></category>

		<guid isPermaLink="false">http://blog.yikuyiku.com/?p=3020</guid>
		<description><![CDATA[在按照http://wiki.multimedia.cx/index.php?title=FFmpeg_filter_HOWTO写好了滤镜之后，需要更改以下2个地方，ffmpeg编译时才会把新滤镜编译进去。 假设新添滤镜名字是“vf_tnegate.c”： 1、libavfilter/Makefile文件里添加一行“OBJS-$(CONFIG_TNEGATE_FILTER) += vf_tnegate.o”； 2、libavfilter/allfilters.c文件里添加一行“REGISTER_FILTER (TNEGATE, tnegate, vf);”； 至于加在哪儿，类似的行很多，很容易找到类似的，随便找一个，比如抄yadif的就好了～ 相关推荐： 如何查看ffmpeg支持的编码器和封装格式 FFmpeg截图耗时 Mac OS X编译MP4Box和ffmpeg ffmpeg出aac音频方法 隐藏h264视频中的x264版本和编码设置 © chen for 陈钢的博客, 2012. &#124; 原文链接 &#124; 实践 Post tags: ffmpeg, 滤镜
相关推荐：<ol>
<li><a href='http://blog.yikuyiku.com/?p=2288' rel='bookmark' title='如何查看ffmpeg支持的编码器和封装格式'>如何查看ffmpeg支持的编码器和封装格式</a></li>
<li><a href='http://blog.yikuyiku.com/?p=2245' rel='bookmark' title='FFmpeg截图耗时'>FFmpeg截图耗时</a></li>
<li><a href='http://blog.yikuyiku.com/?p=2654' rel='bookmark' title='Mac OS X编译MP4Box和ffmpeg'>Mac OS X编译MP4Box和ffmpeg</a></li>
<li><a href='http://blog.yikuyiku.com/?p=1697' rel='bookmark' title='ffmpeg出aac音频方法'>ffmpeg出aac音频方法</a></li>
<li><a href='http://blog.yikuyiku.com/?p=1777' rel='bookmark' title='隐藏h264视频中的x264版本和编码设置'>隐藏h264视频中的x264版本和编码设置</a></li>
</ol>]]></description>
			<content:encoded><![CDATA[<p>在按照http://wiki.multimedia.cx/index.php?title=FFmpeg_filter_HOWTO写好了滤镜之后，需要更改以下2个地方，ffmpeg编译时才会把新滤镜编译进去。</p>
<p>假设新添滤镜名字是“vf_tnegate.c”：</p>
<p>1、libavfilter/Makefile文件里添加一行“OBJS-$(CONFIG_TNEGATE_FILTER)                += vf_tnegate.o”；</p>
<p>2、libavfilter/allfilters.c文件里添加一行“REGISTER_FILTER (TNEGATE,     tnegate,     vf);”；</p>
<p>至于加在哪儿，类似的行很多，很容易找到类似的，随便找一个，比如抄yadif的就好了～</p>
<p>相关推荐：<ol>
<li><a href='http://blog.yikuyiku.com/?p=2288' rel='bookmark' title='如何查看ffmpeg支持的编码器和封装格式'>如何查看ffmpeg支持的编码器和封装格式</a></li>
<li><a href='http://blog.yikuyiku.com/?p=2245' rel='bookmark' title='FFmpeg截图耗时'>FFmpeg截图耗时</a></li>
<li><a href='http://blog.yikuyiku.com/?p=2654' rel='bookmark' title='Mac OS X编译MP4Box和ffmpeg'>Mac OS X编译MP4Box和ffmpeg</a></li>
<li><a href='http://blog.yikuyiku.com/?p=1697' rel='bookmark' title='ffmpeg出aac音频方法'>ffmpeg出aac音频方法</a></li>
<li><a href='http://blog.yikuyiku.com/?p=1777' rel='bookmark' title='隐藏h264视频中的x264版本和编码设置'>隐藏h264视频中的x264版本和编码设置</a></li>
</ol></p><hr />
<p><small>© chen for <a href="http://blog.yikuyiku.com">陈钢的博客</a>, 2012. |
<a href="http://blog.yikuyiku.com/?p=3020">原文链接</a> |
<a href="http://blog.yikuyiku.com/?p=3020#comments"></a> 
<a href="http://blog.yikuyiku.com/?cat=4" title="查看 实践 中的全部文章" rel="category">实践</a>
<br/>
Post tags: <a href="http://blog.yikuyiku.com/?tag=ffmpeg" rel="tag">ffmpeg</a>, <a href="http://blog.yikuyiku.com/?tag=%e6%bb%a4%e9%95%9c" rel="tag">滤镜</a>
<br/>
</small></p>]]></content:encoded>
			<wfw:commentRss>http://blog.yikuyiku.com/?feed=rss2&#038;p=3020</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>去哪里寻找幸福和安宁</title>
		<link>http://blog.yikuyiku.com/?p=2998</link>
		<comments>http://blog.yikuyiku.com/?p=2998#comments</comments>
		<pubDate>Thu, 09 Feb 2012 16:40:35 +0000</pubDate>
		<dc:creator>chen</dc:creator>
				<category><![CDATA[扯淡]]></category>
		<category><![CDATA[基督教]]></category>
		<category><![CDATA[幸福]]></category>

		<guid isPermaLink="false">http://blog.yikuyiku.com/?p=2998</guid>
		<description><![CDATA[看了这个视频：http://v.youku.com/v_show/id_XMzIwMTE4Nzg4.html，觉得挺有触动的。 视频里有一句话说，“我们本来是什么样子（意即人童真的、美好的样子），无论怎样，无论发生过什么，我们都可以回到那个状态去。”。意即“主”可以帮我们免罪，我们自己将可以保有安宁。 之前在博客（125随笔）里说，基督教是“一种廉价的自我原谅”，那时候的我觉得人应该活得坚强一些，更有承担一些。 可现在越来越觉得，生命中确有一些“不可承受之重”难以对付。特别是当心灵一丝丝衰弱，则越发感受到外部世界的强大。 如果说，“主”确实可以拿去“残缺的我”，还我新生。那就是说幸福和安宁确实是可以向内求索得到的，而且比向外攉取更容易。 可那不就又回到印度苦行僧“心灵是神灵，欲念是魔鬼”的老路上去了吗？ 也许很难讲哪种宗教比哪种宗教更优胜。现在最流行的拜物教之所以没有如同基督教般带来安宁，有没有可能是因为我们拜地不够彻底，拜地欲拒还迎呢？ 唉⋯⋯ 南阎浮提，苦生苦灭。 相关推荐： 关于生、关于死 © chen for 陈钢的博客, 2012. &#124; 原文链接 &#124; 扯淡 Post tags: 基督教, 幸福
相关推荐：<ol>
<li><a href='http://blog.yikuyiku.com/?p=1008' rel='bookmark' title='关于生、关于死'>关于生、关于死</a></li>
</ol>]]></description>
			<content:encoded><![CDATA[<p>看了这个视频：http://v.youku.com/v_show/id_XMzIwMTE4Nzg4.html，觉得挺有触动的。</p>
<p>视频里有一句话说，“我们本来是什么样子（意即人童真的、美好的样子），无论怎样，无论发生过什么，我们都可以回到那个状态去。”。意即“主”可以帮我们免罪，我们自己将可以保有安宁。</p>
<p>之前在博客（<a href="http://blog.yikuyiku.com/?p=1083" title="125随笔" target="_blank">125随笔</a>）里说，基督教是“一种廉价的自我原谅”，那时候的我觉得人应该活得坚强一些，更有承担一些。</p>
<p>可现在越来越觉得，生命中确有一些“不可承受之重”难以对付。特别是当心灵一丝丝衰弱，则越发感受到外部世界的强大。</p>
<p>如果说，“主”确实可以拿去“残缺的我”，还我新生。那就是说幸福和安宁确实是可以向内求索得到的，而且比向外攉取更容易。</p>
<p>可那不就又回到印度苦行僧“心灵是神灵，欲念是魔鬼”的老路上去了吗？</p>
<p>也许很难讲哪种宗教比哪种宗教更优胜。现在最流行的拜物教之所以没有如同基督教般带来安宁，有没有可能是因为我们拜地不够彻底，拜地欲拒还迎呢？</p>
<p>唉⋯⋯</p>
<p>南阎浮提，苦生苦灭。</p>
<p>相关推荐：<ol>
<li><a href='http://blog.yikuyiku.com/?p=1008' rel='bookmark' title='关于生、关于死'>关于生、关于死</a></li>
</ol></p><hr />
<p><small>© chen for <a href="http://blog.yikuyiku.com">陈钢的博客</a>, 2012. |
<a href="http://blog.yikuyiku.com/?p=2998">原文链接</a> |
<a href="http://blog.yikuyiku.com/?p=2998#comments"></a> 
<a href="http://blog.yikuyiku.com/?cat=6" title="查看 扯淡 中的全部文章" rel="category">扯淡</a>
<br/>
Post tags: <a href="http://blog.yikuyiku.com/?tag=%e5%9f%ba%e7%9d%a3%e6%95%99" rel="tag">基督教</a>, <a href="http://blog.yikuyiku.com/?tag=%e5%b9%b8%e7%a6%8f" rel="tag">幸福</a>
<br/>
</small></p>]]></content:encoded>
			<wfw:commentRss>http://blog.yikuyiku.com/?feed=rss2&#038;p=2998</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>RHCA培训散记（333网络安全 – 5）《关于NFS》</title>
		<link>http://blog.yikuyiku.com/?p=2979</link>
		<comments>http://blog.yikuyiku.com/?p=2979#comments</comments>
		<pubDate>Fri, 27 Jan 2012 14:56:55 +0000</pubDate>
		<dc:creator>chen</dc:creator>
				<category><![CDATA[备忘]]></category>
		<category><![CDATA[333]]></category>
		<category><![CDATA[kerberos]]></category>
		<category><![CDATA[nfs]]></category>
		<category><![CDATA[NFSv4]]></category>
		<category><![CDATA[pseudo filesystem]]></category>
		<category><![CDATA[RHCA]]></category>
		<category><![CDATA[网络安全]]></category>

		<guid isPermaLink="false">http://blog.yikuyiku.com/?p=2979</guid>
		<description><![CDATA[NFS 1、NFS（网络文件系统Network File System）可能启动的进程有nfsd、rpc.mountd、rpc.statd、lockd（内核态）、rpc.idmapd、rpc.gssd、rpc.svcgssd。nfsd使用2049的TCP和2049的UDP端口，其它的端口号由portmap随机提供（portmap自己监听在111端口）； 2、NFS的各个版本概述： a> NFS v1是SUN公司研发的，包含在Sun的操作系统里，没有单独公开发行过； b> NFS v2是最原始的NFS协议，在RFC 1094中描述。基于UDP协议，最大单个文件4G； c> NFS v3在RFC 1813中描述，相对于v2增加了TCP协议支持，安全的async异步写入支持，服务器端访问控制，支持大于16Y的单个文件大小（64bit），提高了一次读写的最大大小，某些版本可以为RPC开启基于kerberos的身份验证（RHEL使用的版本就可以）。v3相对于v2在性能上是一个大的跃进，但从安全的角度来看并没有多少提升； d> NFS v4在RFC 3530中描述，相对于v3，它把lock和mount都整合进了核心协议中（原来在辅助协议），开发了新的ACL机制，引入对UTF8字符集的支持。NFS v4要求所有的实现都必须支持kerberos的身份验证（RPCSEC_GSS）（替代原有的基于UID、GID的身份验证），要求所有实现必须支持多种加密传输模式。从安全角度而言NFS v4是一个大的跃进，但目前它没有v3版本使用得广泛； 3、NFS v3的连接过程： a> 客户端问服务器端的portmap：rpc.mount目前的用哪个端口？ b> 客户端向服务器端的rpc.mount请求挂载NFS； c> 服务器端的rpc.mount判断权限后给客户端一个文件句柄； d> 客户端使用这个句柄与服务器端的nfsd交流（使用TCP或UDP的2049端口），以读写文件； e> 文件锁是由lockd和rpc.stats管理的； 4、NFSv3协议的服务器端是无状态的，所以就算机器重启了，NFS服务起来以后，客户端依然可以拿着旧文件句柄继续读写文件。但是服务器端的lockd进程是有状态的，重启就有点麻烦，解决方案是服务器端的rpc.statd让客户端报告自己手里的锁，然后重新让lockd恢复锁状态。这种锁状态机制是到v3版本才引入的；v2版本在crash之后会出现锁错误； 5、NFS v3的验证机制： a> NFS v3及其附属协议采用标准的RPC AUTH_SYS（又称AUTH_UNIX）机制验证挂载后的客户端对具体文件的权限，服务器完全信任客户端声名的自己的权限（其实不能被称为是“验证”了）； b> 大概过程就是客户端会在读写之前告诉服务器自己的UID和GID，然后NFS就把这些ID视同自己系统上的ID来验证权限； c> 客户端可以很容易伪造出高权限的ID以达到攻击的目的，防御的临时解决之道是不让NFS暴露在公有网络上且不打开NFS的root权限（是比较弱的防御）； d> 还有一个麻烦是，不同客户端上同一个username的UID想保持同步是件不容易的事； e> 为了解决NFS 验证机制的不足，为NFS引入的解决之道就是kerberos（也就是RPCSEC_GSS验证机制）； 6、NFS的UID／GID同步问题初步讨论： a> 最简单的办法就是用root_squash或者all_squash来做UID映射，把所有用户都映射成匿名用户。需要注意的是匿名用户的UID其实是-2，所以65534其实只是在16bit用户ID的系统上的表现，在一些32bit用户ID的系统上-2会被表现为4294967294； b> pam_lisfile.so和pan_limits.so这两个PAM模块可以帮助我们确保NFS用户不会得到shell登录权限； [...]
相关推荐：<ol>
<li><a href='http://blog.yikuyiku.com/?p=2899' rel='bookmark' title='RHCA培训散记（333网络安全 – 2）《关于加密》'>RHCA培训散记（333网络安全 – 2）《关于加密》</a></li>
<li><a href='http://blog.yikuyiku.com/?p=2930' rel='bookmark' title='RHCA培训散记（333网络安全 – 3）《关于DNS》'>RHCA培训散记（333网络安全 – 3）《关于DNS》</a></li>
<li><a href='http://blog.yikuyiku.com/?p=2956' rel='bookmark' title='RHCA培训散记（333网络安全 – 4）《关于基于网络的身份验证》'>RHCA培训散记（333网络安全 – 4）《关于基于网络的身份验证》</a></li>
<li><a href='http://blog.yikuyiku.com/?p=2859' rel='bookmark' title='RHCA培训散记（333网络安全 – 1）'>RHCA培训散记（333网络安全 – 1）</a></li>
</ol>]]></description>
			<content:encoded><![CDATA[<h2>NFS</h2>
<p><strong>1、NFS</strong>（网络文件系统Network File System）可能启动的进程有nfsd、rpc.mountd、rpc.statd、lockd（内核态）、rpc.idmapd、rpc.gssd、rpc.svcgssd。nfsd使用2049的TCP和2049的UDP端口，其它的端口号由portmap随机提供（portmap自己监听在111端口）；</p>
<p><strong>2、NFS的各个版本概述：</strong><br />
a> NFS v1是SUN公司研发的，包含在Sun的操作系统里，没有单独公开发行过；<br />
b> NFS v2是最原始的NFS协议，在RFC 1094中描述。基于UDP协议，最大单个文件4G；<br />
c> NFS v3在RFC 1813中描述，相对于v2增加了TCP协议支持，安全的async异步写入支持，服务器端访问控制，支持大于16Y的单个文件大小（64bit），提高了一次读写的最大大小，某些版本可以为RPC开启基于kerberos的身份验证（RHEL使用的版本就可以）。v3相对于v2在性能上是一个大的跃进，但从安全的角度来看并没有多少提升；<br />
d> NFS v4在RFC 3530中描述，相对于v3，它把lock和mount都整合进了核心协议中（原来在辅助协议），开发了新的ACL机制，引入对UTF8字符集的支持。NFS v4要求所有的实现都必须支持kerberos的身份验证（RPCSEC_GSS）（替代原有的基于UID、GID的身份验证），要求所有实现必须支持多种加密传输模式。从安全角度而言NFS v4是一个大的跃进，但目前它没有v3版本使用得广泛；</p>
<p><strong>3、NFS v3的连接过程</strong>：<br />
a> 客户端问服务器端的portmap：rpc.mount目前的用哪个端口？<br />
b> 客户端向服务器端的rpc.mount请求挂载NFS；<br />
c> 服务器端的rpc.mount判断权限后给客户端一个文件句柄；<br />
d> 客户端使用这个句柄与服务器端的nfsd交流（使用TCP或UDP的2049端口），以读写文件；<br />
e> 文件锁是由lockd和rpc.stats管理的；</p>
<p><strong>4、NFSv3协议的服务器端是无状态的</strong>，所以就算机器重启了，NFS服务起来以后，客户端依然可以拿着旧文件句柄继续读写文件。但是服务器端的lockd进程是有状态的，重启就有点麻烦，解决方案是服务器端的rpc.statd让客户端报告自己手里的锁，然后重新让lockd恢复锁状态。这种锁状态机制是到v3版本才引入的；v2版本在crash之后会出现锁错误；</p>
<p><strong>5、NFS v3的验证机制</strong>：<br />
a> NFS v3及其附属协议采用标准的RPC AUTH_SYS（又称AUTH_UNIX）机制验证挂载后的客户端对具体文件的权限，服务器完全信任客户端声名的自己的权限（其实不能被称为是“验证”了）；<br />
b> 大概过程就是客户端会在读写之前告诉服务器自己的UID和GID，然后NFS就把这些ID视同自己系统上的ID来验证权限；<br />
c> 客户端可以很容易伪造出高权限的ID以达到攻击的目的，防御的临时解决之道是不让NFS暴露在公有网络上且不打开NFS的root权限（是比较弱的防御）；<br />
d> 还有一个麻烦是，不同客户端上同一个username的UID想保持同步是件不容易的事；<br />
e> 为了解决NFS 验证机制的不足，为NFS引入的解决之道就是kerberos（也就是RPCSEC_GSS验证机制）；</p>
<p><strong>6、NFS的UID／GID同步问题初步讨论：</strong><br />
a> 最简单的办法就是用root_squash或者all_squash来做UID映射，把所有用户都映射成匿名用户。需要注意的是匿名用户的UID其实是-2，所以65534其实只是在16bit用户ID的系统上的表现，在一些32bit用户ID的系统上-2会被表现为4294967294；<br />
b> pam_lisfile.so和pan_limits.so这两个PAM模块可以帮助我们确保NFS用户不会得到shell登录权限；<br />
c> UID和GID在不同客户端之间不同的根治方法是使用NIS或者LDAP的方式直接把UID／GID统一掉；</p>
<p><strong>7、NFS v3的安全怎么做？</strong><br />
a> 不要把包含配置文件的目录export出去；<br />
b> export整个文件系统的根出去，而不是export文件系统中某个目录出去。因为即使只是export一个目录出去，攻击者也可能通过猜测的方式得到文件系统中其它目录的读写权限。比如说一个ext3挂载在/mnt/下了，用NFS export/mnt/data1/出去，攻击者就可能读写/mnt/data2/下的文件。这显然不是我们希望的，因此不如干脆共享整个文件系统（也就是/mnt/）出去。或者也可以使用NFS的substree_check来帮我们做检查来防止这种入侵，但是这个选项会较大幅度降低NFS的性能；<br />
c> 如果一个文件系统挂载点是另一个文件系统的子目录，那么父系统开启crossmnt或者子系统开启nohide就可以把两个文件系统都共享出去，使用这个选项的时候要小心，别共享了自己不想共享的内容出去；<br />
d> 虽然nfsd固定使用2049端口，但是lockd、mountd、statd都使用portmap随机分配的端口，这让防火墙很难配置，而且还可能占用还没起来的其它服务的端口。可以在/etc/sysconfig/nfs中把这些进程的端口都配置成固定的，这样配置防火墙（只放行自己信任的IP）就容易了；<br />
e> NFS基于IP的认证方式让伪造身份成为可能，基于AUTH_SYS（UID／GID）的授权方式让越权变成可能，明文的、没有完整性校验（有校验网络传输完整，没有安全性完整校验）的传输协议让嗅探和篡改变成可能。要解决这些问题，就要采用NFS v4了；<br />
f> NFS是个十分复杂的服务，很多部分要求root权限运行，还运行在内核态里。如果不是必要就尽量不要启动它，以防止未来发现的漏洞给攻击者可乘之机；</p>
<p><strong>8、rpcinfo</strong> -p可以查看portmap分配出去的端口；</p>
<p><strong>9、NFS v4的相对v3的变化：</strong><br />
a> NFS版本4把周边附属协议的lockd、rpc.mountd、rpc.statd都整合进了主协议中，进程也都整合进了nfsd里。整个服务的进程结构精简了很多，防火墙只需要开放2049和111端口NFS即可正确提供服务；<br />
b> 新增了一个必须运行的rpc.idmapd进程（服务器端和客户端都要运行），支持RPC验证协议，用于保持username-uid的映射，解决了之前版本的那个麻烦的UID不一致问题。当然，还是可以选择AUTH_SYS的验证方式，自己去弄那麻烦；<br />
c> NFS v4原生可以支持基于kerberos的RPCSEC_GSS安全认证，开启这个功能的话能简化配置，提高安全性，也解决了UID不一致的问题；<br />
d> 精简的协议带来了一定的速度提升（约10％，详见http://www.fedoraforum.org/forum/showthread.php?t=219433）；<br />
e> NFSv4仍在继续开发中，见www.nfsv4.org；</p>
<p><strong>10、NFS v4引入了一个叫做pseudo filesystem（伪文件系统）的机制</strong>，网上挺多针对它的讨论的，挺多人认为这个纯属脱裤子放屁，教材上也没提它能带来什么好处。大概意思就是说要先选一个目录表明fsid＝0作为一个伪文件系统的根，然后所有需要export的目录和文件系统都挂载在这个伪文件系统的下，就如同是它的一个子目录。我翻了一下RFC3530（http://www.ietf.org/rfc/rfc3530.txt），RFC里的意思呢，主要是为了解决命名空间的问题，有了这个伪文件系统根，再加上主机名，基本命名空间就都散开不会冲突了。再一个，用了这个之后客户端直接用READDIR就能遍历服务器端的所有export，特别在服务器新export了东西以后，客户端不用做出任何更改便可直接使用新的东西（如果有权限的话）。更详细的见RFC3530 7.3.0；</p>
<p><strong>11、RFC2203介绍了NFS如何利用kerberos的验证机制</strong>（又唤RPCSEC_GSS），v3和v4版本都可以用，需要在客户端使用rpc.gssd，在服务器端使用rpc.svcgssd来完成沟通。rpc.svcgssd会通过GSSAPI去获得kerberos的验证信息，而rpc.gssd则负责去向kerberos获得权限认证。它们也会帮忙解决username－uid／groupname－gid映射的麻烦。但是启用了kerberos验证就不能使用基于IP的验证了（当然iptables是可以用的）在Linux 2.6.23和nfs-utils1.1.1后变为可能；</p>
<p><strong>12、kerberos有几种帮助NFS做安全的模式</strong>，这些模式需要在服务器端/etc/exports中指定：<br />
a> krb5。仅使用kerberos来做用户身份验证；<br />
b> krb5i。除了之上，再加上安全的数据完整性校验（基于对称密钥的签名）；<br />
c> krb5p。除了之上，再加上把所有数据都加密；</p>
<p><strong>13、目前SELinux还没有能力在NFS的服务器和客户端之间传递安全上下文</strong>，这个工作正在进行中，所以目前服务器端和客户端分别有自己的安全上下文（客户端的上下文是统一的，自己设置的）。SELinux为NFS预置了一些很有用的布尔值，如nfs_export_all_rw和use_nfs_home_dirs；</p>
<p><strong>14、cat /proc/net/rpc/nfs4.idtoname/content</strong>可以看到NFSv4的名字映射；</p>
<p><strong>15、客户端完全重启NFSv4模块的方法</strong>，顺序执行以下：<br />
a> umount -at nfs4；<br />
b> 停止rpc.gssd、rpc.idmapd、autofs；<br />
c> 重启内核模块：modprobe -r nfs lockd fscache nfs_acl rpcsec_gss_krb5 authrpcgss autofs4；<br />
d> 启动rpc.gssd、rpc.idmapd、autofs；<br />
e> 重新挂载NFSv4文件系统；</p>
<p><strong>16、整理一下启用kerberos验证的NFSv4的几个要素：</strong><br />
a> kerberos机制正常；<br />
b> 服务器正确下载了自己principal的keytab；<br />
c> rpc.svcgssd和rpc.gssd正常启动；<br />
d> TGT正确且没过期（可用klist查看）；<br />
e> rpc.idmapd正确配置了；</p>
<p><strong>17、用于NFS的kerberos目前只能用des-cbc-md5的加密方式</strong>（要在kadd的时候声名）；</p>
<p><strong>18、NFS v3虽然可以使用kerberos，但并没有v4支持地那么完整。</strong>由于它的MOUNT协议还是只支持AUTH_SYS，因此还是需要给未授权用户MOUNT的权限，当然把它们限制到只读是可以的；</p>
<p><strong>19、在高可用的环境下</strong>，虽说NFS的句柄是服务器端无状态的，即在服务器重启之后依然可用，但如果NFS服务从一个服务器飘到另一个服务器上提供服务了，那么旧的文件句柄则可能会因为fsid的改变而变得不可用。此时，在配置文件中指定每一个export的fsid是个不错的主意；</p>
<p>相关推荐：<ol>
<li><a href='http://blog.yikuyiku.com/?p=2899' rel='bookmark' title='RHCA培训散记（333网络安全 – 2）《关于加密》'>RHCA培训散记（333网络安全 – 2）《关于加密》</a></li>
<li><a href='http://blog.yikuyiku.com/?p=2930' rel='bookmark' title='RHCA培训散记（333网络安全 – 3）《关于DNS》'>RHCA培训散记（333网络安全 – 3）《关于DNS》</a></li>
<li><a href='http://blog.yikuyiku.com/?p=2956' rel='bookmark' title='RHCA培训散记（333网络安全 – 4）《关于基于网络的身份验证》'>RHCA培训散记（333网络安全 – 4）《关于基于网络的身份验证》</a></li>
<li><a href='http://blog.yikuyiku.com/?p=2859' rel='bookmark' title='RHCA培训散记（333网络安全 – 1）'>RHCA培训散记（333网络安全 – 1）</a></li>
</ol></p><hr />
<p><small>© chen for <a href="http://blog.yikuyiku.com">陈钢的博客</a>, 2012. |
<a href="http://blog.yikuyiku.com/?p=2979">原文链接</a> |
<a href="http://blog.yikuyiku.com/?p=2979#comments"></a> 
<a href="http://blog.yikuyiku.com/?cat=3" title="查看 备忘 中的全部文章" rel="category">备忘</a>
<br/>
Post tags: <a href="http://blog.yikuyiku.com/?tag=333" rel="tag">333</a>, <a href="http://blog.yikuyiku.com/?tag=kerberos" rel="tag">kerberos</a>, <a href="http://blog.yikuyiku.com/?tag=nfs" rel="tag">nfs</a>, <a href="http://blog.yikuyiku.com/?tag=nfsv4" rel="tag">NFSv4</a>, <a href="http://blog.yikuyiku.com/?tag=pseudo-filesystem" rel="tag">pseudo filesystem</a>, <a href="http://blog.yikuyiku.com/?tag=rhca" rel="tag">RHCA</a>, <a href="http://blog.yikuyiku.com/?tag=%e7%bd%91%e7%bb%9c%e5%ae%89%e5%85%a8" rel="tag">网络安全</a>
<br/>
</small></p>]]></content:encoded>
			<wfw:commentRss>http://blog.yikuyiku.com/?feed=rss2&#038;p=2979</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>RHCA培训散记（333网络安全 – 4）《关于基于网络的身份验证》</title>
		<link>http://blog.yikuyiku.com/?p=2956</link>
		<comments>http://blog.yikuyiku.com/?p=2956#comments</comments>
		<pubDate>Thu, 26 Jan 2012 03:10:48 +0000</pubDate>
		<dc:creator>chen</dc:creator>
				<category><![CDATA[备忘]]></category>
		<category><![CDATA[333]]></category>
		<category><![CDATA[kerberos]]></category>
		<category><![CDATA[NIS]]></category>
		<category><![CDATA[NSS]]></category>
		<category><![CDATA[RHCA]]></category>

		<guid isPermaLink="false">http://blog.yikuyiku.com/?p=2956</guid>
		<description><![CDATA[NIS 1、NSS（Network Security Services）网络安全服务，包含SSL v2、SSL v3、 TLS、 PKCS#5、 PKCS#7、 PKCS#11、 PKCS#12、 S/MIME、 X.509v3 certificates和其它安全协议； 2、网络登录也和其它的登录服务一样，分为验证和授权两部分。反应为密码和其它的信息（登录shell、UID、GID等）分开存放。一般的实现是用NIS（Network Information Service）存储其它信息（NIS也能存放密码，不过是明文传输的，所以不安全），用kerberos存放密码； 3、上述的“其它信息”除了NIS，还可以选择使用LDAP（Lightweight Directory Access Protocol）、DNS（Hesiod记录）； 4、我们通常说的Linux中的RPC其实是指SUN实现的Sun RPC，也叫ONC RPC（Open Network Computing Remote Procedure Call），可以建立在TCP或者UDP上，传输过程中数据会被编码成XDR（External Data Representation standard）（在RFC 1014中描述）格式； 5、RPC中每个应用程序都有一个自己的Program ID，可以在文件/etc/rpc中找到所有的ID的列表。其中最常用到的两个是NFS和NIS； 6、RPC的一个弱点是它会向其它的机器暴露自己可以提供所有基于RPC的应用的列表； 7、nmap -sR -sU -p 1-65535可以扫描目标机器的所有UDP端口，这会是个非常慢的过程，因为一般的标准Linux系统每秒只允许扫描20个UDP端口； 8、NIS服务器支持一个master，可以支持若干个slave服务器； 9、NIS协议是基于Sun RPC的，所以服务器和客户端都必须运行portmap，而且都必需加入同一个NIS域（其实就是一个预定义的统一的字符串）； 10、NIS可以的存储的东西相当于/etc/passwd、/etc/shadow、/etc/group中的内容； 11、NIS协议有1、2、3。NIS3又叫NIS+，在RHEL中只有客户端。服务器端只有版本1、2； 12、NIS虽然使用明文传输，安全性十分低下，但是由于其部署和维护简单，所以在低安全要求的企业环境中还是十分常用； 13、NIS的包名和进程名都是ypserv，同时rpc.yppasswdd和rpc.ypxfrd也是为它服务的。配置文件存在于/var/yp和/etc/yp*； 14、NIS到底不安全在哪？ a> 最大的问题是由于缺乏对客户端的认证机制和消息完整性校验机制，会泄露用户认证的信息，使用中间人的攻击方式伪装成认证服务器也不难；如果认证服务器都被伪装了，那后果的严重性可想而知（比如说更改UID为0取得root，删除已有用户防止正常人登陆，更改密码hash为已知值让自己登录）； b> 其次，默认的配置是所有人都可以把passwd文件dump出来。而passwd采用的md5方式已经被认可为不可逆的，Linux依然使用的原因是假设passwd文件是受到保护的。因此将passwd文件的内容暴露在网上是很不安全的，是可以被反向破解出来的。这里提供一个字典破解法：openssl passwd -table [...]
相关推荐：<ol>
<li><a href='http://blog.yikuyiku.com/?p=2930' rel='bookmark' title='RHCA培训散记（333网络安全 – 3）《关于DNS》'>RHCA培训散记（333网络安全 – 3）《关于DNS》</a></li>
<li><a href='http://blog.yikuyiku.com/?p=2899' rel='bookmark' title='RHCA培训散记（333网络安全 – 2）《关于加密》'>RHCA培训散记（333网络安全 – 2）《关于加密》</a></li>
<li><a href='http://blog.yikuyiku.com/?p=2888' rel='bookmark' title='Kerberos和Samba做的Windows域控制器有何区别'>Kerberos和Samba做的Windows域控制器有何区别</a></li>
<li><a href='http://blog.yikuyiku.com/?p=2859' rel='bookmark' title='RHCA培训散记（333网络安全 – 1）'>RHCA培训散记（333网络安全 – 1）</a></li>
<li><a href='http://blog.yikuyiku.com/?p=2747' rel='bookmark' title='RHCA培训散记（436存储与高可用集群 – 1）'>RHCA培训散记（436存储与高可用集群 – 1）</a></li>
</ol>]]></description>
			<content:encoded><![CDATA[<h2>NIS</h2>
<p><strong>1、NSS（Network Security Services）网络安全服务</strong>，包含SSL v2、SSL v3、 TLS、 PKCS#5、 PKCS#7、 PKCS#11、 PKCS#12、 S/MIME、 X.509v3 certificates和其它安全协议；</p>
<p><strong>2、网络登录也和其它的登录服务一样，分为验证和授权两部分。</strong>反应为密码和其它的信息（登录shell、UID、GID等）分开存放。一般的实现是用NIS（Network Information Service）存储其它信息（NIS也能存放密码，不过是明文传输的，所以不安全），用kerberos存放密码；</p>
<p><strong>3、上述的“其它信息”除了NIS，还可以选择使用LDAP</strong>（Lightweight Directory Access Protocol）、DNS（Hesiod记录）；</p>
<p><strong>4、我们通常说的Linux中的RPC其实是指SUN实现的Sun RPC</strong>，也叫ONC RPC（Open Network Computing Remote Procedure Call），可以建立在TCP或者UDP上，传输过程中数据会被编码成XDR（External Data Representation standard）（在RFC 1014中描述）格式；</p>
<p><strong>5、RPC中每个应用程序都有一个自己的Program ID</strong>，可以在文件/etc/rpc中找到所有的ID的列表。其中最常用到的两个是NFS和NIS；</p>
<p><strong>6、RPC的一个弱点</strong>是它会向其它的机器暴露自己可以提供所有基于RPC的应用的列表；</p>
<p><strong>7、nmap</strong> -sR -sU -p 1-65535可以扫描目标机器的所有UDP端口，这会是个非常慢的过程，因为一般的标准Linux系统每秒只允许扫描20个UDP端口；</p>
<p><strong>8、NIS服务器支持一个master</strong>，可以支持若干个slave服务器；</p>
<p><strong>9、NIS协议是基于Sun RPC的</strong>，所以服务器和客户端都必须运行portmap，而且都必需加入同一个NIS域（其实就是一个预定义的统一的字符串）；</p>
<p><strong>10、NIS可以的存储的东西</strong>相当于/etc/passwd、/etc/shadow、/etc/group中的内容；</p>
<p><strong>11、NIS协议有1、2、3。</strong>NIS3又叫NIS+，在RHEL中只有客户端。服务器端只有版本1、2；</p>
<p><strong>12、NIS虽然使用明文传输</strong>，安全性十分低下，但是由于其部署和维护简单，所以在低安全要求的企业环境中还是十分常用；</p>
<p><strong>13、NIS的包名和进程名都是ypserv</strong>，同时rpc.yppasswdd和rpc.ypxfrd也是为它服务的。配置文件存在于/var/yp和/etc/yp*；</p>
<p><strong>14、NIS到底不安全在哪？</strong><br />
a> 最大的问题是由于缺乏对客户端的认证机制和消息完整性校验机制，会泄露用户认证的信息，使用中间人的攻击方式伪装成认证服务器也不难；如果认证服务器都被伪装了，那后果的严重性可想而知（比如说更改UID为0取得root，删除已有用户防止正常人登陆，更改密码hash为已知值让自己登录）；<br />
b> 其次，默认的配置是所有人都可以把passwd文件dump出来。而passwd采用的md5方式已经被认可为不可逆的，Linux依然使用的原因是假设passwd文件是受到保护的。因此将passwd文件的内容暴露在网上是很不安全的，是可以被反向破解出来的。这里提供一个字典破解法：openssl passwd -table -1 -salt &#8216;SAMESALT&#8217; &#8211; stdin < /usr/share/dict/words | grep 'passwdhash'；<br />
c> 最后，即使攻击者不破解密码，dump出来的用户信息也给他进行下一步攻击提供了舞台；</p>
<p><strong>15、NIS的安全怎么做？</strong><br />
a> NIS基于UDP协议，所以不能使用TLS，只能使用IPsec（一组端对端的网络加密协议，在IPv6中是强制的，在IP4zhog是可选的，详见http://zh.wikipedia.org/wiki/IPsec）；<br />
b> NIS绝对不能暴露在公网或者无线网络中，要严格限制在私有的可信的网络里。限制的方法可以使用tcpwrapper，或者使用/var/yp/securenets来限制网段；<br />
c> 上述手段这样依然可以被IP伪装攻击绕过，所以在一定程度上要尽量不泄露NIS domain name（前述的那个字符串，有这个服务器才会响应你的请求）；<br />
d> 禁用广播的方式去发现NIS Server，而是使用指定NIS的域名或者IP的方式。以防止前述恶意伪造NIS Server的行为；<br />
e> 攻击者可以伪装NIS的响应，用已知的密码hash代替正确的NIS Server响应，这样攻击者就可以登入目标服务器了。要防止这种攻击，就要使用Kerberos（下述）来代替NIS存储密码的那部分功能了（这可以防止攻击者登入目标主机了，但这依然不能防止攻击者获取用户信息）；</p>
<h2>Kerberos</h2>
<p><strong>1、Kerberos是一个基于共享密钥对称加密的安全网络认证系统</strong>，它避免了将密码（包括密码hash）在网上传输，而是将密码作为对称加密的密钥，通过能不能解密来验证用户的身份；</p>
<p><strong>2、Kerberos在验证完用户身份后会发给用户Ticket</strong>，这个Ticket包含了用户的授权，用户拿着这个Ticket去享受各种服务，所以在Kerberos管理的范围内用户只需要登录一次就可以享用所有的服务；</p>
<p><strong>3、如果拿Kerberos作为集群服务器的登录管理</strong>，那么每台工作机（或者是跳板机，但是sshd不提供Kerberos的ticket初始化，需要自己改造一下）都必须是Kerberos于的成员；</p>
<p><strong>4、负责管理发放Ticket和记录授权的中心服务器被称为KDC</strong>（Key Distribution Center），它知道所有用户和服务的密码。这就是Kerberos服务器；</p>
<p><strong>5、类似NIS 的name domian（域）在Kerboeros被称为realm</strong>，Kerberos可以管理多个realm；</p>
<p><strong>6、在Kerberos域（realm）中每添加一个服务或者用户就要添加一条principal</strong>，每个principal都有一个密码。用户principal的密码用户自己记住，服务的principal密码服务自己记录在硬盘上（keytab文件中）；</p>
<p><strong>7、用户principal的命名</strong>类似elis/admin@EXAMPLE.COM，形式是用户名／角色／realm域。服务principal的命名类似ftp/station1.example.com@EXAMPLE.COM，形式是服务名／地址（提供者）／realm域；</p>
<p><strong>8、Kerberos的进程</strong>有krb5kdc（主进程，也就是KDC）、kadmind（用于远程管理pricipal数据库）、kpropd（slave同步数据用）。配置文件在/etc/krb5.conf和/var/kerberos/krb5kdc/下，数据库在/var/kerberos/krb5kdc/princical；</p>
<p><strong>9、Kerberos用户登录过程</strong>：<br />
a> 用户提供用户名密码；<br />
b> login程序（/usr/bin/login）把用户名转成princical名称，给KDC发送登录请求；<br />
c> KDC生成一个用于对称加密的session key，也叫TGT（Ticket-granting Ticket） key。KDC自己留一份，然后复制一份用用户的密码（其实是princical的密码）加密后传给用户（login程序）；<br />
d> login程序收到之后用刚才用户输入的密码解密，获得了TGT key。<br />
e> 认证过程完成，之后的通信使用TGT key加密；</p>
<p><strong>10、Kerberos用户获得TGT之后登录服务过程</strong>：<br />
a> 用户向KDC请求服务授权（用户和KDC的沟通是用TGT加密的）；<br />
b> KDC在验证princical通过后生成一个新的session key。将此key用用户的密码加密一次，再用服务的密码加密一次，把两次加密后的内容都发给用户；<br />
c> 用户把第一份自己能解开的用TGT解密后得到session key（用于和服务沟通）。然后用此session key加密一个时间戳（作为一个对服务的验证）和自己解不开的那份用服务的密码加密的session key一并发给服务；<br />
d> 服务用存储在keytab文件中的密码解密，也得到session key（这证明这玩意儿来自拥有自己密码的KDC）。然后解开用户加密的时间戳（这证明用户确实也拿到session key了，意即通过了KDC的允许）。这里面的时间戳用于防止重放攻击，所以所有的机器都必需使用安全的类似NTP的机制把时间同步好；<br />
e> 登录服务过程完成，整个过程中大家都不知道对方的密码，密码也没有在网络中传输过。之后用户与服务的通信使用session key来加密；</p>
<p><strong>11、可以使用DNS SRV记录来寻找realm</strong>，默认realm也可以通过DNS的TXT记录来设置。但是由于DNS还是比较容易被伪造和攻击，在配置文件中直接写上realm和默认realm是比较安全的；</p>
<p><strong>12、kerberos最初创建KDC的数据库</strong>（krb5_util create）时是需要密码的，以后管理KDC或更改princical都需要这个密码。但是Kerberos在系统启动时却并不会停下来请求用户输入密码。这是因为kerberos将密码存在一个藏起来的文件里面了（当然也可以让它不这么干，创建时不使用 -s选项就行）；</p>
<p><strong>13、KDC可以通过kadmin.local在本地进行管理，也可以通过kadmin远程管理</strong>。由于kadmin本身也可以视为一个服务，所以它也需要dump一份keytab（存储了它的密码）到本地来（存放在/var/kerberos/krb5kdc/kadm5.keytab），并用ktadd命令做好关联（此命令用于将KDC中的密码dump到本地）；</p>
<p><strong>14、kadmin</strong>可以通过/var/kerberos/krb5kdz/kadmin5.acl设置相当灵活的（控制可否添加、删除、修改、修改密码、查询、浏览principals）ACL；</p>
<p><strong>15、虽然默认使用UDP，但RHEL使用的MIT KDC也可以设置TCP端口同样监听</strong>。Kerberos服务的实现除了MIT KDC还有microsoft KDC（用于MS域）等；</p>
<p><strong>16、对称加密的算法可以在配置文件里设置；</strong></p>
<p><strong>17、支持Kerberos的服务</strong>有ftp、http、NFS、login、telnet等，大多数都需要专用的服务器端程序（声名自己支持kerberos的）；</p>
<p><strong>18、部署Kerberos服务</strong>只需要配置好krb5.conf（可以设置Ticket更新时间，是否forward ticket，最小UID之类的）后，在KDC中添加好princical，然后将密码dump回本地存为keytab给服务（如果服务原生不支持，但是支持pam的话可以尝试一下在pam中使用pam_krb5.so来间接支持）去用就ok了；</p>
<p><strong>19、临时Ticket被存储在/tmp／目录下</strong>，名称是krb5cc_UID；</p>
<p><strong>20、ktutil</strong>（其实就是一个查看keytab文件的工具）可以查看本地存储的密码的版本号（每改一次密码，版本号就会步进一次），kvno可以查看KDC中存储的密码的版本号（在客户端使用，有TGT才能用），一对比就能知道本地是否dump了最新的密码回来。这招在调试kerberos时很有用；</p>
<p><strong>21、kerberos自身的安全工作如何做？</strong><br />
a> kerberos的安全是建立在主机都是安全的而网络不是安全的假定之上的。所以kerberos的安全其实就在于把主机的安全做好；<br />
b> 尤其是KDC那台机器的安全。出于安全的考虑，跑KDC的机器上不能再跑别的服务，如果KDC被攻陷了，那么所有的密码就全部泄露了；<br />
c> 如果只是服务的机器被攻陷了，那么更改服务的principal的密码即可；<br />
d> 如果是用户的机器被攻陷了，那么在ticket超时（一般是数小时的时间里）之前，用户都是不安全的，攻击者还有可能尝试反向用户的密码；<br />
e> Kerberos依赖其它的服务来存放用户信息（登录shell、UID、GID啥的），因此需要注意到这些信息依然是很容易遭受攻击并且泄露的；</p>
<p>f> 由于任何人都可以向KDC请求任何用户的TGT（使用用户密码加密的session key），那么攻击者就有可能请求一个这样的包下来尝试解密，他们有充足的时间离线去做这个工作，一旦解开了，他们也就拿到了用户的密码。简单密码几乎一解就开，所以不能设置简单密码，也不能在字典里。另外还可以打开Kerberos的预验证机制来防御这种攻击，预验证机制就是在KDC收到用户请求TGT的请求之后，要求用户先发一个用自己密码加密的时间戳过来给KDC，KDC如果确实可以用自己存储的用户密码解密，才发TGT给用户，这样攻击者在没有用户密码的时候就拿不到可用于反向的包含用户密码的TGT包了。在MIT kerberos中在配置文件中default_principal_flags = + preauth可以打开这个机制。但这个机制也并不是无懈可击的，攻击者依然可以通过嗅探的方式在正常用户请求TGT时拿到上述的那个包（这个难度显然就高了一些）；</p>
<p>g> 还有一个问题是攻击者可能伪造一个KDC，然后用一个伪造的实际不存在的用户向这个KDC请求验证，通过后他就得到了一个用户登录系统的shell。这种攻击需要在客户端防御，需要客户端主动去验证一下KDC是否是正确的KDC。具体来说就是客户端在得到TGT后进一步要求KDC给一个本物理机的principal（也就是一个用物理机密钥加密的串），然后尝试用物理机存储的密码去解密，由于伪造的KDC没有物理机（host／hostname）的principal密码，所以它无法给出这个包，也就被客户端认定为是伪造的KDC，认证失败。这个机制需要在客户端开启（认证服务器端么），默认是关闭的，在krb5.conf里［appdefaults］章节里pam的部分中设置validate=true来开启；</p>
<p><strong>22、Kerberos可以在不同的realm之间建立信任关系</strong>，这样用户在一个realm中登录后就可以享用多个realm中的服务。简单的建立互信的方法是在2个域名都建立一条共享密码的名为krbtgt的principal。更好的建立互信的方法是多个realm都建立一条信任到自己的父realm，这样在子realm之间也能建立起信任；</p>
<p><strong>23、Kerberos还能向SSL帮助网络流量套上一层加密</strong>，比如说telnet这种明文协议就可能能用上。但是跟SSL或者SSH比起来安全性仍然略低，因为它的session key是用对称加密传输的（如果TGT被盗取了，流量的加密也就失效了），这在理论上增加了被嗅探破解的可能。而SSH和SSL协商session key采用的DH算法是理论安全的（被称为PFS即Perfect forward secrecy，详见http://en.wikipedia.org/wiki/Perfect_forward_secrecy）。还有一个问题是Kerberos采用的一些加密算法是已经不安全的了（如CRC32和DES），而另一些加密算法（AES）并没有被其他的kerberos实现广泛使用，所以会有一些兼容性的麻烦，选择加密算法时要避开这些算法；</p>
<p><strong>24、如果将princical密码设置为random</strong>，那么每次将其dump到本地keytab中时，都会random新的密码并dump到本地（可用于更新密码）；</p>
<p>相关推荐：<ol>
<li><a href='http://blog.yikuyiku.com/?p=2930' rel='bookmark' title='RHCA培训散记（333网络安全 – 3）《关于DNS》'>RHCA培训散记（333网络安全 – 3）《关于DNS》</a></li>
<li><a href='http://blog.yikuyiku.com/?p=2899' rel='bookmark' title='RHCA培训散记（333网络安全 – 2）《关于加密》'>RHCA培训散记（333网络安全 – 2）《关于加密》</a></li>
<li><a href='http://blog.yikuyiku.com/?p=2888' rel='bookmark' title='Kerberos和Samba做的Windows域控制器有何区别'>Kerberos和Samba做的Windows域控制器有何区别</a></li>
<li><a href='http://blog.yikuyiku.com/?p=2859' rel='bookmark' title='RHCA培训散记（333网络安全 – 1）'>RHCA培训散记（333网络安全 – 1）</a></li>
<li><a href='http://blog.yikuyiku.com/?p=2747' rel='bookmark' title='RHCA培训散记（436存储与高可用集群 – 1）'>RHCA培训散记（436存储与高可用集群 – 1）</a></li>
</ol></p><hr />
<p><small>© chen for <a href="http://blog.yikuyiku.com">陈钢的博客</a>, 2012. |
<a href="http://blog.yikuyiku.com/?p=2956">原文链接</a> |
<a href="http://blog.yikuyiku.com/?p=2956#comments"></a> 
<a href="http://blog.yikuyiku.com/?cat=3" title="查看 备忘 中的全部文章" rel="category">备忘</a>
<br/>
Post tags: <a href="http://blog.yikuyiku.com/?tag=333" rel="tag">333</a>, <a href="http://blog.yikuyiku.com/?tag=kerberos" rel="tag">kerberos</a>, <a href="http://blog.yikuyiku.com/?tag=nis" rel="tag">NIS</a>, <a href="http://blog.yikuyiku.com/?tag=nss" rel="tag">NSS</a>, <a href="http://blog.yikuyiku.com/?tag=rhca" rel="tag">RHCA</a>
<br/>
</small></p>]]></content:encoded>
			<wfw:commentRss>http://blog.yikuyiku.com/?feed=rss2&#038;p=2956</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>

