內核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
,進程維護的打開文件表會在進程結束時纔會釋放文件。