open

进程打开文件的内核数据结构

task_struct—->file_struct——->file———->dentry———>inode(索引节点号,文件信息)

task_struct: 进程控制块

file_struct: 该结构体包含了进程已经打开的文件表

file:代表一个已经打开的文件

dentry:文件相关目录项

create

返回只写打开的文件描述符

lseek

修改当前文件的偏移量,打开一个文件时,偏移量为0,只修改内核中的记录

1
off_t CurrentPosition=off_t lseek(int filedes,off_t offset,int whence);

filedes: 文件描述符

offset:相对偏移量,需要结合whence才能算出真的偏移量

off_t: 在32位机是32位,在64位机是64位

whence:常见三个参数

  1. SEEK_SET: 当前文件偏移量为:距离文件开始处的offset个字节
  2. SEEK_CUR: 距离当前文件偏移量+offset
  3. SEEK_END:当前文件长度+offset

空洞地方是0

静态库

静态库打包

1
2
3
g++ -c a1.cpp a2.cpp

ar -rc libtest.a a1.o a2.o

nm命令查看符号信息

1
2
3
4
5
g++ statictest.cpp libtest.a

g++ -o statictest statictest.cpp -L. -ltest
-L // 库的路径
-l // 库的名称

动态库 so shared object

1
g++ -fpic -shared -o libtest.so a1.cpp a2.cpp

$ -fpic $ 位置无关代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#include<dlfcn.h>
int main(){
// void *dlopen(const char* file,int mode);
void *handle=dlopen("./libtest.so",RTLD_LAZY);
if(handle==NULL){
exit(-1);
}
typedef void (*Fun)();
Fun f1=(Fun)dlsym(handle,"f");

if(f1==0){
cout<<"error"<<endl;
char *str=dlerror();
cout<< str << endl;
return 0;
}
(*f1)();
dlclose(handle);
return 0;
}
1
2
3
4
5
6
7
8
// 映射动态链接库里的函数
void *dlsym(void* handle,const char *FuncName);
// 打开动态链接库
void *dlopen(const char* file,int mode);
// 关闭动态链接库
void dlclose(void *handle);
// 找到出错原因
char *dlerror(void *handle);

库的编写注意事项

  1. 导出函数的名称

  2. 函数调用的约束

  3. 结构体对齐

  4. 谁分配谁释放

函数导出名

使用 extern “C” 导出的函数函数名不会改变

extern “C”:告诉编译器按照C语言的方式设定函数的导出名

接口用 extern “C”

extern

在头文件里面定义一个变量,被多个源文件包含之后,会有重定义错误。

1
2
3
4
5
6
7
8
9
// a.h
#ifndef A_H
#define A_H

// 应该加上extern防止编译器错误
// 声明一个外部变量
extern int i;

#endif

结构体大小

1
2
3
struct A{
int i;
};

4个字节大小

1
2
3
4
5
struct A{
short k;
int i;
char j;
};

缓冲

全缓存

fclose、fflush、程序结束、全缓存满了会刷新到文件里面。

行缓存

终端(标准输入输出),使用行缓存。

遇到 “\n” 、执行fflush、程序结束、行缓存满

1
2
3
4
5
int fflush(FILE* stream);
void setbuf(FILE *p,char *buf);
//在打开文件流后,读取内容之前,调用setvbuf()可以用来设置文件流的缓冲区
// mode 是缓冲区类型
void setvbuf(FILE *p,char *buf,int mode,unsigned size);

进程控制

fork函数

1
pid_t fork(void);

一次调用,返回两次(自然返回;当子进程返回时再次返回)。

子进程复制父进程的fd(已经打开文件表),复制mm_struct(pgd表),复制父进程地址空间,复制text段。子进程复制父进程地址空间,把页框全部复制过去(新的一份,不同的物理地址),让映射关系保持一致(映射到我们的新复制的那一份)。

  • 子进程获得父进程数据空间、堆和栈的副本
  • 父子进程不共享存储空间
  • 父子进程共享正文段

用COW,不立刻复制页框,仅仅复制需要修改的地方。(一旦读写就产生缺页,就全复制过去)

vfork函数

创建新的进程,执行一个新的程序(可执行文件)。

子进程子啊调用exec或exit之前,在父进程地址空间中运行

exit函数

当父进程调用wait函数,内核将释放终止进程所使用的所有内存,关闭打开的文件

已经终止但是没有父进程没调用wait,就是僵尸进程。

当一个进程正常获知异常终止时,内核给父进程发送SIGCHLD信号

wait函数

1
pid_t wait(int *staloc);

waitpid等待指定子进程返回

exec函数

用可执行文件来替换当前地址空间的一切

execl函数成功不返回值(原来的程序都被替换掉了)

execve系统调用,其他都是库函数

execlp只给出了文件名,路径从环境变量中寻找

l表示list;v表示vector;p表示用路径前缀