Unix环境高级编程(第二版)
第三章、文件IO
Open函数的O_APPEND为每次写时都追加到文件的尾端,此操作为原子操作,因此可以多进程同时以此标志打开进行写操作。
Open函数的O_CREAT与O_EXCL同时使用时,相当于测试文件是否存在,此操作为原子操作。
O_RSYNC: 使每一个以文件描述符作为参数的read操作等待,直到任何对文件同一部分进行的未决写操作都完成。
(比如,进程A在文件1的100字节处进行写操作,而进程B刚好正要在100字节处进行读操作,那么使用了该标志位以后,进行B会等待进行A的操作完成,以达到同步效果)
O_DSYNC: 使每次write操作等待物理IO操作完成,但是如果写操作并不影响读取写入的数据,则不等待文件属性被更新。
O_SYNC: 使每次write操作等待物理IO操作完成,包括由write操作引起的文件属性被更新所需的IO。
(区别:在重写现有文件内容时,O_DSYNC标志的操作并不会更新文件属性信息,但是O_SYNC标志的操作会更新文件属性,比如文件修改时间)
Creat函数不足之处在于只能以只写方式打开所创建的文件。可以使用
Open(filename, O_RDWR|O_CREAT|O_TRUNC,mode);
Lseek不能对管道、FIFO和网络套接字进行定位。
如何对文件进行原子读写操作?
1: 以O_APPEND标志打开
2. 使用原子函数pread和pwrite函数。

其中的节点信息就是指:文件大小、文件创建、修改时间等等。
Dup和dup2函数复制一个新的文件描述符,但是两个文件描述符共享一个文件表,即当其中一个文件描述符使用lseek定位时,另一个文件描述符的当前文件偏移量也就变了。如下图所示:
Linux内核里有IO缓冲区,为了避免掉电时可以使用下面fsync(int fd)函数强制写入物理磁盘。
第7章、环境变量
一个典型的C程序存储空间布局由以下几个部分组成:
正文段:CPU执行的指令部分,也就是主要的程序代码编译出来的结果,只读,通常可以共享。
初始化数据段:通常称之为数据段,包含了程序中需要明确赋值的变量,譬如一些初始化的全局变量等,如 int a = 10,变量名和值都存放在这个段中。
未初始化数据段:通常称之为BSS(BlockStarted by Symbol)段,包含了程序中没有进行赋值的变量,譬如一些未初始化的全局变量,如 int a,在程序执行之前,内核会把这部分全部置为0(NULL),
栈:自动变量以及每次函数调用时所需保存的信息放在此段中。如函数调用时要保存返回地址等。栈是从上向下分配的。
堆:通常在堆中进行动态存储分配,如malloc,calloc, realloc等都从这里面分配。堆是从下向上分配的。
通常堆顶和栈底之间的虚拟地址空间是很大的。
对X86处理器上的Linux,正文段从0x08048000开始,栈底则从0xC0000000之下开始。
下图是一个典型的C程序存储空间的逻辑布局:
//main.cpp
int a = 0; 全局初始化区
char *p1; 全局未初始化区
main()
{
int b; 栈
char s[] = "abc"; 栈
char *p2; 栈
char *p3 = "123456"; 123456/0在常量区,p3在栈上。
static int c =0;全局(静态)初始化区
p1 = (char *)malloc(10);
p2 = (char *)malloc(20);
分配得来得10和20字节的区域就在堆区。
strcpy(p1, "123456"); 123456/0放在常量区,编译器可能会将它与p3所指向的"123456"优化成一个地方。
}
进程资源限制:
getrlimit和setrlimit函数。
Struct rlimit
{
rlim_trlim_cur; //软限制
rlim_trlim_max; //硬限制
}
第8章、进程控制
Fork函数之后父子进程的区别。
Vfork函数保证子进程先运行,直到子进程结束后才继续运行父进程.并且子进程和父进程共享数据。
进程cpu时间times函数,
Clock_ttimes(struct tms *buf);
Struct tms
{
Clock_ttms_utime;//用户cpu时间
Clock_t tms_stime;//系统cpu时间
Clock_ttms_cutime;//子进程用户cpu时间
Clock_ttms_sutime;//子进程系统cpu时间
}
第10章信号
被中断的系统调用处理方式:
again:
if((n = read(fd, buf, BUFFSIZE)) < 0)
{
if (errno = EINTR)
goto again;
}
类似的函数还有ioctl/write/readv等
可重入函数:
是指在信号处理函数中调用函数的可靠性。
事实证明,在信号处理程序中调用一个不可重入的函数,则其结果是不可预见的。
第11章 线程
线程可以安排它退出时需要调用的函数,这个和进程可以使用atexit函数一样。处理程序记录的栈中,也就是说它们的执行顺序与它们注册时的顺序相反。
Void pthread_cleanup_push(void (*rtn)(void*), void *arg);
Void pthread_cleanup_pop(int execute);
如果execute参数设置为0,清理函数不被调用,但无论如何,都将删除上次push调用建立的清理处理程序。
这个函数有个限制,由于它们可以实现为宏,所以必须在于线程相同的作用域中以匹配的形式使用,push函数的宏定义可包括字符{,在这种情况下对应的匹配字符}就要在pop定义中出现。
在默认情况下,线程的终止状态会保存到对该线程调用pthread_join,如果线程已经处于分离状态,线程的底层存储资源可以在线程终止时立即收回。当线程被分离时,并不能用join函数等待它的状态终止,会被返回一个失败,返回EINVAL。Pthread_detach调用可以用于使线程处于分离状态。
Int pthread_detach(pthread_t tid);