auto_ptr 和 shared_ptr

偶然的机会看到boost里还有一个shared_ptr型智能指针,费解,C++标准库里不是有auto_ptr了已经?我们先翻出源代码来看看。

先看auto_ptr,因为它比较简单。既然它在标准库里,那么我们就在gcc的代码中找,恩,最后确定在libstdc++-v3/include/backward/auto_ptr.h里面。说实话,auto_ptr真是太简单了,里面的私有变量就一个:

private:
_Tp* _M_ptr;

其它操作皆是围绕这个进行的,你自己猜都能猜到,看看吧,构造函数中的一个:
[cpp]
explicit
auto_ptr(element_type p = 0) throw() : _M_ptr(p) { }
[/cpp]
析构函数:
[cpp]
~auto_ptr() { delete _M_ptr; }
[/cpp]
重载的赋值运算符:
[cpp]
template
auto_ptr&
operator=(auto_ptr& a) throw()
{
reset(
a.release());
return
this;
}
[/cpp]
调用了自己的reset和release方法,按住不表,下面会介绍。重载的提领运算符:
[cpp]
element_type&
operator() const throw()
{
_GLIBCXX_DEBUG_ASSERT(_M_ptr != 0);
return
_M_ptr;
}
[/cpp]

成员运算符:
[cpp]
element_type*
operator->() const throw()
{
_GLIBCXX_DEBUG_ASSERT(_M_ptr != 0);
return _M_ptr;
}
[/cpp]

get()方法:

[cpp]
element_type*
get() const throw() { return _M_ptr; }
[/cpp]

release()方法:
[cpp]
element_type
release() throw()
{
element_type
tmp = _M_ptr;
_M_ptr = 0;
return
tmp;
}
[/cpp]

reset()方法:
[cpp]
void
reset(element_type* p = 0) throw()
{
if (
p != _M_ptr)
{
delete _M_ptr;
_M_ptr = __p;
}
}
[/cpp]

这些应该没什么难理解的,可能除了那个成员运算符,只说一句:C++会把foo->bar()翻译成:(foo.operator->())->bar()。

从上面我们可以看出,auto_ptr明显没有计数引用,而shared_ptr和它的最大区别就是它有计数。shared_ptr的源代码在boost/shared_ptr.hpp里。先看它的私有变量:

T * px; // contained pointer
boost::detail::shared_count pn; // reference counter

多了一个引用计数器,是一个boost::detail::shared_count类型,再翻源代码boost/detail/shared_count.hpp,只看用到的三个方法:
[cpp]
bool unique() const // nothrow
{
return use_count() == 1;
}

void swap(shared_count & r) // nothrow
{
    sp_counted_base * tmp = r.pi_;
    r.pi_ = pi_;
    pi_ = tmp;
}

long use_count() const // nothrow
{
    return pi_ != 0? pi_->use_count(): 0;
}

[/cpp]
呃,还有一个不能漏了,赋值运算符:
[cpp]
sharedcount & operator= (shared_count const & r) // nothrow
{
sp_counted_base * tmp = r.pi
;

    if( tmp != pi_ )
    {
        if( tmp != 0 ) tmp->add_ref_copy();
        if( pi_ != 0 ) pi_->release();
        pi_ = tmp;
    }

    return *this;
}

[/cpp]
再回过头去看shared_ptr的源代码:
[cpp]
reference operator () const // never throws
{
BOOST_ASSERT(px != 0);
return
px;
}

T * operator-> () const // never throws
{
    BOOST_ASSERT(px != 0);
    return px;
}

T * get() const // never throws
{
    return px;
}

bool operator! () const // never throws
{
    return px == 0;
}

bool unique() const // never throws
{
    return pn.unique();
}

long use_count() const // never throws
{
    return pn.use_count();
}

void swap(shared_ptr & other) // never throws
{
    std::swap(px, other.px);
    pn.swap(other.pn);
}

shared_ptr & operator=( shared_ptr && r ) // never throws
{
    this_type( static_cast( r ) ).swap( *this );
    return *this;
}

[/cpp]

这样一切都明了了。所以,auto_ptr和shared_ptr在使用上的区别也好理解了,下面的小程序可以展示:

[cpp]

include

include

include

using namespace boost;
using std::cout;
using std::endl;
using std::auto_ptr;

class A
{
public:
void print()
{
cout<<"hello"<<endl;
}
};

