网站页面怎么做,wordpress发表的文章在页面找不到,商务网站主页设计公司,屏蔽网页 的网站备案系列文章目录 文章目录 系列文章目录前言十一、操作系统上的进程1. 从系统启动到第一个进程系统调用#xff1a;fork(), 创建进程execv()PATH环境变量销毁进程 十二、进程的地址空间**查看进程的地址空间**进程地址空间管理进程地址空间隔离 十三、系统调用和 shell十四、C标准…系列文章目录 文章目录 系列文章目录前言十一、操作系统上的进程1. 从系统启动到第一个进程系统调用fork(), 创建进程execv()PATH环境变量销毁进程 十二、进程的地址空间**查看进程的地址空间**进程地址空间管理进程地址空间隔离 十三、系统调用和 shell十四、C标准库的实现fd, execve等封装内存管理封装 十五、fork概念fork的实现other应用弊端 十六、可执行文件可执行文件状态机的描述解析可执行文件逆向工程(Reverse Engineering)from C code to binary 十七、可执行文件的加载ELF loader on OS动态链接 十八、XV6调度 二十、处理器调度Linux Namespace Control Groupscgroups man procNAME描述推荐阅读 前言
十一、操作系统上的进程
操纵系统内核的启动CPU Reset-Firmware-Boot loader-Kernel_start()-… init进程, init进程通过系统调用创建Linux中的所有…
操作系统启动后做了什么操作系统如何管理程序(进程)
1. 从系统启动到第一个进程
eg:
int main()
{cte_init(on_interrupt); // 注册中断for (int i 0; i LENGTH(tasks); i){// 启动线程某些mcu甚至没有内存虚拟化只能启动简单的线程}mpe_init(); // 进入系统内核
}操作系统启动后会加载“第一个程序”之后Linux kernel进入后台成为“中断/异常处理程序”。
程序状态机
c代码视角语句汇编/机器代码视角指令与操作系统交互的方式syscall
syscall: 进程管理内存管理文件管理…
系统调用fork(), 创建进程
调用系统API, fork将当前状态复制
/* Clone the calling process, creating an exact copy.Return -1 for errors, 0 to the new process,and the process ID of the new process to the old process. */
extern __pid_t fork (void) __THROWNL;操作系统状态机的管理者 虚拟化操作系统可以同时管理多个状态机每一步选择一个状态执行
#include unistd.h
#include stdio.hint main()
{pid_t pid1 fork();pid_t pid2 fork();pid_t pid3 fork();printf(Hello World from (%d, %d, %d)\n, pid1, pid2, pid3);
}#include unistd.h
#include stdio.h
#include sys/wait.hint main(int argc, const *argv[])
{for (int i 0; i 2; i) {fork();printf(Hello\n);}for (int i 0; i 2; i) {wait(nullptr);}
}./a.out 打印6个Hello但是 ./a.out | cat 以及 ./a.out | cat 显示8个Hello 需要在开头加入 setbuf(stdout, nullptr); 不缓冲标准输出
解释
printf(Hello); // fflush(stdout);
int *ptr nullptr;
*ptr 1;若无 // 程序报错时不会输出hello若在输出前清空缓冲区则在报错前会输出hello
printf(); 会缓冲标准输出fork时缓冲区的内容也会同步复制因此./a.out | wc -l 为8
execv()
/* Replace the current process, executing PATH with arguments ARGV andenvironment ENVP. ARGV and ENVP are terminated by NULL pointers. */
extern int execve (const char *__path, char *const __argv[],char *const __envp[]) __THROW __nonnull ((1, 2));重置状态机
man execvebash -c env # 显示当前环境变量#include unistd.h
#include stdio.h// int main(int argc, char* argv[], char* envp[]);
int main()
{// nullptr 为手册中规定的必填项char* argv[] {/bin/bash, -c, env, nullptr};char* envp[] {HELLOWORLD, nullptr};execve(argv[0], argv, envp); // 重置状态机printf(Hello, World!\n);
}常用环境变量 略
export HELLOWORLD so env | grep HELLO get: HELLOWORLD
PATH环境变量
PATHx:y:z gcc test.c # 改变gcc环境变量从而到会不能正常运行销毁进程
销毁状态机 return; void exit(int status); // 进程normal terminate将 status 返回给父进程 _exit(); syscall(SYS_exit, 0);
#include stdlib.h
#include stdio.h
void func()
{printf(Goodbye, OS\n);
}
int main(int argc, char* argv[])
{atexit(func);// main函数返回退出进程if (argc 2) return EXIT_FAILURE;// 退出进程if (strcmp(argv[1], exit) 0) exit(0);// 退出进程不调用funcif (strcmp(argv[1], _exit) 0) _exit(0);// 强行退出当前线程不安全不会析构对象if (strcmp(argv[1], __exit) 0) syscall(SYS_exit, 0);
}详情
man exit
man _exit
man syscall十二、进程的地址空间
c代码堆、栈、内核区
汇编代码 地址空间寄存器
char *p可以和intptr_t互相转换
可以指向“任何地方”合法的地址可读或可写 代码main, %rip会从此处取出待执行的指令只读数据(static int x) 读写堆栈(int y), 读写运行时分配的内存读写动态链接库 非法的地址 NULL, 导致segmentation fault
查看进程的地址空间
32085: ./a.out
0000561407b26000 4K r---- a.out
0000561407b27000 4K r-x-- a.out
0000561407b28000 4K r---- a.out
0000561407b29000 4K r---- a.out
0000561407b2a000 4K rw--- a.out
00005614097ac000 132K rw--- [ anon ]
00007f9754000000 132K rw--- [ anon ]
00007f9754021000 65404K ----- [ anon ]
00007f975bbfe000 4K ----- [ anon ]
00007f975bbff000 8192K rw--- [ anon ]
00007f975c3ff000 4K ----- [ anon ]
00007f975c400000 8192K rw--- [ anon ]
00007f975cc00000 160K r---- libc.so.6
00007f975cc28000 1620K r-x-- libc.so.6
00007f975cdbd000 352K r---- libc.so.6
00007f975ce15000 16K r---- libc.so.6
00007f975ce19000 8K rw--- libc.so.6
00007f975ce1b000 52K rw--- [ anon ]
00007f975d000000 616K r---- libstdc.so.6.0.30
00007f975d09a000 1088K r-x-- libstdc.so.6.0.30
00007f975d1aa000 444K r---- libstdc.so.6.0.30
00007f975d219000 44K r---- libstdc.so.6.0.30
00007f975d224000 12K rw--- libstdc.so.6.0.30
00007f975d227000 12K rw--- [ anon ]
00007f975d2fc000 16K rw--- [ anon ]
00007f975d300000 56K r---- libm.so.6
00007f975d30e000 496K r-x-- libm.so.6
00007f975d38a000 364K r---- libm.so.6
00007f975d3e5000 4K r---- libm.so.6
00007f975d3e6000 4K rw--- libm.so.6
00007f975d3e7000 12K r---- libgcc_s.so.1
00007f975d3ea000 92K r-x-- libgcc_s.so.1
00007f975d401000 16K r---- libgcc_s.so.1
00007f975d405000 4K r---- libgcc_s.so.1
00007f975d406000 4K rw--- libgcc_s.so.1
00007f975d419000 8K rw--- [ anon ]
00007f975d41b000 8K r---- ld-linux-x86-64.so.2
00007f975d41d000 168K r-x-- ld-linux-x86-64.so.2
00007f975d447000 44K r---- ld-linux-x86-64.so.2
00007f975d453000 8K r---- ld-linux-x86-64.so.2
00007f975d455000 8K rw--- ld-linux-x86-64.so.2
00007fffca336000 132K rw--- [ stack ]
00007fffca3a6000 16K r---- [ anon ]
00007fffca3aa000 8K r-x-- [ anon ]
ffffffffff600000 4K --x-- [ anon ]pmap(1) - report memory of a process
Claim: pmap是通过访问procfs(/proc/)实现的 查看进程的地址空间进程的地址空间若干连续的“段”“段”的内存可以访问不在段内/违反权限的内存访问触发SIGSEGV gdb可以“越权访问”但不能访问“不存在”的地址
在不进入系统内核的情况下完成系统调用 例子
time时间内核维护秒级的时间(所有进程映射同一个页面)例子gettimeofday RTFSC RTFM
进程地址空间管理
#include sys/mman.hvoid *mmap(void *addr, size_t length, int prot, int flags,int fd, off_t offset);
int munmap(void *addr, size_t length);
将文件映射到进程地址空间
进程地址空间隔离
进程内的地址和数据只能访问本进程的地址空间
通过open(/proc/pid/mem, xxx);可访问其他进程的内存空间
十三、系统调用和 shell
shell提供用户接口
“与人类直接交互的第一个程序”帮助人类创建/管理进程(应用程序)、数据文件…
如
“Command-line interface”shell 是一门“把用户指令翻译成系统调用”的编程语言man sh(推荐阅读) bash …Graphical ShellGUI如WindowsSymbianAndroid…
shell 常用命令
重定向 ls a.txt管道 ls | wc -l后台 ls 命令组合 (echo a; echo b) | wc -l
A Zero-dependency UNIX Shell
零库函数依赖(-ffreestanding编译、ld链接)可以作为最小Linux的init程序用到 文件描述符一个打开文件的“指针”
如何阅读A Zero-dependency UNIX Shell的代码
strace gdb
管道的运行原理 easy and clear
十四、C标准库的实现
libc: 如何实现 printf(const char* fmt, …); 可变参数
fd, execve等封装
stdio.h FILE* 背后其实是是个文件描述符
可以用gdb查看具体的FILE* (如stdout)可以看到 glibc 的一些内部实现可以加载glibc的 debugs symbols封装了文件描述符上的系统调用(fseek, fgetpos, ftell, feof)
// 更易用的封装接口
execlp(echo, hello, world, nullptr);环境变量env, bash -c env
实现打印环境变量的小程序
#include stdio.h
int main()
{// Q? environ 是如何被赋值的extern char** environ;for (char** env environ; *env; env) {printf(%s\n, *env);}
}编译 gcc env.c -g -static 静态链接 gcc env.c -g 动态链接
gdb a.out
p (char**)environstartiwa (char**)environc
内存管理封装
malloc 和 free
在大区间[L, R) 中维护不相交的区间集合 M {[l0, r0), [l1, r1), …}malloc(s) - 返回一段大小未 s 的区间 必要时可以向操作系统申请额外的[L, R) 观察strace允许在内存不足时“拒绝”请求 free(l) - 给定l 删除 {l, r) ∈ M
Premature optimization is the root of all evil. – D.E.Knuth
workload? 合理假设
越小的对象创建/分配越频繁较为频繁地分配中等大小的对象低频率的大对象分配满足并行要求即每个线程都会“同时”分配内存 设置两套系统fast path 性能极好、并行度极高、覆盖大部分情况但有小概率会失败 slow path 不在乎那么快但把困难的事情做好 计算机系统里有很多这样的例子(比如cache)
参考 STL 的二级空间分配器 先用锁分配大块内存然后再把大块内存切成同等小块的分配
十五、fork
概念
系统调用 - libc - shell - 应用软件栈
fd本质是int型它像一个指针指向了操作系统的对象。
在调用execve时fd不会重置
int open(const char* pathname, int flags);
RTFM:O_CLOEXEC, O_APPEND 文件描述符一个指向操作系统内对象的“指针”对象只能通过操作系统允许的方式访问从0开始编号(0,1,2…stdin,stdout,stderr)可以通过open取的close释放dup“复制”对于数据文件文件描述符会“记住”上次访问文件的位置 write(3, “a”, 1); write(3, “b”, 1);
fork下的文件描述符
fd open(a.txt, O_WRONLY | O_CREAT); assert(fd 0);
int pid fork(); assert(pid 0);
if (pid 0) {write(fd, Hello, 5);
} else {write(fd, World, 5);
}在dup时不同的fd号指向同一个对象同时也共享offset
fork的实现
mmu分页 进程所有的页面属于操作系统进程只拥有映射表
page fault 缺页错误
copy on write
Copy-on-write只有被写入的页面才会复制一份 被复制后整个地址空间都被标记为“只读”操作系统捕获Page Fault后酌情复制页面fork-execve效率得到提升 操作系统会维护每个页面的引用计数
申请内存malloc(128M);
for(int i0; i1000; i) {if (pid 0) break;
}
使用内存所以真个操作系统里libc代码和只读数据只有一个副本推论统计进程占用的内存是个伪命题
所以一个进程占用了多少内存如何计算 用虚拟内存查看内存泄露
other
利用fork实现回溯 dfs_fork
#include stdio.h
#include unistd.h
#include stdint.h
#include assert.h
#include stdlib.h
#include string.h
#include errno.h
#include sys/wait.h#define DEST
#define EMPTY .struct move {int x, y, ch;
} moves[] {{ 0, 1, },{ 1, 0, v },{ 0, -1, },{ -1, 0, ^ },
};char map[][512] {#######,#.#.##,#.....#,#.....#,#...#.#,#######,,
};void display();void dfs(int x, int y) {if (map[x][y] DEST) {display();} else {int nfork 0;for (struct move *m moves; m moves 4; m) {int x1 x m-x, y1 y m-y;if (map[x1][y1] DEST || map[x1][y1] EMPTY) {int pid fork(); assert(pid 0);if (pid 0) { // map[][] copiedmap[x][y] m-ch;dfs(x1, y1);exit(0); // clobbered map[][] discarded} else {nfork;waitpid(pid, NULL, 0); // wait here to serialize the search}}}while (nfork--) wait(NULL);}
}int main() {dfs(1, 1);
}void display() {for (int i 0; ; i) {for (const char *s map[i]; *s; s) {switch (*s) {case EMPTY: printf( ); break;case DEST : printf( ○ ); break;case : printf( → ); break;case : printf( ← ); break;case ^ : printf( ↑ ); break;case v : printf( ↓ ); break;default : printf(▇▇▇); break;}}printf(\n);if (strlen(map[i]) 0) break;}fflush(stdout);sleep(1); // to see the effect of parallel search
}应用
跳过初始化
Zygote Process (Android) Java Virtual Machine 初始化涉及大量的类加载一次加载全员使用 App 使用的系统资源基础类库libc… Chrome site isolation (Chrome)Fork server (AFL)
利用fork保存当前程序快照从而记录程序运行中的某种状态
弊端
如果只有内存和文件描述符没问题信号线程进程间通讯对象ptrace追踪/调试
so:
int posix_spawn(pid_t *pid, char*path, posix_spawn_file_actions_t *file_actions, posix_spawnattr_t *attrp, char* argv[], char* envp[]);十六、可执行文件
可执行文件解析可执行文件链接和加载 假设只有静态链接
手册 System V ABI: System V Application Binary Interface
file a.out a.out: ELF 64-bit LSB executable, x86-64, version 1 (GNU/Linux), statically linked, BuildID[sha1]b6e0470ba2180936a61ec7ab71131a73121f88ee, for GNU/Linux 3.2.0, not stripped
可执行文件状态机的描述
可执行文件是最重要的操作系统对象描述了状态机的初始状态迁移的数据结构 寄存器内存
strace a.out
---
execve(./a.out, [./a.out], 0x7ffe0d273c20 /* 63 vars */) 0
arch_prctl(0x3001 /* ARCH_??? */, 0x7ffc6f6cbdc0) -1 EINVAL (Invalid argument)
brk(NULL) 0xd62000
brk(0xd62dc0) 0xd62dc0
arch_prctl(ARCH_SET_FS, 0xd623c0) 0
set_tid_address(0xd62690) 7873
set_robust_list(0xd626a0, 24) 0
rseq(0xd62d60, 0x20, 0, 0x53053053) 0
uname({sysnameLinux, nodenameMyComputer, ...}) 0
prlimit64(0, RLIMIT_STACK, NULL, {rlim_cur8192*1024, rlim_maxRLIM64_INFINITY}) 0
readlink(/proc/self/exe, /home/sdf/a.out, 4096) 15
getrandom(\x81\x04\x29\xc6\xd5\x76\x65\x0f, 8, GRND_NONBLOCK) 8
brk(0xd83dc0) 0xd83dc0
brk(0xd84000) 0xd84000
mprotect(0x4c1000, 16384, PROT_READ) 0
exit_group(0) ?exited with 0 strace ./a.c
---
execve(./a.c, [./a.c], 0x7ffe85525b00 /* 63 vars */) -1 ENOEXEC (Exec format error)
strace: exec: Exec format errorexited with 1
Linux a.out(deprecated)ELF(Executable Linkable Format)Shell-bang Shell-bang 其实是一个“偷换参数”的execve当加载器读取到#!后会将其后的参数传入execve #! magic number解析可执行文件
https://www.gnu.org/software/binutils/ GNU binutils
生成可执行文件 ld(linker), as(assembler)ar, ranlib 分析可执行文件 objcopy/objdump/readelfaddr2line, size, nm
gdb core bt/backtrace 如何追踪到程序挂掉时的调用栈 函数调用call时会在栈上留下endbr然后push %rbp然后mov %rsp,%rbp
逆向工程(Reverse Engineering)
from C code to binary
int main()
{hello();
}void hello()
{char* p (char*)main 0xa 1;int32_t offset *(int32_t*)p;assert( (char*)main 0xf offset (char*)hello );// (char*)main 0xf // call hello的next PC
}重新理解编译、链接 连接器(ld)将所有的符号链接
十七、可执行文件的加载
若干真正的静态ELF加载器动态链接和加载
ELF loader on OS
可执行文件
一个描述了状态机的初始状态(迁移)的数据结构 不同于内存里的数据结构“指针”都被“偏移量”代替数据结构各个部分定义/usr/include/elf.h
加载器(loader)
解析数据结构复制到内存跳转创建进程运行时初始状态(argv, evnp, …) loader-static.c 可以加载任何静态链接的代码 minimal.S, dfs-fork.c并且能正确处理参数/环境变量 env.c RTFM:
loader-static.c
#include stdint.h
#include stdio.h
#include string.h
#include stdlib.h
#include unistd.h
#include assert.h
#include elf.h
#include fcntl.h
#include sys/mman.h#define STK_SZ (1 20)
#define ROUND(x, align) (void *)(((uintptr_t)x) ~(align - 1))
#define MOD(x, align) (((uintptr_t)x) (align - 1))
#define push(sp, T, ...) ({ *((T*)sp) (T)__VA_ARGS__; sp (void *)((uintptr_t)(sp) sizeof(T)); })void execve_(const char *file, char *argv[], char *envp[]) {// WARNING: This execve_ does not free process resources.int fd open(file, O_RDONLY);assert(fd 0);Elf64_Ehdr *h mmap(NULL, 4096, PROT_READ, MAP_PRIVATE, fd, 0);assert(h ! (void *)-1);assert(h-e_type ET_EXEC h-e_machine EM_X86_64);Elf64_Phdr *pht (Elf64_Phdr *)((char *)h h-e_phoff);for (int i 0; i h-e_phnum; i) {Elf64_Phdr *p pht[i];if (p-p_type PT_LOAD) {int prot 0;if (p-p_flags PF_R) prot | PROT_READ;if (p-p_flags PF_W) prot | PROT_WRITE;if (p-p_flags PF_X) prot | PROT_EXEC;void *ret mmap(ROUND(p-p_vaddr, p-p_align), // addr, rounded to ALIGNp-p_memsz MOD(p-p_vaddr, p-p_align), // lengthprot, // protectionMAP_PRIVATE | MAP_FIXED, // flags, private strictfd, // file descriptor(uintptr_t)ROUND(p-p_offset, p-p_align)); // offsetassert(ret ! (void *)-1);memset((void *)(p-p_vaddr p-p_filesz), 0, p-p_memsz - p-p_filesz);}}close(fd);static char stack[STK_SZ], rnd[16];void *sp ROUND(stack sizeof(stack) - 4096, 16);void *sp_exec sp;int argc 0;// argcwhile (argv[argc]) argc;push(sp, intptr_t, argc);// argv[], NULL-terminatefor (int i 0; i argc; i)push(sp, intptr_t, argv[i]);// envp[], NULL-terminatefor (; *envp; envp) {if (!strchr(*envp, _)) // remove some verbose onespush(sp, intptr_t, *envp);}// auxv[], AT_NULL-terminatepush(sp, intptr_t, 0);push(sp, Elf64_auxv_t, { .a_type AT_RANDOM, .a_un.a_val (uintptr_t)rnd } );push(sp, Elf64_auxv_t, { .a_type AT_NULL } );asm volatile(mov $0, %%rdx; // required by ABImov %0, %%rsp;jmp *%1 : : a(sp_exec), b(h-e_entry));
}int main(int argc, char *argv[], char *envp[]) {if (argc 2) {fprintf(stderr, Usage: %s file [args...]\n, argv[0]);exit(1);}execve_(argv[1], argv 1, envp);
}boot main
#include stdint.h
#include elf.h
#include x86/x86.h#define SECTSIZE 512
#define ARGSIZE 1024static inline void wait_disk(void) {while ((inb(0x1f7) 0xc0) ! 0x40);
}static inline void read_disk(void *buf, int sect) {wait_disk();outb(0x1f2, 1);outb(0x1f3, sect);outb(0x1f4, sect 8);outb(0x1f5, sect 16);outb(0x1f6, (sect 24) | 0xE0);outb(0x1f7, 0x20);wait_disk();for (int i 0; i SECTSIZE / 4; i ) {((uint32_t *)buf)[i] inl(0x1f0);}
}static inline void copy_from_disk(void *buf, int nbytes, int disk_offset) {uint32_t cur (uint32_t)buf ~(SECTSIZE - 1);uint32_t ed (uint32_t)buf nbytes;uint32_t sect (disk_offset / SECTSIZE) (ARGSIZE / SECTSIZE) 1;for(; cur ed; cur SECTSIZE, sect )read_disk((void *)cur, sect);
}static void load_program(uint32_t filesz, uint32_t memsz, uint32_t paddr, uint32_t offset) {copy_from_disk((void *)paddr, filesz, offset);char *bss (void *)(paddr filesz);for (uint32_t i filesz; i ! memsz; i) {*bss 0;}
}static void load_elf64(Elf64_Ehdr *elf) {Elf64_Phdr *ph (Elf64_Phdr *)((char *)elf elf-e_phoff);for (int i 0; i elf-e_phnum; i, ph) {load_program((uint32_t)ph-p_filesz,(uint32_t)ph-p_memsz,(uint32_t)ph-p_paddr,(uint32_t)ph-p_offset);}
}static void load_elf32(Elf32_Ehdr *elf) {Elf32_Phdr *ph (Elf32_Phdr *)((char *)elf elf-e_phoff);for (int i 0; i elf-e_phnum; i, ph) {load_program((uint32_t)ph-p_filesz,(uint32_t)ph-p_memsz,(uint32_t)ph-p_paddr,(uint32_t)ph-p_offset);}
}void load_kernel(void) {Elf32_Ehdr *elf32 (void *)0x8000;Elf64_Ehdr *elf64 (void *)0x8000;int is_ap boot_record()-is_ap;if (!is_ap) {// load argument (string) to memorycopy_from_disk((void *)MAINARG_ADDR, 1024, -1024);// load elf header to memorycopy_from_disk(elf32, 4096, 0);if (elf32-e_machine EM_X86_64) {load_elf64(elf64);} else {load_elf32(elf32);}} else {// everything should be loaded}if (elf32-e_machine EM_X86_64) {((void(*)())(uint32_t)elf64-e_entry)();} else {((void(*)())(uint32_t)elf32-e_entry)();}
} linux 内核源码
解压make menuconfig (生成.config文件)make bzImage -j8 (生成镜像)
编译结果
vmlinuxELF格式的内核二进制代码vmlinuz ( 压缩的镜像可以直接被QEMU加载 )readelf入口地址0x1000000(物理内存16M位置)
动态链接
…
存储保护和加载位置
允许将.dl中的一部分以某个指定的权限映射到内存的某个位置(program header table)允许自由指定加载器加入INTERP
空间浪费
字符串存储在常量池统一通过“指针”访问
–
“符号表”就是Global Offset Table(GOT)增加一层indirection: Procedure Linkage Table(PLT)所有未解析的符号都统一翻译成call
十八、XV6
source code: xv6-riscv
xv6: UNIX v6的现代“克隆” 接近完整的UNIX Shell体验
基本工具集(wc, echo, cat, …)命令执行、管道、重定向 支持多处理器Now in RISC-V 通过这些系统调用足够支撑以下应用 cc, as, ld, vi, sed, awk, troff, lp, …
当拿到makefile时
make -nB qemu想要知道所有命令调用序列 make -nB qemu | vim - :set nowrap 格式化显示文件的编译 :%s/ /\r /gbear make qemu 在xv6内生成 compile_commands.json
调度 二十、处理器调度
最简单最直接的调度方法轮询调度法或称时间片轮转法。
考虑到上述调度算法的缺陷引入优先级策略 nice : [-20,19] 数字越大优先级越低。nice好人卡nice19老好人了谁要CPU 就给谁一点不抢nice-20这个人坏极了一直霸占cpu。 基于优先级的调度 RTOS: 优先级高的抢占优先级低的优先级高的总是先执行直到高优先级程序放弃cpu执行权Linuxnice相差10CPU资源获取率相差10倍nice/renice 将某个进程绑定到某个cpu上 taskset -c 0 nice -n 19 yes /dev/null taskset -c 0 nice -n 9 yes /dev/null 动态优先级(MLFQ) Complete Fair Scheduling (CFS) const int sched_prio_to_weight[40]
{
/*-20*/ 88761,71755,56483,46273,36291,
/*-15*/ 29154,23254,18705,14949,11916,
/*-10*/ 9548,7620,6100,4904,3906,
/*-5*/ 3121,2501,1991,1586,1277,
/*0*/ 1024,820,655,526,423,
/*5*/ 335,272,215,172,137,
/*10*/ 110, 87, 70, 56,45,
/*15*/ 36, 29, 23, 18, 15,
}哪个程序占用的 虚拟cpu时间 短就给谁 CPU
没有完美的调度算法不存在一种包揽所有场景的调度算法。 算法设计需要和业务相配合
Linux Namespace Control Groupscgroups
namespace轻量级虚拟化 cgroups 允许以进程组为单位管理资源 man proc
NAME
proc - 进程信息伪文件系统
描述
proc伪文件系统
推荐阅读 The GNU C Library newlibc