unix环境高级编程_学习笔记

相关书籍:

c专家编程、tcp/ip卷一、深入理解计算机系统、Linux/UNIX系统编程手册、APUE

涉及知识:

c语言基础、linux shell

内容补充:

1、第一章有一张,应用程序,库,shell,内核之间的关系图。这张图相当关键,必须搞懂。
2、第二章讲ISO C POSIX SUS 规范之间的关系,与定义,这些是UNIX系统规范,注意理解。如果初学稍微记一记。完全理解不太可能。但是要理解,后面章节所有的内容都是围绕这些内容来介绍的。规范如何定义,实现如何满足规范。
3、第三章、第五章关于IO的理解,注意广义IO与非广义IO的区别,一般学C的时候,IO仅仅是标准IO与文件IO。而第三章与第五章说的IO还有,套接字、终端、打印机等等这些可以叫设备IO。
4、第四章,如果对linux使用了解深刻,理解上不是大问题,主要在介绍linux一切结文件的理念。
5、第六章,需要先对linux有较深的了解。
6、目前我看到第七章,这里会有C专家编程说过的很多内容,如果能理解C专家编程上说过的内容,这章很容易理解。
7、说到底无非是进程、文件、虚存以及网络。
8、进程控制 ,线程控制,进程环境,进程通信 讲得尤为精彩,网络 部分讲得比较浅,但也够用

Unix环境高级编程要点笔记

第5章 标准I/O库

标准I/O库职责:

处理细节,例如缓冲区分配(优化长度执行I/O)不用担心块长度的选择。

流和File对象:

当使用标准I/O库打开或创建一个文件时,我们已使一个流与一个文件相关联。
标准I/O文件流可用于单字节或多字节(“宽”)字符集。
流的定向决定了所读写的字符是单字节还是多字节的。freopen清楚流的定向,fwide设置流的定向。
w或a创建新文件时,无法说明文件的访问权限位

输入一行:fgets gets

Gets不能指定缓冲区的长度,会造成缓冲区溢出;并且不能将换行符存入缓冲区中。
Fgets必须指定缓冲区的长度n。此函数一直读到下一个换行符为止,但是不超过n-1个字符。读入的字符被送入缓冲区。该缓冲区以null字符结尾。如若该行(包括最后一个换行符)的字符数超过n-1,则fgets只返回一个不完整的行,但是缓冲区总是以null字符结尾。对fgets的下一次调用会继续读该行。

输出函数:Fputs puts

puts将一个以null符终止的字符串写到标准输出,终止符不写出。但是,puts然后又讲一个换行符写道标准输出。
Fputs 同puts,但不一定是每次输出一行,因为它不要求在null符之前一定是换行符。

输入一个字符:Getchar getc fgetc ungetc clearerr

Getchar==getc(stdin)前两个函数的区别是getc可被实现为宏,而fgetc则不能。这意味着getc的参数不应当是具有副作用的表达式;且因为fgetc一定是一个函数,所以可以得到其地址。这就允许将fgetc的地址作为一个参数传送给另一个函数;调用fgetc所需时间可能长于调用getc,因为调用函数通常所需的时间长于调用宏。

Putchar putc fputc

补充

printf和scanf是格式化输出输入格式转换

第7章 进程环境

main函数

原型是

int main(int argc, char *argv[]);//分别代表命令行参数的数量与指针

当内核执行c程序时(使用exec函数),在调用main前先调用一个特殊的启动例程。可执行程序文件将此启动例程指定为程序的起始地址。这是由连接
编辑器设置的,二连接编辑器则由C编译器(通常是cc)调用。启动例程从内核取得命令行参数和环境变量值,然后为上述方式调用main函数做好安排。

进程终止

8种方式终止进程:
-正常终止
从main返回
调用exit
调用_exit或_Exit
最后一个线程从其启动例程返回
最后一个线程调用pthread_exit
-异常终止
调用abort
接到一个信号并终止
最后一个线程对取消请求做出响应

