2009年1月31日星期六

stream学习

通过stream实现文件复制
经典的方法是借助一个指定大小的Buffer,调用fopen,fread,fwrite,fclose。
通过STL的i/o stream可以简洁的实现文件复制:
ifstream ifs("infilename");
ofstream ofs("outfilename");
ofs << inf.rdbuf();
原来是有很多书还没有看过Effective STL就是其中之一。其中的Item17就是之前在DevX上看到通过swap来去除vector过剩容量的内容。而Item29就是这里的ifstreambuf_iterator>复制文本文件到字符串中。
ifstream instream("txt.txt");
string input(
istreambuf_iterator(instream.rdbuf()),
istreambuf_iterator()
);
又引出istreambuf_iterator的一个特殊之处:如果一个istreambuf_iterator对象到达stream的末尾或者由默认构造函数构造生成,则它的值为end-of-stream,类似于文件操作中的EOF。上面的string构造函数的第二个参数就是利用了这一点。
这里用到的是string的构造函数:template string (InputIterator begin, InputIterator end);

std::find学习

死循环的原因大概找到了,是std::find使用不当的问题。
SGI的说明中函数原型为:
templateInputIterator, class EqualityComparable>
InputIterator find(InputIterator first, InputIterator last,
const EqualityComparable& value);
函数返回值为[first, last)范围内,第一个值等于value的迭代器。如果不存在,则返回last。前提是first与
last之间是一个有效地范围。没有提到first位于last之后的情况。但就实际情况来说,如果出现这种情况,仍
然返回last。环境为Linux,libstdc++.so.6。
原先的代码

1 inline T * Find(const T& v) {
2 T * p = std::find(objs, objs + count, v);
3 while (p && p->Full())
4 {
5 p = std::find(p + 1, objs + count, v);
6 }
7 return (p == objs + count) ? 0 : p;
8 }

问题在于,如果查找的对象不存在,第2行返回值为objs+count。而进入循环进行,条件检测均可以满足。接下来第5行,由于p + 1已经位于objs + count之后,所以返回值依然为objs + count。从而陷入了死循环。
可能是原作者也存在和我同样的误解,武断的认为如果不存在满足查找条件的对象,std::find会返回空指针。
修改后的代码如下,也就是多加了一项对于p是否为objs+count的判断。

inline T * Find(const T& v) {
T * p = std::find(objs, objs + count, v);
while (p && (p != objs + count) && p->Full())
{
p = std::find(p + 1, objs + count, v);
}
return (p == objs + count) ? 0 : p;
}

之前基本上是学习原有代码,这个算是又一点不同的收获吧。

STL map的一些注意

