内核node
内核职责linux
- 进程调度(哪些进程得到CPU的使用,每一个进程使用多久)
- 内存管理(虚拟内存管理:进程之间,进程和内核之间相互隔离;只需将进程的一部份内容放在内存中,变相提升CPU使用率)
- 提供文件系统
- 建立终止进程
- 对提供设备访问接口
- 提供系统调用API
硬件执行之指令使CPU在两种状态间切换;用户态运行时只能访问用户态内存,操做系统位域内核空间(shell是用户进程)shell
文件描述符:其实是一个索引指向内核为每个进程所维护的打开的文件记录表编程
进程内存布局安全
- 文本:进程的指令
- 数据:程序静态变量
- 堆:动态分配额外内存
- 栈:(线程仅仅有本身的栈)
进程的建立app
- fork建立,子进程继承父进程数据段,堆段,栈段,在内存中建立副本,共享只读的程序文本
- execve加载新的程序,会销毁现有的文本,数据,堆,栈段
特殊的进程 — idel 进程 (第一个进程,N个处理器N个idle进程,cpu没事干的时候进入) — init进程 — kthread进程ide
mmap()
)
- 文件映射:将文件部分区域映射到进程的虚拟内存
- 匿名映射:经过传入标志,决定映射的内容其余进程是否可见(通常用于父子进程间通信)
系统调用流程 (通常:应用程序—>C外壳函数(异常正)—>system_call()例程(异常负)—>服务例程(异常正))函数
- 经过C函数库的外壳函数发起系统调用
- 外壳函数将调用参数复制到寄存器,同时将系统调用的编号复制到CPU寄存器(%eax)。
- 外壳函数执行一条中断指令(int 0x80),引起处理器从用户态切换到内核态,并执行(0x80)的中断矢量指向的代码。
- 为响应0x80,内核会调用system_call()列程处理中断
- 内核栈保存寄存器值
- 审核系统调用编号有效性,参数有效性,执行任务,将结果返回system_call()列程
- 从内核栈恢复各寄存器值,将系统的调用返回值置于栈中。
- 返回至外壳函数,切换到用户态。
- 若系统调用返回值代表有误,外壳函数会使用该值来设置全局变量errno,而后外壳函数返回调用程序,并同时返回一个整型值(ex:-1),代表是否成功。
tip:布局
- 一些系统调用成功后仍是返回-1(ex:getpriority()), 须要在调用前将errno至为0,便于排查(p39);
void jkstack(void) __attribute__((noreturn));
就是告诉编译器这个函数不会返回给调用者,以便编译器在优化时去掉没必要要的函数返回代码。
#define LINUX_REBOOT_MAGIC1 0xfee1dead
#define LINUX_REBOOT_MAGIC2 672274793
#define LINUX_REBOOT_MAGIC2A 85072278
#define LINUX_REBOOT_MAGIC2B 369367448
#define LINUX_REBOOT_MAGIC2C 537993216
/*672274793 = 0x28121969 85072278 = 0x05121996 369367448 = 0x16041998 537993216 = 0x20112000*/
复制代码
char buffer[BUFFER_SIZE]
//1
fd = open(pathname,flags,mode) // flags指定文件打开方式以及是否建立, mode指定建立文件的权限
//2
numread=read(fd, buffer, count) // 从fd中读取至多count字节数据,存储到buffer
//3
numwritten=write(fd,buffer,count) //从buffer读取至多count,写入fd
//4
status=close(fd) //释放fd以及相应内核资源
复制代码
/proc/$PID/fdinfo
得到系统内任意一进程所打开的fd优化
pos: 0 //当前文件偏移量
flags: 0100002
mnt_id: 17 // represents mount ID of the file system containing the opened file
复制代码
// tee.c
#include<unistd.h>
#include<stdio.h>
#include<fcntl.h>
#ifndef BUF_SIZE
#define BUF_SIZE 1024
#endif
int main(int argc, char *argv[]){
int append = 0;
int opt;
while ((opt=getopt(argc, argv, "a"))!=1){
if ((unsigned char) opt =='a'){ append=1;}
}
if (optind>=argc) {
printf("%s [-a] file\n", argv[0]);
}
int fd = open(argv[optind], O_CREAT|O_WRONLY|(append?O_APPEND:O_TRUNC), S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH);
if (fd == -1) {printf("opening file %s", argv[optind]);}
ssize_t numRead;
char buf[BUF_SIZE];
int oFd[2] = {fd, STDOUT_FILENO};
while ((numRead = read(STDIN_FILENO, buf, BUF_SIZE))>0){
for (int i=0; i<(int)(sizeof(oFd)/sizeof(oFd[0]));i++){
if (write(fd, buf, numRead)!=numRead){printf("couldn't wirte whole buff");}
}
}
if (numRead==-1){
printf("read error");
}
if (close(fd)==-1){
printf("colse error");
}
_exit(-1);
}
复制代码
- 进程级的文件描述符表
- 控制文件描述符操做的一组标志(close-on-exec标志)
- 打开文件句柄的引用
- 系统级的打开文件表
- 当前文件偏移量
- 打开文件时使用的状态参数flags
- 文件访问模式
- 信号驱动I/O相关的设置
- 对文件i-node的引用
- 文件系统的i-node表
- 文件类型(FIFO ,套接字,常规文件)
- 指向文件所持有的锁的列表的指针
- 文件的各类属性
进程A,B的文件描述符指向同一文件句柄:多是fork()继承或者是UNIX域套接字传递;
进程A,B的文件描述符指向不一样文件句柄,不一样文件句柄指向i-node列表同一条目:对同一文件发起open()调用,(统一进程两次open()同一文件相似)
同一进程不一样文件描述符指向同一句柄,多是
dup()
实现
./myscripts > t.log 2>&1
#include<unistd.h>
#include<fcntl.h>
int newfd;
int dup(int oldfd);
int dup2(int oldfd, int newfd); // 忽略close(newfd)的错误
int fcntl(int fd, int cmd, ...);
//复制时 为 fcntl(oldfd, F_DUPFD, startfd) 大于等于startfd的最小未用值做为描述符编号
//下面三行成功时一致;oldfg不存在时dup2不会close(newfd),new=old时什么都不作
close(2); newfd=dup(1)
newfd=dup2(1,2)
close(2); newfd=fcntl(1, F_DUPFD,2)
#include<unistd.d>
//线程安全read,write
ssize_t pread(int fd, void *buf, size_t count, off_t offset);
ssize_t pwirte(int fd, const void *buf, size_t count, off_t offset);
#include<sys/uio.h>
struct iovec{
void *iov_base;
size_t iov_len;
}
//多缓冲区读写
ssize_t readv(int fd, const struct iovec *iov, int iovcnt);
ssize_t writev(int fd, const struct iovec *iov, int iovcnt);
复制代码
非阻塞I/O
管道,FIFO,套接字,设备支持非阻塞模式
大文件I/O(LFS)
大于2GB (32位)
两种方式支持:
/dev/fd目录
每一个进程的/dev/fd 连接到 /proc/$PID/fd
同时存在/dev/[stdin|stdout|stderr] 分别连接到/dev/fd/[0|1|2]
//对于同一进程如下等价
fd = open("/dev/fd/1", O_WRONLY)
fd = dup(1)
复制代码
临时文件的实现:打开文件后马上
unlink
,进程维护的打开文件表会在进程结束时才会释放文件。