温古而知新,今天重温了这本书,这里记录一下之前没有注意到的细节,以及最新工作遇到的一些疑问:
1、linux目录:
文件,除了本身包含的内容以外,它还会有一外名字和一些属性,即“管理信息”,包括文件的创建/修改日期和它的访问权限。这些属性被保存在文件的inode(节点)中,它是文件系统中的特殊的数据块,它同时还包含文件的长度和文件在磁盘上的存放位置。系统使用的是文件的inode编号,目录结构为文件命名仅仅是为了便于人们使用。
目录是用于保存其它文件的节点号和文件名的文件,目录文件中的每个数据项都是指向某个文件节点的链接,删除文件名就等于删除与之相对应的链接(文件节点号可以通过ln -i命令来查看)。你可以通过使用ln命令在不同的目录中创建指向同一个文件的链接。
删除一个文件时,实质上是删除了该文件对应的目录项,同时指向该文件的链接数减1。该文件中的数据可能仍然能够通过其它指向同一文件的链接访问到。如果指向某个文件的连接数ls -l为零时,就表示该节点以及其指向的数据块不再被使用,磁盘上相应的位置就会被标记为可用空间。
另外,/proc/${pid}/fd目录下,系统会将所有进程打开的文件链接到这下面,所以我们手动去删除文件的时候,如果进程正在打开,那么文件其实并不会被真正删除,进程仍然可以正常读写已经打开的文件,直到进程关闭此文件,使用指向该文件的链接(引用)数降为0,文件才会被彻底删除。这也是我们能删除程序正在使用的文件的原因。
2、目录操作相当的系统函数:
chown系统调用
chmod系统调用
unlink、link、和syslink系统调用
unlink用来删除一个文件,link用来创建指向已有文件的新链接(硬链接),symlink用于创建一个符号连接。一个文件的符号连接并不会增加该文件的被引用数,所以它不会像普通(硬)链接那样子防止文件被删除。
mkdir、rmdir系统调用
chdir和getcwd系统调用
chdir用于更改当前工作目录,而getcwd用于获取当前工作目录。
文件操作出现错误时的错误处理,出错时,错误码被存放在全局变量errno里面,系统调用返回错误时候,应用应该立刻去检查这个错误码,错误代码的取值和含义都列在头文件errno.h里面。
EPRM: 操作不允许
ENOENT: 文件或目录不存在
EINTR: 系统调用被中断
EIO: IO错误
EBUSY: 设备忙
EEXIST: 文件存在
EINVAL: 参数无效
EMFILE: 打开的文件过多
ENODEV: 设备不存在
EISDIR: 目标是一个目录
ENOTDIR: 目录不是一个目录
stderror函数用于将错误代码映射成一个字符串。它在头文件string.h中声明。
perror函数用于将错误代码映射成一个字符串,在前面加上prefix,并打印到stdout中。perror("prefix") ==> printf("%s : %s ", prefix, stderr(errno))
4、锁文件
创建锁文件
通过open("/tmp/test.lock", O_RDWR | O_CREAT | O_EXCL, 0444),通过带上O_EXCEL选项来表明这是一个原子的系统调用。如果文件已经存在则会返回失败。这样子可以确保在多个进程同时创建锁文件时候,只有其中一个进程能创建成功。
其它文件锁操作比如区域锁定,读写锁等,比较少用,因此不细节看了。
5、进程和信号
应用中可能通过getenv和setenv系统调用来获取或者设置进程的环境变量。
启动一个新进程
system系统调用实质上是调用shell 来启动一个进程,比如system("ps -ef")等同于sh -c ps -e
替换进程映像
exec系统函数一组相关的函数组成,exec函数可以把当前进程替换成一个新的进程,新进程由path和file参数指定,我们可以使用exec函数来将程序的执行从一个程序切换到另一个程序,exec调用将不会返回。由exec启动的新进程继承了原进程的许多特性,特别地,在原进程中已经打开的文件描述符在新进程中仍将保持打开,除非它们的设置了“执行时关闭标志”(close on exec flag),任何在原进程中已经打开的目录流都 将在新进程中被关闭。
复制进程映像
fork函数用于复制进程映像,它将返回新进程的PID.
等待子进程退出
wait函数用于等待fork出来的子进程退出。
僵尸进程
一直对这个概念很模糊,fork出来的子进程终止时,它与你进程之间的关联还会保持,直到父进程也正常终止或者父进程调用wait来确认子进程结束 。因此,进程表中代表子进程的项并不会立刻被释放,虽然子进程已经不再运行,但它仍然存在于系统中,因为它的退出码还需要保存起来,以备父进程后面的wait函数调用。这时候它将成为一个僵尸。如果父进程异常终止,子进程自动把PID为1的进程(即init)作为它自己的父进程。
如果子进程已经成为一个僵尸进程,如果其父进程异常终止了,进而被 init进程接管。僵尸进程将一直保留在进程表中直到被init进程发现并释放 。被init“领养”的进程并不会是僵尸进程,init进程将随时准备调用wait来结束它们。
另外可以通过waitpid函数来等待某个特定的子进程结束。