STL map一些内部的机制如果不注意或不了解很容易造成困扰。
特别是取下标操作符data_type& operator[](const key_type& k)。在sgi的文档里清楚的写着:由于operator[]可能向map插入新元素,所以不能是const成员函数。而m[k]实际上相当于((((m.insert(value_type(k, data_type()))).first)).second。严格来说,这个成员函数不是必需的,只是为了简便使用。(也带来了一些困扰和开销)。
typedef map INT_MAP;
INT_MAP iMap;
iMap[3] = "three";

插入3时,先在iMap中查找主键为3的项,没发现,然后将一个新的对象插入iMap,键是3,值是一个空字符串,插入完成后,将字符串赋为"three"; 该方法会将每个值都赋为缺省值,然后再赋为显示的值,如果元素是类对象,则开销比较大。可以用以下方法来避免开销:
enumMap.insert(map :: value_type(3, "three"))

同样,以string tmp = iMap[3];的操作来获取一个键值的对应值也存在问题。只有当map中有这个键值才成立,否则自动插入一个实例,值为默认初始值。

crtDbgFlag

在Windows中检查内存泄露的代码
_CrtSetDbgFlag(_CRTDBG_LEAK_CHECK_DF | _CrtSetDbgFlag(_CRTDBG_REPORT_FLAG));
以下来自msdn

_crtDbgFlag 标志包含下列位域:

位域默认值说明
_CRTDBG_ALLOC_MEM_DFOn打开调试分配。当该位为 off 时,分配仍链接在一起,但它们的块类型为 _IGNORE_BLOCK
_CRTDBG_DELAY_FREE_MEM_DFOff防止实际释放内存,与模拟内存不足情况相同。当该位为 on 时,已释放块保留在调试堆的链接列表中,但标记为 _FREE_BLOCK,并用特殊字节值填充。
_CRTDBG_CHECK_ALWAYS_DFOff导致每次分配和释放时均调用 _CrtCheckMemory。这将减慢执行,但可快速捕捉错误。
_CRTDBG_CHECK_CRT_DFOff导致将标记为 _CRT_BLOCK 类型的块包括在泄漏检测和状态差异操作中。当该位为 off 时,在这些操作期间将忽略由运行时库内部使用的内存。
_CRTDBG_LEAK_CHECK_DFOff导致在程序退出时通过调用 _CrtDumpMemoryLeaks 来执行泄漏检查。如果应用程序未能释放其所分配的所有内存,将生成错误报告。

2009年1月29日星期四

real, effective, saved UID

要学的还有太多,以下是转载。

Linux系统下与进程相关的ID有:
real user ID,
real group ID,
effective user ID,
effective group ID
saved set-user-id,
saved set-group-id
1. real user ID, real group ID
用于标识启动进程的用户的真实身份。这个ID与该用户在/etc/passwd中的id一致。
2. effective user ID, effective group ID
用于权限检查。比如,进程希望对某个文件进行操作时,需要将该进程的这两个ID与文件的访问权限位进行比较。通常情况下,effective ID和real ID相等。
但是,如果process image file设置了setuid, 那么在exec()时,进程的effective ID将被设置为该image file的owner 的user/group ID。【注意,此处与文件访问权限的setuid标志位密切相关!】
3. saved set-use-rid, saved set-group-id
在exec时,一旦设置了effective uid/gid, 将保存一个copy,作为saved set user/group id
保存值可在必要的时候得以恢复。
基本上,user/group ID主要用于身份标识和权限检查。
与ID设置相关的系统调用包括:
setuid(),setgid(),
seteuid(),setegid(),
setreuid(), setregid()
setresuid(), setresgid()

首先需要明确一点,这几个概念都是和进程相关的.
real user ID表示的是实际上进程的执行者是谁,effective user ID主要用于校验该进程在执行时所获得的文件访问权限,也就是说当进程访问文件时检查权限时实际上检查的该进程的"effective user ID",saved set-user-ID 仅在effective user ID发生改变时保存.

一般情况下,real user ID就是进程的effective user ID,但是当要运行的可执行程序设置了"set-user-ID" 位之后,进程的effective user ID变成该文件的属主用户id,同时该进程的"saved set-user-ID"变成此时进程的"effective user ID",也就是该可执行程序的属主用户ID,该进程在执行一些与文件访问权限相关的操作时系统检查的是进程的effective user ID.

为什么需要一个"saved set-user-ID"?因为当进程没有超级用户权限的时候,进程在设置"effective user ID"时需要将需要设置的ID和该进程的"real user ID"或者"saved set-user-ID"进行比较.

APUE2中进行的解释是:
1)If the process has superuser privileges, the setuid function sets the real user ID, effective user ID, and saved set-user-ID to uid.

2)If the process does not have superuser privileges, but uid equals either the real user ID or the saved set-user-ID, setuid sets only the effective user ID to uid. The real user ID and the saved set-user-ID are not changed.

3)If neither of these two conditions is true, errno is set to EPERM, and 1 is returned
也就是说:
1)当用户具有超级用户权限的时候,setuid 函数设置的id对三者都起效.
2)否则,仅当该id为real user ID 或者saved set-user-ID时,该id对effective user ID起效.
3)否则,setuid函数调用失败.

也就是说,这个saved set-user-ID更多的作用是在进程切换自己的effective user ID起作用.

需要特别提醒的是:并没有任何的API可以获取到进程的saved set-user-ID,它仅仅是系统在调用setuid函数时进行比较而起作用的.
APUE2中关于此事的原话如下:
Note that we can obtain only the current value of the real user ID and the effective user ID with the functions getuid and geteuid from Section 8.2. We can't obtain the current value of the saved set-user-ID.


