我要好offer之 系统基础大总结

1. APUE Unix环境高级编程

(1) Unix基础知识: 内核->系统调用->shell和库函数->应用软件

(2) 文件I/O:read函数返回值、进程的文件描述符表、文件共享、inode、fcntl函数

(3) 文件和目录:文件类型(普通、目录、socket、FIFO、PIPE)、文件系统(硬链接、软链接)

(4) 标准I/O库:FIFE封装、最佳缓冲大小测试

(5) 进程环境:进程终止情况、longjmp、volatile用法

(6) 进程控制:fork(返回值、父子进程的文件描述符表)、wait函数、exec系函数

(7) 信号:三种处理方式、常见信号(SIGCHLD、SIGSEGV)及其默认处理方式

(8) 线程同步:进程和线程的异同点、线程回调函数的传参、进程和线程原语对比、线程同步(读写、写写、i++多线程、mutex)

(9) 高级I/O(一):非阻塞I/O、select使用、readn和writen封装、mmap映射

(10) 高级I/O(二):管道、消息队列、共享存储器mmap

(11) 网络套接字:大小端字节序、addrinfo结构体、getaddrinfo函数、tcp_listen和tcp_connect封装

2. Linux系统编程

(1) 系统调用、API和ABI、文件(各种类型简介、inode、软链接、硬链接)、进程简介

(2) 文件I/O:read函数返回值、readn和writen函数封装、文件定位、文件同步、文件截断、多路I/O(select、poll)、Linux内核实现

(3) 标准I/O缓冲:测试最佳I/O缓冲区大小、三种类型缓冲、标准I/O的线程安全、标准I/O的缺点

(4) 高级I/O:聚集I/O(readv、writev性能好、原子性)、epoll(LT和ET)、mmap(优缺点)、I/O调度算法(电梯调度)

(5) 进程管理:进程pid分配方法、fork and exec、copy on write、wait、僵尸进程的回收

(6) 进程调度:Linux进程调度算法(FIFO、最短优先、时间片轮转)、CPU绑定(缓存影响)

(7) 线程:虚拟化抽象(虚拟内存与进程关联、虚拟处理器与线程关联)、多线程好处、多线程i++解析、pthread使用、RAII封装mutex

(8) 文件和目录管理:文件inode节点元数据、目录操作、软链接和硬链接、块设备/dev/null

(9) 内存管理:进程地址空间、动态分配内存(malloc、calloc、brk)、数据对齐、匿名文件映射、变长数组

(10) 信号:信号的3中处理方式、常见的信号

(11) 时间:gettimeofday、sleep休眠、定时器alarm

3. 位、字节、整型

字节序问题:

大端:地址为 阅读顺序:Sun系统 和 网络字节序

小端:地址为 逆阅读序:X86

信息 = Bit位 + 解释方式,无论怎么转换,Bit位是永远不变的,只有解释方式改变了

对于int的使用,参见:Google c++编程规范中int型使用

C语言整型溢出问题by @左耳朵耗子

尽量不要使用无符号数,极易bug:当执行一个运算时,如果一个运算数是有符号的而另一个是无符号的,那么c语言会隐式地将有符号数强制类型转换为无符号数

比如:函数参数类型为size_t, 我传入实参-1,这时-1会隐式的 转换为 最大正数,完全错了

4. 数据对齐、结构体访问、offsetof

结构体数据对齐有2个原则:

(1) 每个成员数据的偏移 必须是 该数据类型大小的整数倍

(2) 整个结构体大小 必须是 最大数据类型大小的 整数倍

数据对齐产生了 内部碎片,为了使内部碎片最小,必须让 最大的数据类型摆放在最前面

如何计算 每个成员数据在结构体内的偏移呢? C语言提供 offsetof宏