int main()
{
auto_ptr aptr1(new A);
auto_ptr
aptr2;

aptr2 = aptr1;
aptr2-&gt;print(); //Ok
cout&lt;&lt;&quot;pointer in aptr1 is: &quot;&lt;&lt;aptr1.get()&lt;print(); //Wrong!

A * a = new A;
shared_ptr<a> sptr1(a);
shared_ptr<a> sptr2(sptr1); //alright
sptr2 = sptr1;
sptr2-&gt;print(); //ok
sptr1-&gt;print(); //ok

int * b = new int;
shared_ptr sptr3(b);
shared_ptr sptr4(b); //WRONG!!

return 0;

}
[/cpp]

编程太难,我们去写脚本吧!

perl.com 上发布了Larry Wall的一篇文章,题目就是本文的标题,非常有意思。

文中从脚本语言的历史谈起,提到了脚本语言的一些特征,和设计脚本语言的方方面面。当然了,Larry写这篇文章的目的之一肯定就是鼓吹Perl6了,看来他是力挺Perl6的。总之,推荐读一下这篇文章。

另,很期待perl6的到来,不知道得等到哪一年的圣诞节啦~~!

对标准的一条评论

The standard is written in a variant of English sometimes called
standardese. It’s designed (or at least intended) to be
absolutely precise and unambiguous, even at the cost of
understandability. It doesn’t always succeed with its intent,
but in all cases, precision and a lack of ambiguity have
precedence over readability.
非常精辟的评价~!来自James Kanze。

总结08,展望09

今天又是年三十了,我觉得我应该写点什么来总结一下过去的这一年,也算对自己有个交代,以后看起来也方便。

08年对我来说是内容最丰富的一年。国内发生了那么多事,我也基本上都赶了,反正今年整个国家没消停……

我也没消停,出了次国,看了看外面的精彩世界,认识了很多外国友人,看到了很多异国风情,可是人家再好也是人家的国家,我的国家依旧如此……

毕业了,不再是学生了,虽然现在还经常被某些人错认为学生……

总算是工作了,不光不再花父母的钱了,而且还能给家里寄钱了,很高兴。可是花着自己挣的钱有时真比花父母给的钱还不舍得,虽然我一个月挣得够我爸挣好几年的。

生活上也不错。越来越喜欢旅游了,今年去了很多很多地方,希望这只是一个开始,以后还要去更多的地方,还要去继续探索这个大千世界……还有就是越来越懂得认真去生活了,工作之余和朋友出去吃个饭,喝个酒,去去酒吧……单身汉的生活就应该是这样吧?女朋友咱没有,钱也可以没有,兄弟们不能没有。:-)

技术上就不总结了,唉,越来越觉得自己学到的东西太简单,不是我谦虚,我感觉确实如此,感觉别人知道我不知道的东西,哪怕很少我也感觉他/她知道得比我多很多,我知道别人不知道的东西,即使很多我也觉得不过就那样……越学越傻了都……

总之我对08年挺满意的,希望09年可以更好。我不太喜欢列什么目标,因为我列了我也基本上完不成,不过为了在09年年末时看看自己到底有多少没完成,我还是列出来吧。:-P 09年的目标大体如下,排列分先后:

1. 找到一份新工作;

2. 找一个女朋友;

3. 如果2太难实现,可考虑:认识更多的新朋友;

4. 到更多的地方去旅游;

5. 如果因为种种原因4没完成,那么一定要完成这个:学到更多的知识(不可仅限计算机);

6. 如果上述目标都tmd完不成,至少至少,一定要完成这个:高高兴兴地生活。

我想至少最后一个还是比较好完成的,所以这么来看09年似乎可以完成目标。:-)

P.S. 如果经济很很很危机,找不到工作捏?那我就去卖煎饼果子,就像《血色浪漫》里的钟跃民!哈哈哈!

P.P.S. 千万别说我是有理想的好青年!我现在最大的理想是以后等有钱了自己去开家咖啡馆,找一个干净安静的小城市,然后知足地过一辈子……哈哈哈哈~!

=========================新年的分割线=========================

最后祝各位在新的一年里生活得更好,更开心,向着自己的目标继续奋斗!(别说心想事成,那太假,没有人能想什么就有什么。)

wine 了一把《红警2》

