Linux

IO

标准IO

具有一定的可移植性。标准IO库处理很多细节。例如缓存分配,以优化长度执行IO等。标准的IO提供了三种类型的缓存。

  • 全缓存:当填满标准IO缓存后才进行实际的IO操作。
  • 行缓存:当输入或输出中遇到新行符时,标准IO库执行IO操作。
  • 不带缓存:stderr

文件IO

不带缓存的IO(unbuffered I/O), 每个read,write都调用内核中的一个系统调用。
也就是一般所说的低级I/O——操作系统提供的基本IO服务,与os绑定,特定于Unix平台。

区别

  • 标准I/O默认采用了缓冲机制,比如调用fopen函数,不仅打开一个文件,而且建立了一个缓冲区(读写模式下将建立两个缓冲区),还创建了一个包含文件和缓冲区相关数据的数据结构(FILE *)。
  • 低级I/O一般没有采用缓冲,需要自己创建缓冲区,不过其实在linux系统中,都是有使用称为内核缓冲的技术用于提高效率,读写调用是在内核缓冲区和进程缓冲区之间进行的数据复制。
  • 使用标准IO就不需要自己维护缓冲区了,标准IO库会根据stdin/stdout来选择缓冲类型,也就是说当你使用标准IO的时候,要清楚它的stdin/stdou是什么类型以及其默认的缓冲模式,如果不合适,你需要用setvbuf先设置,再使用,例如协同进程的标准输入和输出的类型都是管道,所以其默认的缓冲类型是全缓冲的,如果要使用标准IO,就需要现设置行缓冲。
  • 对于文件IO,只要你自己能维护好缓冲区,完全可以不用标准IO。
  • 从名字上来区分,文件I/O主要针对文件操作,读写硬盘等,标准I/O,主要是打印输出到屏幕等。因为他们设备不一样,文件io针对的是文件,标准io是对控制台,操作的是字符流。对于不同设备得特性不一样,必须有不同api访问才最高效。

进程

wait和waitpid

  • wait会令调用者阻塞直至某个子进程终止;
  • waitpid则可以通过设置一个选项来设置为非阻塞,另外waitpid并不是等待第一个结束的进程而是等待参数中pid指定的进程。
  • waitpid中pid的含义依据其具体值而变:
      pid==-1 等待任何一个子进程,此时waitpid的作用与wait相同
      pid >0   等待进程ID与pid值相同的子进程
      pid==0   等待与调用者进程组ID相同的任意子进程
      pid<-1   等待进程组ID与pid绝对值相等的任意子进程
  • waitpid提供了wait所没有的三个特性:
      1 waitpid使我们可以等待指定的进程
      2 waitpid提供了一个无阻塞的wait
      3 waitpid支持工作控制

vfork

子进程与父进程共享数据段.  vfork()保证子进程先运行,在她调用exec或_exit之后父进程才可能被调度运行。如果在调用这两个函数之前子进程依赖于父进程的进一步动作,则会导致死锁。

守护进程

守护进程是运行在后台的一种特殊进程, 不受终端控制,Linux系统的大多数服务器就是通过守护进程实现的。一个守护进程的父进程是init进程。

  • 创建子进程,父进程退出
  • 在子进程中创建新会话 setsid()
  • 改变当前目录 chdir(“/”);
  • 重设文件权限掩码 umask(0);
  • 重定向文件描述符 dup2(open(“/dev/null”),0~2)

编译运行

ELF文件

section 是被链接器使用的,但是 segments 是被加载器所使用的。加载器会将所需要的 segment 加载到内存空间中运行。

  • 可重定位的对象文件(Relocatable file)(没有segments)
    这是由汇编器汇编生成的 .o 文件。后面的链接器(link editor)拿一个或一些 Relocatable object files 作为输入,经链接处理后,生成一个可执行的对象文件 (Executable file) 或者一个可被共享的对象文件(Shared object file)。我们可以使用 ar 工具将众多的 .o Relocatable object files 归档(archive)成 .a 静态库文件。
  • 可执行的对象文件(Executable file)
  • 可被共享的对象文件(Shared object file)

这些就是所谓的动态库文件,也即 .so 文件。

在ELF文件里面,每一个 sections 内都装载了性质属性都一样的内容,比方:

  • .text section 里装载了可执行代码;
  • .data section 里面装载了被初始化的数据;
  • .bss section 里面装载了未被初始化的数据;
  • 以 .rec 打头的 sections 里面装载了重定位条目;
  • .symtab 或者 .dynsym section 里面装载了符号信息;
  • .strtab 或者 .dynstr section 里面装载了字符串信息;
  • 其他还有为满足不同目的所设置的section,比方满足调试的目的、满足动态链接与加载的目的等等。

把带有相同属性(比方都是只读并可加载的)的 section 都合并成所谓 segments(段): 代码段: .text +字符常量
数据段:.data bss: .bss

映射: 动态链接库, mmap

内核

源文件->执行文件

  • 预处理阶段:对源代码文件中文件包含关系(头文件)、预编译语句(宏定义)进行分析和替换,生成预编译文件。
  • 编译阶段:词法、语法、语义、优化
  • 汇编阶段:将编译阶段生成的汇编文件转化成机器码,生成可重定位目标文件
  • 链接阶段:将多个目标文件及所需要的库连接成最终的可执行目标文件

程序启动过程

  • 内核创建新进程
  • 往这个新进程的进程空间 加载代码段和数据段
  • 加载进动态连接器 (/lib/ld-linux.so)的代码段和数据。
  • 内核将控制传递给动态链接库里面的代码。动态连接器加载应用程序所需要使用的各种动态库。
  • 动态连接器将控制传递给main函数。

过程链接表(PLT)

全局偏移量表(GOT)

开机流程

  • 加载BIOS的硬件信息与进行自我测试,并依据设置取得第一个可启动设备;
  • 读取并执行第一个启动设备内MBR(主引导分区)的Boot Loader(即是gurb等程序);
  • 依据Boot Loader的设置加载Kernel,Kernel会开始检测硬件与加载驱动程序;
  • 在硬件驱动成功后,Kernel会主动调用init进程(/sbin/init),而init会取得runlevel信息;
  • init执行/etc/rc.d/rc.sysinit文件来准备软件的操作环境(如网络、时区等);
  • init执行runlevel的各个服务的启动(script方式);
  • init执行/etc/rc.d/rc.local文件;
  • init执行终端机模拟程序mingetty来启动login程序,最后等待用户登录。

工具

  • ldd 查看程序依赖库
  • lsof list of open file
  • ps Process Status
  • pstack 跟踪进程栈
  • strace 跟踪进程中的系统调用
  • ipcs 查询进程间通信状态
  • vmstat 监视虚拟内存使用情况
  • iostat 监视I/O子系统
  • sar 找出系统瓶颈的利器
  • ifstat 看网络带宽
  • netstat命令是一个监控TCP/IP网络的非常有用的工具,它可以显示路由表、实际的网络连接以及每一个网络接口设备的状态信息。-t tcp