Linux内核系列:shell命令和脚本执行原理

shell命令

shell不属于内核,而是以内核之外的用户态方式运行。因此它就相当于操作系统的一层外壳,为用户提供使用操作系统的接口。
shell命令分为内置命令和外部命令,如下图,cd是外部命令,cat等都是外部命令。


当我们在命令行输入一个命令之后,会发生什么?

  • 检查用户输入的命令是否是一个内部命令,如果不是检查是不是一个应用程序;
  • shell在搜索路径或者环境变量中找应用程序;
  • 如果不是一个外部命令并且没有查找到可执行文件,显示错误信息;- 如果成功找到,内部命令或者应用程序会被分解为系统调用传给内核,内核完成工作。

像cd,pwd这些内置命令是属于Shell的一部分,当Shell一运行起来就随Shell加载入内存,因此,当我们在命令行上输入这些命令就可以像调用函数一样直接使用,效率非常高。而如ls,cat这些外部命令却不是如此,当我们在命令行输入cat,当前的Shell会fork一个子进程,然后调用exec载入这个命令的可执行文件,比如bin/cat,(注意execve系统调用:它会把新程序加载到当前进程的内存空间内,当前的进程会被丢弃,它的堆、栈和所有的段数据都会被新进程相应的部分代替,然后会从新程序的初始化代码和 main 函数开始运行。同时,进程的 ID 将保持不变。)因此效率上稍微低了点,最后shell用wait命令等待新进程结束

shell 脚本

shell脚本原理和shell命令相同,对于外部都是使用子进程执行。
这里突发奇想shell脚本能多线程吗?查了资料是可以的:

  • 使用&后台运行
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    #/bin/bash

    all_num=10

    a=$(date +%H%M%S)

    for num in `seq 1 ${all_num}`
    do
    {
    sleep 1
    echo ${num}
    } &
    done

    b=$(date +%H%M%S)

    echo -e "startTime:\t$a"
    echo -e "endTime:\t$b"

控制同步:

  • 使用&后台运行,使用wait同步
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    #/bin/bash

    all_num=10

    a=$(date +%H%M%S)

    for num in `seq 1 ${all_num}`
    do
    {
    sleep 1
    echo ${num}
    } &
    done

    wait

    b=$(date +%H%M%S)

    echo -e "startTime:\t$a"
    echo -e "endTime:\t$b"
    控制并发数:
  • 使用xargs -P
1
2
3
4
5
6
7
8
9
10
11
12
13
#/bin/bash

all_num=10
thread_num=5

a=$(date +%H%M%S)

seq 1 ${all_num} | xargs -n 1 -I {} -P ${thread_num} sh -c "sleep 1;echo {}"

b=$(date +%H%M%S)

echo -e "startTime:\t$a"
echo -e "endTime:\t$b"
  • 使用GNU parallel
1
2
3
4
5
6
7
8
9
10
11
12
13
14
#/bin/bash

all_num=10
thread_num=6

a=$(date +%H%M%S)


parallel -j 5 "sleep 1;echo {}" ::: `seq 1 10`

b=$(date +%H%M%S)

echo -e "startTime:\t$a"
echo -e "endTime:\t$b"