昨天和几个哥们出去玩去,本来说打死也不去网吧的结果最后还是跑到网吧去了,到了网吧后我发现我只有《红警》能玩,于是又疯狂了一把。回去后觉得意犹未尽,干脆在linux上装了个wine跑一个来玩,没想到wine 1.1.12对《红警2》支持那么好,图像音效什么的都很棒!就还有两个小问题:1. 速度有点慢;2. 每次鼠标到左上角的时候总是暂时“退出”该窗口,怀疑是个bug……

唉,想当年上高一的时候我对《红警2》那个疯狂啊~!当时我还搞到一张正版的《红警2》,上面录像很全,我认认真真地看了一遍,而且后来硬是把任务一关一关地都过了一遍。哎,往事不堪回首矣~!

reentrant,thread-safe 和 async-signal-safe

先上定义吧,POSIX对它们的定义分别是:

Reentrant Function

A function whose effect, when called by two or more threads, is guaranteed to be as if the threads each executed the function one after another in an undefined order, even if the actual execution is interleaved.

Thread-Safe

A function that may be safely invoked concurrently by multiple threads. Each function defined in the System Interfaces volume of IEEE Std 1003.1-2001 is thread-safe unless explicitly stated otherwise.

Async-Signal-Safe Function

A function that may be invoked, without restriction, from signal-catching functions. No function is async-signal-safe unless explicitly described as such.
可重入我们都清楚,顾名思义,就是可以重新进入,进一步讲就是,用相同的输入,每次调用函数一定会返回相同的结果。这就是可重入。wikipedia上)有更严谨的定义:

  • Must hold no static (global) non-constant data.
  • Must not return the address to static (global) non-constant data.
  • Must work only on the data provided to it by the caller.
  • Must not rely on locks to singleton resources.
  • Must not call non-reentrant computer programs or routines.
    然后是线程安全,从定义上看,它仅要求了可以安全地被线程并发执行。这是一个相对较低的要求,因为它内部可以访问全局变量或静态变量,不过需要加锁,也就是说,只要是在线程可控之中的,每次调用它返回不同的结果也没关系。到这里我们可以看出:可重入函数一定是线程安全的,而反之未必。wikipedia上也写道:
    Therefore, reentrancy is a more fundamental property than thread-safety and by definition, leads to thread-safety: Every reentrant function is thread-safe, however, not every thread-safe function is reentrant.
    例子,有很多,最出名的莫过于strtok(3),我们认识可重入这个概念就是从它开始的,它内部适用了静态变量,显然是不可重入的(它的可重入版是strtok_r(3))。其次应该是malloc(3),嘿嘿,其实也很明显,我就不多说了。但是,strtok(3)不是线程安全的,而malloc(3)是。

还有一个概念我们不常碰到,那就是异步信号安全,它其实也很简单,就是一个函数可以在信号处理函数中被安全地调用。看起来它似乎和线程安全类似,其实不然,我们知道信号是异步产生的,但是,信号处理函数是打断主函数(相对于信号处理函数)后执行,执行完后又返回主函数中去的。也就是说,它不是并发的!

一个函数,它访问了全局变量,那么它就是不可重入的,不过我们可以把它变成线程安全的,加上锁就可以,但是这种方法并不会把它变成异步信号安全的,而几乎可以肯定的是,使用了锁的一定不是信号安全的(除非屏蔽了信号,显然),信号完全可以在锁加上后解开前到来,然后就很可能形成死锁…… 这里有个很好的例子。所以,可重入的函数也一定是异步信号安全的,而反之未必。可以参考IBM上一篇不错的文章

关于异步信号安全的函数列表可以参考man 7 signal;关于线程安全的函数列表可以参考APUE第12.5节 ;关于可重入函数列表,可参考APUE第10.6节。另请参阅

用 sigaction(2) 的原因

Linux man page中提到:

The behavior of signal() varies across Unix versions, and has also varied
historically across different versions of Linux. Avoid its use: use sigaction(2) instead.

The only portable use of signal() is to set a signal’s disposition to SIG_DFL or SIG_IGN. The semantics when using signal() to establish a signal handler vary across systems (and POSIX.1 explicitly permits this variation); do not use it for this purpose.
所以signal(2)的可移植性首先就差了一截子。还有就是,signal(2)也不能像sigaction(2)那样去屏蔽信号,POSIX中提到:
When a signal is caught by a signal-catching function installed by
sigaction(), a new signal mask is calculated and installed for the
duration of the signal-catching function (or until a call to either
sigprocmask() or sigsuspend() is made). This mask is formed by taking
the union of the current signal mask and the value of the sa_mask for
the signal being delivered unless SA_NODEFER or
SA_RESETHAND is set, And then including the signal being
delivered. If and when the user’s signal handler returns normally, the
original signal mask is restored.

