作者 | 闪客

来源 | CSDN博客

新建一个非常简单的 info.txt 文件。

name:flash
age:28
language:java

在命令行输入一条十分简单的命令。

[root@linux0.11] cat info.txt | wc -l
3

这条命令的意思是读取刚刚的 info.txt 文件,输出它的行数。

我们之前分析了一下 shell 进程是如何读取你的命令的,流程如下图。

当然,这里的 sleep_onwake_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 指向的内存地址处。

[root@linux0.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 部署应用

点分享

点收藏

点点赞

点在看

一条 shell 命令的阻塞与唤醒相关推荐

  1. python 运行多条shell命令

    使用py时可能需要连续运行多条shell 命令 1. # coding: UTF-8 import sys reload(sys) sys.setdefaultencoding('utf8')impo ...

  2. awk命令中执行多条shell命令

    awk中使用的shell命令,有2种方法: 一.使用system() 二.使用print cmd | "/bin/bash" http://www.gnu.org/software ...

  3. pythonsubprocess执行多条shell命令_python中subprocess批量执行linux命令

    可以执行shell命令的相关模块和函数有: os.system os.spawn os.popen --废弃 popen --废弃 commands --废弃,3.x中被移除 以上执行shell命令的 ...

  4. 用几条shell命令快速去重10G数据

    试想一下,如果有10G数据,或者更多:怎么才能够快速地去重呢?你会说将数据导入到数据库(mysql等)进行去重,或者用java写个程序进行去重,或者用Hadoop进行处理.如果是大量的数据要写入数据库 ...

  5. c语言字符串去重用指针,用几条shell命令快速去重10G数据

    试想一下,如果有10G数据,或者更多:怎么才能够快速地去重呢?你会说将数据导入到数据库(mysql等)进行去重,或者用java写个程序进行去重,或者用Hadoop进行处理.如果是大量的数据要写入数据库 ...

  6. shell脚本不暂停进程,暂停几秒执行下一条shell命令

    需求如下: 跑monkey脚本如果遇到异常结束,则使用logcat输出日志,输出三秒后停止输出 如果使用sleep 命令的话,则会导致logcat在sleep的时候也暂停执行了 解决方法如下,通过wh ...

  7. linux脚本执行暂停,shell脚本不暂停进程,暂停几秒执行下一条shell命令

    需求如下: 跑monkey脚本如果遇到异常结束,则使用logcat输出日志,输出三秒后停止输出 如果使用sleep 命令的话,则会导致logcat在sleep的时候也暂停执行了 解决方法如下,通过wh ...

  8. Android adb shell 命令

    adb 概述 SDK的Tools文件夹下包含着Android模拟器操作的重要命令 adb,adb的全称为(Android Debug Bridge就是调试桥的作用.通过adb我们可以在Eclipse中 ...

  9. 【Makefile】Makefile与shell命令为何总是藕断丝连?

    博主最近在项目实践过程中,需要深度定制项目的Makefile,其中有些复杂的流程必须得借助shell脚本才能高效地完成,为此博主特意深入学习了在Makefile种调用shell命令的方法. 大家都知道 ...

最新文章

  1. C++sparse matrix 稀疏矩阵的实现算法(附完整源码)
  2. 在基于Jetty Fast CGI Proxy和php-fpm下配置Discuz
  3. Java synchronized 中的while 和 notifyAll
  4. Intellij Idea乱码解决方案都在这里了
  5. greenplum vacuum清理删除数据命令
  6. 如何在Word里面自动生成目录
  7. 【目标检测】单阶段算法--YOLOv3详解
  8. 我的世界pc正版好玩的服务器,都来看看好玩的服务器
  9. python字符编码正确的是_python字符编码
  10. spark 查看yarn日志_spark周边项目之Livy
  11. Nginx一个server配置多个location
  12. python java正则表达式_java 正则表达式
  13. 7.凤凰架构:构建可靠的大型分布式系统 --- 从类库到服务
  14. keras小程序(一),用cnn做分类
  15. 寻找两个正序数组中的中位数 数组
  16. 【华为云技术分享】软件工程师的AI模型训练起步
  17. 联想开机壁纸存放位置
  18. 浏览器主页被360篡改怎么办?
  19. 网站建设的一些基本教程操作
  20. 武神主宰中的科幻理论体系设定

热门文章

  1. 使用Facebook的Stetho调试Android应用
  2. python运用在大数据中精准生活_在大数据中精准生活阅读理解答案
  3. python图像拼接_python opencv 图像拼接的实现方法
  4. iOS定位及解析经纬度【Swift】
  5. 夫妻同心 ,黄土变金。
  6. 21种方法快速上手谷歌SEO
  7. 超大容量充电宝哪个好?手机充电宝哪款容量大又好用
  8. ubuntu18.04 下firefox 不能 播放视频,因为默认未安装FLASH插件。(当然只是原因之一)
  9. Android Java判断密码强度 强度显示
  10. IIS mime类型