在一个进程终止时,内核逐个检查所有活动进程,以判断它是否正要终止进程的子进程,如果是,则将该进程的父进程ID更改为1(init进程的ID)。
内核为每个终止子进程保存了一定量的信息,所以当终止进程的父进程调用wait或waitpid时,可以得到这些信息。
在Unix术语中,一个已经终止,但是其父进程尚未对其进行善后处理(获取终止子进程的有关信息,释放它仍占用的资源)的进程被称为僵死进程(zombie)。
无论何时只要有一个子进程终止,init就会调用一个wait函数取得其终止状态。防止系统中有很多僵死进程。
当提及一个init的子进程时,可能是init直接产生的进程(例如getty进程),也可能是其父进程已终止,由init领养的进程。

wait和waitpid
  1. 如果其所有子进程都还在运行,则阻塞。
  2. 如果一个子进程已终止,正等待父进程获取其终止状态,则取得该子进程的终止状态立即返回
  3. 如果它没有任何子进程,则立即出错返回
  4. 区别在于。在一个子进程终止前,wait使调用者阻塞,而waitpid可选择不阻塞,
  5. 区别在于。waitpid并不等待在其调用之后的第一个终止子进程,它有若干个选项,可以控制它所等待的进程。
  6. 区别在于。waitpid可等待一个特定的进程,而wait则返回任一终止子进程的状态。
  7. 区别在于。waitpid提供一个sait的非阻塞版本。有时用户希望取得一个子进程的状态,但不想阻塞。
  8. waitpid支持作业控制(利用WUNTRACED和WCONTINUED选项)

环境表

是一个字符指针数组。其中每个指针包含一个以null结束的C字符串的地址。全局变量environ则包含了该指针数组的地址。

extern char **environ

C程序的存储空间布局(由低到高)

-正文段
-初始化数据段
-非初始化数据段(bss段)
-堆
-栈
高地址还有命令行参数和环境变量

共享库

优点

1.共享库使得可执行文件中不再需要包含公用的库例程,而只需在所有进程都可引用的存储区中维护这种库例程的一个副本。
程序第一次执行或者第一次调用某个库函数时,用动态链接方法将程序与共享库函数相链接。这减少了每个可执行文件的长度,但增加了一些运行时间开销。这种时间开销发生在该程序第一次被执行或者每个共享库函数第一次被调用时。
2.可以用库函数的新版本代替老版本。

存储器分配

1.malloc 分配指定字节数的存储区,此存储区中的初始值不稳定。
2.calloc 为指定数量具指定长度的对象分配存储空间。该空间中的每一位都初始化为0
3.realloc 更改以前分配区的长度。当增加长度时,可能需将以前分配区的内容移到另一个足够大的区域,以便在尾端提供增加的存储区,而新增区域内的初始值则不确定。

#include <stdlib.h>
void *malloc(size_t size);
void *calloc(size_t nobj, size_t size);
void *realloc(void *ptr, size_t newsize);
//三个函数返回值:若成功则返回非空指针, 若出错则返回NULL。
void free(void *ptr);

函数free释放ptr指向的存储空间。被释放的空间通常被送入可用存储区池,以后,可在调用上述三个分配函数时再分配。
可能产生的致命错误:
1.在动态分配的缓冲区前或后进行写操作,破坏的可能不仅仅是该区的管理记录信息。在动态分配的缓冲区前后的存储区很可能用于其他动态分配的对象。这些对象与破坏它们的代码可能无关,这造成寻求信息破坏的源头更加困难。
2.释放一个已经释放了的块
3.调用free时所用的指针不是三个alloc函数的返回值等。
泄露:即如果一个进程调用malloc函数,却忘记调用free函数,则该进程占用的存储器就会连续增加。

感想:完善分配器出错问题可以改善安卓性能衰减?

替代(malloc和free)的函数:
1.libmalloc
2.vmalloc
3.快速适配(quick-fit):趋向于使用较多的存储器。将存储器分裂成各种长度的缓冲区,并将未使用的缓冲区按其长度组成不同的空闲区列表。在许多ftp网站上可方便地取用基于快速适配的malloc和free开放源代码实现。
4.alloca函数:优点是当函数返回时,自动释放它所使用的栈帧。缺点是增加了栈帧的长度,而某些系统在函数已被调用后不能增加栈帧长度,于是不能支持alloca函数。

第8章 进程控制