举一个例子说明问题,假设这样的一种情况,系统中有两个用户A,B,还有一个由B创建的可执行程序proc,该可执行程序的set-
user-id位已经进行了设置.

当A用户执行程序proc时,
程序的real user ID = A的用户ID,effective user ID = B的用户ID, saved set-user-ID=B的用户ID.

假如在该进程结束了对某些限制只能由用户B访问的文件操作后,程序将effective user ID设置回A,也就是说此时:
程序的real user ID = A的用户ID,effective user ID = A的用户ID, saved set-user-ID=B的用户ID.

这个改动之所以能成功,原因在于上面列举出的情况2):该ID为进程的real user ID.

最后,假设由于种种原因进程需要再次切换effective user ID为B,可是因为不能通过API获取进程的saved set-user-ID(该值为B的用户ID),所以只能通过两种途径获得(可能还有别的途径):
a)在设置effective user ID变回A之前保存effective user ID,它的值为B的用户ID.
b)调用函数getpwnam( "B"),在返回的struct passwd *指针中成员pw_uid存放的就是用户B的ID.
这样,这个调用setuid(B的用户ID)就会成功,原因也在于上面说的情况2):该ID与进程的saved set-user-ID相同.

APUE2中关于这几个值的相关解释在section4.4和section8.11中都有涉及.


2009年1月19日星期一

UIProgressView(zt)

很好的例子,来自http://blogs.oreilly.com/digitalmedia/2008/03/open-iphone-sdk-building-a-uip.html
Progress bars allow end-users to anticipate wait times. They present bars that fill from left to right. These bars indicate the degree to which a task has finished. Progress bars work best for long waits where providing state feedback allows your users to retain the feel of control.

To create a progress bar, allocate it and set its frame. To use the bar, issue setProgress:. This takes one argument, a floating point number that ranges between 0.0 (no progress) and 1.0 (finished). Progress bars come in two styles: basic white (style 0) or light gray (style 1). setStyle: chooses the kind your prefer.Unlike the other kinds of progress indicators, it’s completely up to you to show and hide the progress bar’s view. I like adding progress bars to alert sheets. This simplifies both bringing them onto the screen and dismissing them. Another advantage is that when alert sheets display, the rest of the screen dims. This forces a modal presentation as your task progresses. Users cannot interact with the GUI until you dismiss the alert.

@implementation SampleApp
#define STYLE 0

- (void) handleTimer: (id) timer
{
    amt += 1;
    [progbar setProgress: (amt / 20.0)];
    if (amt > 20.0) {[alert dismiss]; [timer invalidate];}
}

- (void) applicationDidFinishLaunching: (id) unused
{
    UIImage *img = [UIImage applicationImageNamed:@"Default.png"];
    UIImageView *imgView = [[UIImageView alloc] initWithImage:img];

    struct CGRect rect = [UIHardware fullScreenApplicationContentRect];
    UIWindow *window = [[UIWindow alloc] initWithContentRect: rect];
    [window orderFront: self];
    [window makeKey: self];
    [window setContentView: imgView];

    // Create an otherwise-empty alert sheet
    alert = [[UIAlertSheet alloc] 
initWithTitle:@"Updating Database"
buttons:NULL
defaultButtonIndex:0
delegate:self
context:self];
    [alert setBodyText:@"Please wait...\n\n\n\n"];

    // Create the progress bar and add it to the alert
    progbar = [[UIProgressBar alloc] initWithFrame:
                 CGRectMake(50.0f, 70.0f, 220.0f, 90.0f)];
    [alert addSubview:progbar];
    [progbar setStyle: STYLE];
    [alert presentSheetInView:imgView];

    // This timer takes the place of a real task
    amt = 0.0;
    id timer = [NSTimer scheduledTimerWithTimeInterval: 0.5
                 target: self
                 selector: @selector(handleTimer:)
                 userInfo: nil
                 repeats: YES];
}
@end