sparse 的一个 bug

今天用sparse的时候遇到一个bug:

error: unable to open ‘stddef.h’

简单地搜了搜,发现debian上已经有人报告,最新的sparse的git树里已经解决了这个问题,可是Fedora还没有。于是只好自己编译,git-clone之后 make && make install PREFIX=/usr 就可以了。希望Fedora也尽快更新sparse。

Linux分区个数限制

在linux上,对于IDE硬盘来说,分区个数限制在63个;而对于SCSI硬盘限制在15个。

我们可以直接在linux内核源代码中找到证据:

include/linux/ide.h

define PARTN_BITS 6 / number of minor dev bits for partitions /

次设备号也就决定了可挂载的IDE设备不会超过63个(2^6-1,之所以减一是因为hdX本身也会占用一个)。

SCSI也差不多,见 drivers/scsi/sd.c

if !defined(CONFIG_DEBUG_BLOCK_EXT_DEVT)

define SD_MINORS 16

else

define SD_MINORS 0

endif

在内核文档中也可以看到,见Documentation/devices.txt :

For partitions, add to the whole disk device number:
0 = /dev/hd? Whole disk
1 = /dev/hd?1 First partition
2 = /dev/hd?2 Second partition

63 = /dev/hd?63 63rd partition

8 block SCSI disk devices (0-15)

Partitions are handled in the same way as for IDE
disks (see major number 3) except that the limit on
partitions is 15.

IDE, ATA, SCSI 等等

关于硬盘的术语有一大堆,而且和很多别的计算机术语一样,界定不清,含义模糊。在这里简单澄清一下,但我不排除你看了之后会更糊涂。:^)

IDE:Integrated Drive Electronics 的缩写。最早的硬盘接口,后来被 ATA 标准所取代,准确地讲应该是 ATA-1。所以有时也被称为 IDE/ATA,可参见其历史介绍。后来,几乎和ATA-1同时出现的 EIDE(Enhanced IDE )又被规范化为 ATA-2。

ATA:Advanced Technology Attachment 的缩写,存储设备(并非仅限硬盘)的接口标准。有时也被称为 IDE。IDE 在 ATA 标准出现之前就有了,所以估计这个也是因为历史原因。不过根据wikipedia记载,这两个术语还是有些许不同之处的:

The terms “integrated drive electronics” (IDE), “enhanced IDE” and “EIDE” have come to be used interchangeably with ATA (now Parallel ATA). However the terms “IDE” and “EIDE” are at best imprecise. Every ATA drive is an “integrated drive electronics” drive, but not every “integrated drive electronics” drive is an ATA drive, as the term also correctly describes SCSI drives: They have the drive controllers on board and present the drive to the host as an array of blocks.
后来就都是 ATA 了。又因为 SATA(Serial ATA,即 ATA-7)的出现,所以老的 ATA 又都被称为 PATA(Parallel ATA)了。

SCSI:Small Computer System Interface 的缩写,另一个完全不同的标准,定义了内部的命令,协议和接口。SCSI更快,当然也更贵,多用在中,高性能服务器上,不过apple的mac也是最早使用它的计算机之一。新的SCSI标准,Serial Attached SCSI又被简称为SAS

SCSI 和前面的总起来并列为三个标准:PATA,SATA,SCSI。其中SATA应该是会继续流行的PC机硬盘接口。

HDD:Hard Disk Drive 的缩写,就是我们通常所说的硬盘,磁盘。

SSD:Solid State Drive 的缩写,通常我们称为固态硬盘。SSD耗电少,抗震性能好,但比起HDD来要贵一些,而且容量通常也不像HDD那么大。

RAID:Redundant Array of Independent Disks 的缩写,就是我们通常所说的磁盘阵列。RAID是对多块硬盘的组合,需要硬件支持(貌似也有从文件系统层支持RAID的,btrfs?)。根据不同的组合方式,RAID又分为RAID0到RAID7,详细参考这里

参考:

http://www.91wp.com/Article/jswz/200704/350.html
http://www.pcbuyerbeware.co.uk/IDE.htm
http://www.pcguide.com/ref/hdd/if/ide/over.htm
http://en.wikipedia.org/wiki/RAID
http://en.wikipedia.org/wiki/SCSI
http://en.wikipedia.org/wiki/AT_Attachment