延迟重用算法:多数Unix系统中,使赋予新建进程的ID不同于最近终止进程所使用的ID。
ID为0的进程通常是调度进程,常被称为交换进程。该进程是内核的一部分,不执行任何磁盘上的程序,也被称为系统进程。
ID为1的进程通常是init进程,在自举过程结束时由内核调用。该进程的程序文件在早期Unix的早期版本中是/etc/init,在较新版本中是/sbin/init。此进程负责在自举内核后启动一个Unix系统。
init通常读与系统有关的初始化文件(/etc/rc*;/etc/inittab;/etc/init.d),并将系统引导到一个状态(例如多用户)。init进程绝不会终止。他说一个普通的用户进程(与交换进程不同,它不是内核中的系统进程),但是它以超级用户特权运行。成为所有孤儿进程的父进程。
ID为2在某些Unix的虚拟存储器实现中是页守护进程,负责支持虚拟存储系统的分页操作。
进程中除ID外的标识符:

#include <unistd.h>
pid_t getpid(void); //返回值:调用进程的进程ID
pid_t getppid(void); //返回值:调用进程的父进程ID
uid_t getuid(void); //返回值:调用进程的实际用户ID
uid_t geteuid(void); //返回值:调用进程的有效用户ID
gid_t getgid(void); //返回值:调用进程的实际组ID
gid_t getegid(void); //返回值:调用进程的有效组ID

进程创建函数:

1.fork 创建子进程。fork函数被调用一次,返回两次,两次返回的唯一区别是子进程的返回值是0,而父进程的返回值则是新子进程的进程ID。子进程和父进程继续执行fork调用之后的指令。子进程是父进程的副本。例如,子进程获得父进程数据空间、堆和栈的副本。注意,这是子进程所拥有的副本。父、子进程并不共享这些存储空间部分。父子进程共享正文段。
2.clone 系统调用,是一种fork的泛型,允许调用者控制哪些部分由父、子进程共享。
3.rfork 类似Linux的clone调用。是从Plan9操作系统派生出来的。
4.fork的一个特性是父进程的所有打开文件描述符都被复制到子进程中。父、子进程都向标准输出进行写操作。

fork函数实例p173

#include "apue.h"
int global = 6; /*external variable in initialized data*/
char buf[] = "a write to stdout\n";
int main(void){
	int var; /*automatic variable on the stack*/
	pid_t pid;
	var = 88;
	if (write(STDOUT_FILENO, buf, sizeof(buf)-1) != sizeof(bif) - 1)
		err_sys("write error"); /*we don't flush stdout*/
	printf("before fork\n");
	if((pid = fork()) < 0){
		err_sys("fork error");
	}else if (pid == 0){ /*child*/
		glob++; /*modify variables*/
		var++;
	}else{
		sleep(2); /*parent*/
	}
	printf("pid = %d, glob = %d, var = %d\n", getpid(), glob, var);
	exit(0);
}

子进程继承的父进程属性

  1. 共享文件的文件偏移量
  2. 实际用户ID、实际组ID、有效用户ID、有效组ID、附加组ID、进程组ID、会话ID、设置用户ID标志和设置组ID标志
  3. 控制终端
  4. 当前工作目录、根目录
  5. 文件模式创建屏蔽字、信号屏蔽和安排
  6. 针对任一打开文件描述符的在执行时关闭(close-on-exec)标志
  7. 环境
  8. 连接的共享存储段、存储映射、资源映射

父子进程的区别

  1. fork的返回值
  2. 进程ID
  3. 子进程的tms_utime、tms_stime、tms_cutime以及tms_ustime均被设置为0
  4. 父进程设置的文件锁不会被子进程继承
  5. 子进程的未处理的闹钟(alarm)被清除
  6. 子进程的未处理信号集设置为空集。

fork/vfork的用法

  1. 父进程希望复制自己,使父、子进程同时执行不同的代码段。(父进程调用fork使子进程处理客户端的服务请求,父进程继续等待下一个服务请求到达)
  2. 一个进程要执行不同的程序(子进程从fork返回后立即调用exec)
  3. vfork不会讲父进程的地址空间完全复制到子进程中,因为子进程会立刻调用exec(或exit)命令,于是也就不会存访该地址空间。相反,在子进程中调用命令之前,它在父进程的空间中运行,这种优化工作方式在某些unix的页式虚拟存储器实现中提高了效率。
  4. vfork保证紫禁城先运行,在它调用命令之后父进程才可能被调度运行。

竞争条件

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

数据结构森林

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值