顺义电大网上作业在那个网站做,专业网站优化方案,公司网站封面怎么做,注册公司上什么网站作者 | 闪客来源 | CSDN博客新建一个非常简单的 info.txt 文件。name:flash
age:28
language:java在命令行输入一条十分简单的命令。[rootlinux0.11] cat info.txt | wc -l
3这条命令的意思是读取刚刚的 info.txt 文件#xff0c;输出它的行数。我们之前分析了一下 shell 进程… 作者 | 闪客来源 | CSDN博客新建一个非常简单的 info.txt 文件。name:flash
age:28
language:java在命令行输入一条十分简单的命令。[rootlinux0.11] cat info.txt | wc -l
3这条命令的意思是读取刚刚的 info.txt 文件输出它的行数。我们之前分析了一下 shell 进程是如何读取你的命令的流程如下图。当然这里的 sleep_on 和 wake_up 是进程的阻塞与唤醒机制的实现我们没有展开讲解。那我们今天就详细看看这块的逻辑。首先表示进程的数据结构是 task_struct其中有一个 state 字段表示进程的状态它在 Linux 0.11 里有五种枚举值。// shed.h
#define TASK_RUNNING 0 // 运行态
#define TASK_INTERRUPTIBLE 1 // 可中断等待状态。
#define TASK_UNINTERRUPTIBLE 2 // 不可中断等待状态
#define TASK_ZOMBIE 3 // 僵死状态
#define TASK_STOPPED 4 // 停止当进程首次被创建时也就是 fork 函数执行后它的初始状态是 0也就是运行态。// system_call.s
_sys_fork:...call _copy_process...// fork.c
int copy_process(...) {...p-state TASK_RUNNING;...
}只有当处于运行态的进程才会被调度机制选中送入 CPU 开始执行。// sched.c
void schedule (void) {...if ((*p)-state TASK_RUNNING (*p)-counter c) {...next i;}...switch_to (next);
}以上我简单列出了关键代码基本可以描绘进程调度的大体框架。 所以使得一个进程阻塞的方法非常简单并不需要什么魔法只需要将其 state 字段变成非 TASK_RUNNING 也就是非运行态即可让它暂时不被 CPU 调度也就达到了阻塞的效果。同样唤醒也非常简单就是再将对应进程的 state 字段变成 TASK_RUNNING 即可。Linux 0.11 中的阻塞与唤醒就是 sleep_on 和 wake_up 函数。其中 sleep_on 函数将 state 变为 TASK_UNINTERRUPTIBLE。// sched.c
void sleep_on (struct task_struct **p) {struct task_struct *tmp;...tmp *p;*p current;current-state TASK_UNINTERRUPTIBLE;schedule();if (tmp)tmp-state 0;
}而 wake_up 函数将 state 变回为 TASK_RUNNING也就是 0。// sched.c
void wake_up (struct task_struct **p) {(**p).state 0;
}是不是非常简单当然 sleep_on 函数除了改变 state 状态之外还有些难理解的操作我们先试着来分析一下。当首次调用 sleep_on 函数时比如 tty_read 在 secondary 队列为空时调用 sleep_on传入的 *p 为 NULL因为此时还没有等待 secondary 这个队列的任务。struct tty_queue {... struct task_struct * proc_list;
};struct tty_struct {...struct tty_queue secondary;
};int tty_read(unsigned channel, char * buf, int nr) {...sleep_if_empty(tty-secondary);...
}static void sleep_if_empty(struct tty_queue * queue) {...interruptible_sleep_on(queue-proc_list);...
}通过 tmp *p 和 *p current 两个赋值操作此时tmp NULL*p 当前任务同时也使得 proc_list 指向了当前任务的 task_struct。当有另一个进程调用了 tty_read 读取了同一个 tty 的数据时就需要再次 sleep_on此时携带的 *p 就是一个指向了之前的当前任务的结构体。那么经过 tmp *p 和 *p current 两个赋值操作后会变成这个样子。也就是说通过每一个当前任务所在的代码块中的 tmp 变量总能找到上一个正在同样等待一个资源的进程因此也就形成了一个链表。那么当某进程调用了 wake_up 函数唤醒 proc_list 上指向的第一个任务时改任务变会在 sleep_on 函数执行完 schedule() 后被唤醒并执行下面的代码把 tmp 指针指向的上一个任务也同样唤醒。// sched.c
void sleep_on (struct task_struct **p) {struct task_struct *tmp;...tmp *p;*p current;current-state TASK_UNINTERRUPTIBLE;schedule();if (tmp)tmp-state 0;
}永远记住唤醒其实就是把 state 变成 0 而已。而上一个进程唤醒后和这个被唤醒的进程一样也会走过它自己的 sleep_on 函数的后半段把它的上一个进程也就是上上一个进程唤醒。 那么上上一个进程又会唤醒上上上一个进程上上上一个进程又会...看懂了没通过一个 wake_up 函数以及上述这种 tmp 变量的巧妙设计我们就能制造出唤醒的一连串连锁反应。当然唤醒后谁能优先抢到资源那就得看调度的时机以及调度的机制了对我们来说相当于听天由命了。OK现在我们的 shell 进程通过 read 函数中间经过了层层封装以及后面经过了阻塞与唤醒这一番折腾后终于把键盘输入的字符们成功由 tty 中的 secondary 队列读取并存放与 buf 指向的内存地址处。[rootlinux0.11] cat info.txt | wc -l接下来就该解析并执行这条命令了。// xv6-public sh.c
int main(void) {static char buf[100];// 读取命令while(getcmd(buf, sizeof(buf)) 0){// 创建新进程if(fork() 0)// 执行命令runcmd(parsecmd(buf));// 等待进程退出wait();}
}也就是上述函数中的 runcmd 命令。往期推荐Docker 那些事儿如何安全地停止、删除容器使用 nginx 轻松管理 kubernetes 资源文件Redis 内存满了怎么办这样置才正确实战 Kubectl 创建 Deployment 部署应用点分享点收藏点点赞点在看