随笔-系统编程-《Linux 环境高级编程》
https://courses.fmsoft.cn/aple
目录
一、内存管理
虚拟内存原理
- MMU和页表
- 页表:逻辑地址到物理地址的映射表,每个进程一个,也要占用内存空间
C/C++程序的堆/栈管理
API
常见:
- C: malloc/calloc/realloc/free C++: new/delete
不常见:
- alloca,VLA
- posix_memalign/aligned_alloc
- brk/sbrk
- etext/edata/end
C/C++进程的内存布局
- command-line arg and env var
- stack/stack frame
- ...
- heap
- uninitialized data segment (BSS, block started by symbol)
- initialized data segment
- text segment
常用工具
- nm (name list, symbol table)
- strings
- size
- readelf
- objdump
其他分配内存的方法
- 匿名内存映射 mmap()
- memfd:memfd_create() # 可用来作为进程通信的手段
- 问题:大块连续的虚拟内存,对应物理内存是否连续? 不一定连续,一般是不连续的
常⻅内存使用问题
- 不对⻬访问(unaligned access)
- 访问了不可访问的地址 # 越界访问或非法指针
- OOM # 内存泄露 最终被系统kill掉
- ...
二、文件和文件系统
STDIO vs 传统文件读写系统调用
问题:STDIO接口的主要功能有哪些?
fread/fwrite、sprintf/snprintf、scanf/sscanf、...
问题:读写文件时应使用 STDIO 还是系 统调用,哪个效率高?
A:STDIO 带有缓冲,如果read会多读
STDIO 的缓冲模式
默认行缓冲、可以设置无缓冲、块缓冲、完全缓冲
改变缓冲模式:setvbuf()/setbuf()/setbuffer()/setlinebuf()
问题:键盘上的输入何时才能被程序获得?
键盘产生中断,内核先处理,内核支持行编辑(输入、delete删除、回车),并在程序界面上显示你的输入,上层应用程序主动调相关接口才能获取到
基本的文件读写系统调用
creat()/open()、read()/write()、lseek()、close()、fcntl()
FILE 和 fd 互操作
- fdopen():已知文件描述符创建流对象
- fileno():已知流对象获取对应文件描述符
鲜为人知的STDIO接口
- fmemopen():将内存区域抽象为流对象
- freopen():关闭一个流对象并使用新模式打开
- funopen():自定义流对象
- fropen():自定义只读流对象
- fwopen():自定义只写流对象
问题:fflush()/fpurge() 和 fsync() 有何区别?
stdio buffer:用户空间的东西,内核不知道,这个东西是为了减少系统调用的次数
内核也有自己的一个buffer cache,这个东西是为了减少读写磁盘
fflush是将用户空间buffer的内容刷到内核缓冲区
fsync行参是fd,是将内核空间的buffer cache刷到磁盘
文件描述符
文件共享、重定向及文件锁
父子进程共享所有数据和代码,也共享文件描述符
- colse-on-exe 标志的重要性
- 问题:fork() 的资源成本有多大?
通过 UNIX domain socket 发送文件描述符
- 分配并共享大块内存
memfd_create() -> sendmsg() -> mmap()
重定向的实现
dup2()
文件锁
- flock() 仅用于父子进程之间,且只是建议锁(advisory lock)
- flock() 的锁粒度太大(整个文件)
- fcntl()/lockf() 可实现强制锁(mandatory lock) 且可以只限定某一区域
- 为什么方同学的代码没有出现写入混乱?
讨论:内核中打开的文件是以进程为单位维护还是全局的?
以进程为单位
文件系统操作
常用接口:
- STDC: fopen(), remove(), rename()
- POSIX: creat(), unlink(),rmdir(), renameat(), mkdir(), ...
- 遍历目录项:opendir(), closedir(), readdir(), rewinddir(), scandir(), seekdir(), telldir(), ...
- 文件属性:chmod(), chown(), stat(), umask(),
- ...
三、信号和进程管理
略
四、进程间通讯
五、并发编程-多线程编程
并发编程:在同一时刻允许多个任务同时执行的编程方法
常⻅的并发编程模型
- 单任务 IO 多路复用 (select/poll/epoll 在一个线程中管理多个fd的技术)
- 事件驱动+回调函数 (select/poll/epoll 返回不同事件,不同事件调用不同的回调函数)
- 多进程
- 多线程
- 多协程
多线程编程概要
- 线程和进程的区别以及多线程带来的好处
- 多线程编程接口:
- POSIX Thread:<pthread.h>
- C99:<thread.h>
- 多线程同步机制:
- 线程本身
- 互斥量(mutex)、读写锁(read/write lock) 条件变量(condition variable)
- 信号量(semaphore)
- 其他 Linux 内核机制:文件锁、eventfd、System V 信号量等
- 特有机制:单次函数、线程本地存储(TLS)
六、并发编程-多进程编程 - 略 多进程比较重,用得比较少
七、网络编程
套接字操作接口
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
int socket(int domain, int type, int protocol);
int bind(int sockfd, const struct sockaddr *addr,
socklen_t addrlen);
int listen(int sockfd, int backlog);
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
int connect(int sockfd, const struct sockaddr *addr,
socklen_t addrlen);
- 服务器进程调用 socket() 建立流套接字,获得流套接字文件描述符。
- 服务器进程调用 bind() 函数将给定的套接字文件名称和文件描述符绑定在一起。
- 服务器进程调用 listen() 函数在该文件描述符上监听连接请求。
- 客户进程调用 socket() 建立流套接字,获得流套接字文件描述符。
- 客户进程调用 bind() 函数将给定的套接字文件名称和文件描述符绑定在一起。
- 客户进程调用connect() 将自己的流套接字文件描述符连接到服务器的套接字文件上。
- 服务器收到该连接请求,调用 accept() 函数接受请求。该函数的返回一个新的文件描述符,该描述符就是流套接字在服 务器端的读写描述符。
- 服务器接受连接请求之后,客户的connect() 函数返回。客户可以用先前建立的流套接字描述符来和服务器通讯了。
套接字的域、类型和协议
-
常用套接字域(domain)
- AF_UNIX/AF_LOCAL:UNIX域套接字
- AF_INET:IPv4
- AF_INET6:IPv6
- AF_NETLINK:Linux netlink
-
常用套接字类型(type)
- SOCK_STREAM:流
- SOCK_DGRAM:数据报
- SOCK_RAW:裸网络协议访问
-
套接字协议(protocol): ip, ipv6, tcp, udp 等;通常取 0。已知协议⻅:/etc/ protocols
套接字地址
struct sockaddr {
sa_family_t sa_family;
char sa_data[14];
}
这个结构只是一个别名。 不同的套接字域有不同的结构定义。 使用时需要强制做指针的强制类型转换。
套接字地址(AF_UNIX)/套接字地址(AF_INET)/套接字地址(AF_INET6)
套接字选项
网络地址转换
网络地址转换(IPv4专用)
字节序转换
网络接口枚举
监听网络接口变化(netlink)
八、高级输入输出
定位读写
多缓冲区/向量读写
在文件间快速传输或复制数据
异步和非阻塞读写
IO多路复用
九、沙箱、容器及其背后的技术 - TODO 用得比较少
十、性能分析及调试工具 - 略 自己本身比较熟悉
本文来自博客园,作者:LiYanbin,转载请注明原文链接:https://www.cnblogs.com/stellar-liyanbin/p/18212916