#define offsetof(type, member) (size_t)&(((type*)0)->member)
    struct test {
        int  val;
        int  a[2];
    };
    // 结构体访问和数组访问的实质都是 首地址加上对应偏移,然后读取对应数据,注意 取地址 和 取成员 操作的区别
    struct test* ptest = NULL;              // 结构体首地址为0
    fprintf(stdout, "1:%p
", &ptest->val); // 注意:这是读取 结构体成员数据的偏移,直接就是 首地址加上 offset,而不是 先取到数据然后取地址
    fprintf(stdout, "2:%d
", ptest->val);  // 错误,可以打印地址0的地址值,但是不能访问地址值为0的数据
    fprintf(stdout, "3:%p
", &ptest->a);   // 正确,首地址加上offset
    fprintf(stdout, "4:%p
", ptest->a);    // 同上,数组名退化为 数组首地址,所以这还是一个取地址操作

具体详见:C语言结构体内的数组和指针by@左耳朵耗子

5. cache 矩阵乘法

CPU Cache示例by@左耳朵耗子

C语言数组行优先存储,当数组大小超过 缓存块大小时,不同的存取方式之间 差别很大,以 A[N][N] * B[N][N] 存储在 C[N][N]中

     

 

 

6. 编译链接

静态编译和动态编译

详见陈硕的 <C++编译链接模型精要>

 

链接器主要完成两件事:

(1) 符号解析:将每个符号的引用(reference)对应到相应符号的定义(definition)

C程序员应该尽可能使用static属性在模块内部隐藏变量和函数声明,即模块私有

注:使用 readelf命令来查看目标文件的符号表

当linker遇到重名时:

重名Local定义:编译器解决

重名Global定义:强符号(函数名和已初始化的全局变量)和弱符号(未初始化的全局变量)

(2) 重定位:合并不同文件时修改符号地址,将符号的相对地址 改为 绝对地址

Linux系统为动态链接库提供了一个简单的接口,允许应用程序在运行时加载和链接共享库

#include <dlfcn.h>
void* dlopen(const char* filename, int flag); // dlopen函数加载和链接共享库filename
void* dlsym(void* handle, char* symbol);      // dlsym函数的输入参数为dlopen打开的共享库句柄和符号名,返回符号的地址
int   dlclose(void* handle);  

7. 进程间通信

(1) 管道、FIFO(命名管道)

管道:管道是一种半双工的通信方式,数据只能单向流动,而且只能在具有亲缘关系的进程间使用。进程的亲缘关系通常是指父子进程关系

FIFO:有名管道也是半双工的通信方式,但是它允许无亲缘关系进程间的通信。

(2) 信号(Signal)

信号用于通知接受进程有某种事件发生,除了用于进程间通信外,进程还可以发送信号给进程本身;linux除了支持Unix早期信号语义函数sigal外,还支持语义符合Posix.1标准的信号函数sigaction(实际上,该函数是基于BSD的,BSD为了实现可靠信号机制,又能够统一对外接口,用sigaction函数重新实现了signal函数)

(3) 报文(Message)队列(消息队列)

消息队列是消息的链接表,包括Posix消息队列system V消息队列。有足够权限的进程可以向队列中添加消息,被赋予读权限的进程则可以读走队列中的消息。消息队列克服了信号承载信息量少、管道只能承载无格式字节流以及缓冲区大小受限等缺点。

(4) 信号量(semaphore)

主要作为进程间以及同一进程不同线程之间的同步手段,是一个计数器,用于多进程对共享数据对象的访问

(5) 共享内存

共享内存就是映射一段能被其他进程所访问的内存,这段共享内存由一个进程创建,但多个进程都可以访问

共享内存是最快的 IPC 方式(因为数据不需要在客户进程和服务器进程之间复制)

使用mmap进行文件内存映射 和 匿名存储映射(将fd设为-1)

(6) socket套接字

套接字接口既可以用于多机之间进程通信,也可以用于单机内部进程通信

8. 进程同步原语

(1) 互斥锁

(2) 条件变量

(3) 读写锁

9.高效拷贝文件 

    

10.高效传输远程文件

原文地址:https://www.cnblogs.com/wwwjieo0/p/3941198.html