在Linux中,所有内容都是以文件的形式保存和管理的,即一切皆文件,普通文件是文件,目录(Windows下称为文件夹)是文件,硬件设备(键盘、监视器、硬盘、打印机等)都是文件,就连套接字(socket)、网络通信等资源也都是文件。
Linux为应用程序访问文件提供了统一的接口,如read、write、open等,也称为
虚拟文件系统(VFS,Virtual File System)。虚拟文件系统其实就是一个目录树,树上不同的节点可以映射到物理的文件地址,也可以进行挂载,相当于一个解耦层,在具体的文件系统之上抽象的一层,为能够给各种文件系统提供一个通用的接口,使上层的应用程序能够使用通用的接口访问不同文件系统、不同的驱动。
逻辑上抽象为一棵树,物理上可以来自于不同的资源。
虚拟文件目录树的结构
Linux下虚拟文件目录树的结构如下:
# ls /
bin boot cdrom data dev etc home lib lib64 media mnt opt proc root run sbin srv sys tmp usr var
看起来虽然所有的文件都位于同一个根目录下,但是其中的部分文件来源于不同的分区中,例如/boot
目录来源于分区/dev/sda1
:
# df -h
Filesystem Size Used Avail Use% Mounted on
/dev/mapper/centos-root 20G 14G 6.4G 68% /
devtmpfs 3.9G 0 3.9G 0% /dev
tmpfs 3.9G 0 3.9G 0% /dev/shm
tmpfs 3.9G 377M 3.5G 10% /run
tmpfs 3.9G 0 3.9G 0% /sys/fs/cgroup
/dev/mapper/centos-home 53G 2.4G 50G 5% /home
/dev/sda1 497M 98M 399M 20% /boot
tmpfs 782M 0 782M 0% /run/user/0
/dev/loop0 97M 4.9M 85M 6% /mnt/test
计算机引导的时候是先挂载了分区/dev/sda1
,又挂载了分区/dev/mapper/vg_10-lv_root
,为了方便查看内核启动时的一些配置,又将/dev/mapper/vg_10-lv_root
挂载到了/
目录下,所以/boot
目录已经完成了他的使命,是可以被卸载的。
下面演示/boot
的卸载:
# ls
/boot
config-2.6.32-431.el6.x86_64 efi grub initramfs-2.6.32-431.el6.x86_64.img initrd-2.6.32-431.el6.x86_64kdump.img lost+found symvers-2.6.32-431.el6.x86_64.gz System.map-2.6.32-431.el6.x86_64 vmlinuz-2.6.32-431.el6.x86_64
# umount /boot
# ll /boot
total 0
# mount /dev/sda1 /boot
# ls /boot
config-2.6.32-431.el6.x86_64 efi grub initramfs-2.6.32-431.el6.x86_64.img initrd-2.6.32-431.el6.x86_64kdump.img lost+found symvers-2.6.32-431.el6.x86_64.gz System.map-2.6.32-431.el6.x86_64 vmlinuz-2.6.32-431.el6.x86_64
inode
虚拟文件系统目录树的每一个文件都有个inode号,当对这个文件进行读取操作时会先读取到PageCache页缓存中,然后再读到应用程序的缓冲区中,每一个PageCache是4kb的大小,如果两个程序如果打开的是同一个文件,那么这两个程序会共享同一个PageCache。
如果后续我们对pagecache进行了修改,会产生脏数据,这时候需要使用flush刷新到磁盘中去。“脏”这个标识是内核对于上层打开的文件的一个统一的管理,并不是针对某一个文件。内核会根据自己的设定,把数据输入到磁盘中去。如果你在5秒钟内产生了3.8G数据,又恰好没有触发内核的写磁盘操作,这时候突然断电会导致你这些数数据的丢失。
如果两个线程同时修改同一块数据,会加锁。
文件描述符
文件描述符:File descriptor,简称fd,当应用程序请求内核打开/新建一个文件(资源)时,内核会返回一个文件描述符用于对应这个打开/新建的文件,其fd本质上就是一个非负整数。实际上,它是一个索引值,指向内核为每一个进程所维护的该进程打开文件的记录表。系统为每一个进程维护了一个文件描述符表,该表的值都是从0开始的,所以在不同的进程中你会看到相同的文件描述符,这种情况下相同文件描述符有可能指向同一个文件,也有可能指向不同的文件。当程序打开一个现有文件或者创建一个新文件时,内核向进程返回一个文件描述符。
每一个进程都会有下面三个文件描述符:
- 0:标准输入(standard input)
- 1:标准输出(standard output)
- 2:标准错误(standard error)
怎么查看一个进程打开了那些文件呢?可以使用lsof命令。
lsof是list open files的简称,它的作用主要是列出系统中打开的文件,基本上linux系统中所有的对象都可以看作文件,lsof可以查看用户和进程操作了哪些文件,也可以查看系统中网络的使用情况,以及设备的信息。
例如可以使用ls -p $$
查看当前shell打开了哪些文件,其中node列表示的是文件的inode号 :
# lsof -p $$
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
bash 49289 root cwd DIR 253,1 4096 100663425 /root
bash 49289 root rtd DIR 253,1 4096 128 /
bash 49289 root txt REG 253,1 960368 67152288 /usr/bin/bash
bash 49289 root mem REG 253,1 106176928 100665182 /usr/lib/locale/locale-archive
bash 49289 root mem REG 253,1 61560 100665275 /usr/lib64/libnss_files-2.17.so
bash 49289 root mem REG 253,1 2156240 100665256 /usr/lib64/libc-2.17.so
bash 49289 root mem REG 253,1 19248 100665262 /usr/lib64/libdl-2.17.so
bash 49289 root mem REG 253,1 174520 100665356 /usr/lib64/libtinfo.so.5.9
bash 49289 root mem REG 253,1 163312 100665250 /usr/lib64/ld-2.17.so
bash 49289 root mem REG 253,1 26970 2712 /usr/lib64/gconv/gconv-modules.cache
bash 49289 root 0u CHR 136,0 0t0 3 /dev/pts/0
bash 49289 root 1u CHR 136,0 0t0 3 /dev/pts/0
bash 49289 root 2u CHR 136,0 0t0 3 /dev/pts/0
bash 49289 root 255u CHR 136,0 0t0 3 /dev/pts/0
下面通过一个例子来模拟文件的读与写:
- 创建一个文件描述符8,用来读取ooxx.txt,lsof加上-o参数的话,会显示一列OFFSET,表示当前读文件位置的指针。
# exec 8< ooxx.txt
# ll /proc/$$/fd
total 0
lrwx------ 1 root root 64 Mar 2 09:53 0 -> /dev/pts/0
lrwx------ 1 root root 64 Mar 2 09:53 1 -> /dev/pts/0
lrwx------ 1 root root 64 Mar 2 09:53 2 -> /dev/pts/0
lrwx------ 1 root root 64 Mar 2 10:28 255 -> /dev/pts/0
lr-x------ 1 root root 64 Mar 2 10:32 8 -> /root/test/ooxx.txt
# stat /root/test/ooxx.txt
File: ‘/root/test/ooxx.txt’
Size: 167 Blocks: 8 IO Block: 4096 regular file
Device: fd01h/64769d Inode: 101908892 Links: 1
Access: (0644/-rw-r--r--) Uid: ( 0/ root) Gid: ( 0/ root)
Access: 2021-03-02 10:31:41.600724381 +0800
Modify: 2021-03-02 10:31:41.600724381 +0800
Change: 2021-03-02 10:31:41.600724381 +0800
Birth: -
# lsof -op $$
COMMAND PID USER FD TYPE DEVICE OFFSET NODE NAME
bash 49289 root cwd DIR 0,3 412864500 /proc/49289/fd
bash 49289 root rtd DIR 253,1 128 /
bash 49289 root txt REG 253,1 67152288 /usr/bin/bash
bash 49289 root mem REG 253,1 100665182 /usr/lib/locale/locale-archive
bash 49289 root mem REG 253,1 100665275 /usr/lib64/libnss_files-2.17.so
bash 49289 root mem REG 253,1 100665256 /usr/lib64/libc-2.17.so
bash 49289 root mem REG 253,1 100665262 /usr/lib64/libdl-2.17.so
bash 49289 root mem REG 253,1 100665356 /usr/lib64/libtinfo.so.5.9
bash 49289 root mem REG 253,1 100665250 /usr/lib64/ld-2.17.so
bash 49289 root mem REG 253,1 2712 /usr/lib64/gconv/gconv-modules.cache
bash 49289 root 0u CHR 136,0 0t0 3 /dev/pts/0
bash 49289 root 1u CHR 136,0 0t0 3 /dev/pts/0
bash 49289 root 2u CHR 136,0 0t0 3 /dev/pts/0
bash 49289 root 8r REG 253,1 0t0 101908892 /root/test/ooxx.txt
bash 49289 root 255u CHR 136,0 0t0 3 /dev/pts/0
从上面的结果可以看出:
- /proc/$$/fd
下面多了文件描述符8。 - lsof的node列的编号与ooxx.txt的inode号一致,offfset列的偏移量为0。
- 使用read读文件:
# read a 0<&8
# echo $a
TYPE=Ethernet
# lsof -op $$
COMMAND PID USER FD TYPE DEVICE OFFSET NODE NAME
bash 49289 root cwd DIR 0,3 412864500 /proc/49289/fd
bash 49289 root rtd DIR 253,1 128 /
bash 49289 root txt REG 253,1 67152288 /usr/bin/bash
bash 49289 root mem REG 253,1 100665182 /usr/lib/locale/locale-archive
bash 49289 root mem REG 253,1 100665275 /usr/lib64/libnss_files-2.17.so
bash 49289 root mem REG 253,1 100665256 /usr/lib64/libc-2.17.so
bash 49289 root mem REG 253,1 100665262 /usr/lib64/libdl-2.17.so
bash 49289 root mem REG 253,1 100665356 /usr/lib64/libtinfo.so.5.9
bash 49289 root mem REG 253,1 100665250 /usr/lib64/ld-2.17.so
bash 49289 root mem REG 253,1 2712 /usr/lib64/gconv/gconv-modules.cache
bash 49289 root 0u CHR 136,0 0t0 3 /dev/pts/0
bash 49289 root 1u CHR 136,0 0t0 3 /dev/pts/0
bash 49289 root 2u CHR 136,0 0t0 3 /dev/pts/0
bash 49289 root 8r REG 253,1 0t14 101908892 /root/test/ooxx.txt
bash 49289 root 255u CHR 136,0 0t0 3 /dev/pts/0
说明:
- 定义了一个变量a,将文件描述符8的内容读取到标准输入0,并赋值给a。
- 由于read命令遇到换行符就会停止,所以一共读出了
TYPE=Ethernet\n
14个字符,offset的位置变成了14。
- 新开一个标签页,用一个新的文件描述符6,去读取ooxx.txt证明两个进程读取文件时,不会相互影响:
# exec 6<ooxx.txt
# ll /proc/$$/fd
total 0
lrwx------ 1 root root 64 Mar 2 10:56 0 -> /dev/pts/1
lrwx------ 1 root root 64 Mar 2 10:56 1 -> /dev/pts/1
lrwx------ 1 root root 64 Mar 2 10:56 2 -> /dev/pts/1
lrwx------ 1 root root 64 Mar 2 10:56 255 -> /dev/pts/1
lr-x------ 1 root root 64 Mar 2 10:56 6 -> /root/test/ooxx.txt
# lsof -op $$
COMMAND PID USER FD TYPE DEVICE OFFSET NODE NAME
bash 56095 root cwd DIR 253,1 101908867 /root/test
bash 56095 root rtd DIR 253,1 128 /
bash 56095 root txt REG 253,1 67152288 /usr/bin/bash
bash 56095 root mem REG 253,1 100665182 /usr/lib/locale/locale-archive
bash 56095 root mem REG 253,1 100665275 /usr/lib64/libnss_files-2.17.so
bash 56095 root mem REG 253,1 100665256 /usr/lib64/libc-2.17.so
bash 56095 root mem REG 253,1 100665262 /usr/lib64/libdl-2.17.so
bash 56095 root mem REG 253,1 100665356 /usr/lib64/libtinfo.so.5.9
bash 56095 root mem REG 253,1 100665250 /usr/lib64/ld-2.17.so
bash 56095 root mem REG 253,1 2712 /usr/lib64/gconv/gconv-modules.cache
bash 56095 root 0u CHR 136,1 0t0 4 /dev/pts/1
bash 56095 root 1u CHR 136,1 0t0 4 /dev/pts/1
bash 56095 root 2u CHR 136,1 0t0 4 /dev/pts/1
bash 56095 root 6r REG 253,1 0t0 101908892 /root/test/ooxx.txt
bash 56095 root 255u CHR 136,1 0t0 4 /dev/pts/1
从上面的结果可以发现两个进程读取同一个文件时的偏移量时不一样的,互相不影响。
文件类型
既然虚拟文件系统中的一切皆文件,那么如何区分这些不同的文件?我们引入文件类型:
文件类型 | 说明 |
-(regular file) | 普通文件 |
d(directory) | 目录 |
l(link) | 链接 |
b(block) | 块设备文件,如磁盘,支持以block为单位随机访问 |
c(character) | 字符设备文件,如键盘,支持以character为单位进行线性访问,不支持往前读 |
p(pipeline) | 管道文件 |
s(socke) | socket套接字文件 |
下面分别演示各种文件类型的特性。
普通文件与目录
ll命令打印出来结果的第一列表示文件的类型。
# ll
total 8
drwxr-xr-x. 2 root root 4096 Mar 1 19:01 java
-rw-r--r--. 1 root root 58 Mar 1 17:57 xxx.txt
链接
Linux下链接分为硬链接和软链接。
- 软链接可以理解为windows下的快捷方式,可以让你快速链接到目标文件。
- 硬链接其实底层是同一个文件,只不过inode的links数不为1。
无论是硬链接还是软连接,如果修改任意一方,另外一个文件也会看到这个变化。
软链接的演示
# ln -s xxx.txt yyy.txt
# stat xxx.txt
File: `xxx.txt'
Size: 0 Blocks: 0 IO Block: 4096 regular empty file
Device: fd00h/64768d Inode: 1197701 Links: 1
Access: (0644/-rw-r--r--) Uid: ( 0/ root) Gid: ( 0/ root)
Access: 2021-03-01 19:04:47.324964864 +0800
Modify: 2021-03-01 19:04:47.324964864 +0800
Change: 2021-03-01 19:04:47.324964864 +0800
# stat yyy.txt
File: `yyy.txt' -> `xxx.txt'
Size: 7 Blocks: 0 IO Block: 4096 symbolic link
Device: fd00h/64768d Inode: 1197702 Links: 1
Access: (0777/lrwxrwxrwx) Uid: ( 0/ root) Gid: ( 0/ root)
Access: 2021-03-01 19:05:56.010964909 +0800
Modify: 2021-03-01 19:05:56.010964909 +0800
Change: 2021-03-01 19:05:56.010964909 +0800
# ll
total 0
-rw-r--r--. 1 root root 0 Mar 1 19:04 xxx.txt
lrwxrwxrwx. 1 root root 7 Mar 1 19:05 yyy.txt -> xxx.txt
# rm -rf xxx.txt
文件删除了会标红。
从上面的演示可以发现xxx.txt和yyy.txt的inode号不同,是两个不一样的文件,xxx.txt是源文件,yyy.txt是一个快捷方式,一旦源文件被删除了,那么快捷方式就会报错。
硬链接的演示
# ln test.txt xxx.txt
# ls
test.txt xxx.txt
# stat test.txt
File: `test.txt'
Size: 58 Blocks: 8 IO Block: 4096 regular file
Device: fd00h/64768d Inode: 1197702 Links: 2
Access: (0644/-rw-r--r--) Uid: ( 0/ root) Gid: ( 0/ root)
Access: 2021-03-01 17:57:03.273967638 +0800
Modify: 2021-03-01 17:57:03.273967638 +0800
Change: 2021-03-01 17:57:31.313967620 +0800
# stat xxx.txt
File: `xxx.txt'
Size: 58 Blocks: 8 IO Block: 4096 regular file
Device: fd00h/64768d Inode: 1197702 Links: 2
Access: (0644/-rw-r--r--) Uid: ( 0/ root) Gid: ( 0/ root)
Access: 2021-03-01 17:57:03.273967638 +0800
Modify: 2021-03-01 17:57:03.273967638 +0800
Change: 2021-03-01 17:57:31.313967620 +0800
# rm -rf test.txt
# stat xxx.txt
File: `xxx.txt'
Size: 58 Blocks: 8 IO Block: 4096 regular file
Device: fd00h/64768d Inode: 1197702 Links: 1
Access: (0644/-rw-r--r--) Uid: ( 0/ root) Gid: ( 0/ root)
Access: 2021-03-01 17:57:03.273967638 +0800
Modify: 2021-03-01 17:57:03.273967638 +0800
Change: 2021-03-01 17:59:44.479967355 +0800
从上面的演示可以发现test.txt和xxx.txt其实是同一个inode号,也就是说这两个链接指向的是同一个文件,Links为2表示有两个链接指向它,删除其中一个,links数就会减一。
块设备文件和字符设备文件
# ll /dev
brw-rw----. 1 root disk 8, 0 Nov 16 2019 sda
brw-rw----. 1 root disk 8, 1 Nov 16 2019 sda1
brw-rw----. 1 root disk 8, 2 Nov 16 2019 sda2
... ...
crw-rw-rw-. 1 root tty 5, 0 Nov 16 2019 tty
crw--w----. 1 root tty 4, 0 Nov 16 2019 tty0
crw--w----. 1 root tty 4, 1 Nov 16 2019 tty1
从上面的结果可以看出sda硬盘对应的文件类型为b(块设备文件),tty终端命令行对应的文件类型为c(字符设备文件)。
socket套接字
# exec 7<> /dev/tcp/www.baidu.com/80
# ll /proc/$$/fd
total 0
lrwx------ 1 root root 64 Mar 2 11:12 0 -> /dev/pts/1
lrwx------ 1 root root 64 Mar 2 11:12 1 -> /dev/pts/1
lrwx------ 1 root root 64 Mar 2 11:12 2 -> /dev/pts/1
lrwx------ 1 root root 64 Mar 2 11:14 255 -> /dev/pts/1
lrwx------ 1 root root 64 Mar 2 11:12 7 -> socket:[412905059]
# lsof -op $$
COMMAND PID USER FD TYPE DEVICE OFFSET NODE NAME
bash 57909 root cwd DIR 253,1 100663425 /root
bash 57909 root rtd DIR 253,1 128 /
bash 57909 root txt REG 253,1 67152288 /usr/bin/bash
bash 57909 root mem REG 253,1 102185907 /usr/lib64/libresolv-2.17.so
bash 57909 root mem REG 253,1 100665273 /usr/lib64/libnss_dns-2.17.so
bash 57909 root mem REG 253,1 100665182 /usr/lib/locale/locale-archive
bash 57909 root mem REG 253,1 100665275 /usr/lib64/libnss_files-2.17.so
bash 57909 root mem REG 253,1 100665256 /usr/lib64/libc-2.17.so
bash 57909 root mem REG 253,1 100665262 /usr/lib64/libdl-2.17.so
bash 57909 root mem REG 253,1 100665356 /usr/lib64/libtinfo.so.5.9
bash 57909 root mem REG 253,1 100665250 /usr/lib64/ld-2.17.so
bash 57909 root mem REG 253,1 2712 /usr/lib64/gconv/gconv-modules.cache
bash 57909 root 0u CHR 136,1 0t0 4 /dev/pts/1
bash 57909 root 1u CHR 136,1 0t0 4 /dev/pts/1
bash 57909 root 2u CHR 136,1 0t0 4 /dev/pts/1
bash 57909 root 7u IPv4 412905059 0t0 TCP 10.0.4.149:56877->14.215.177.39:http (ESTABLISHED)
bash 57909 root 255u CHR 136,1 0t0 4 /dev/pts/1
证明一切皆文件实验
用dd命令生成一个大小为100M的空的mydisk.img文件:
# dd if=/dev/zero of=mydisk.img bs=1048576 count=100
100+0 records in
100+0 records out
104857600 bytes (105 MB) copied, 0.0780972 s, 1.3 GB/s
# ll -h
total 100M
-rw-r--r--. 1 root root 100M Mar 1 19:21 mydisk.img
然后将mydisk.img文件挂载到虚拟的环回设备上,挂载之后进行格式化:
# losetup /dev/loop0 mydisk.img
# mke2fs /dev/loop0
mke2fs 1.41.12 (17-May-2010)
Discarding device blocks: done
Filesystem label=
OS type: Linux
Block size=1024 (log=0)
Fragment size=1024 (log=0)
Stride=0 blocks, Stripe width=0 blocks
25688 inodes, 102400 blocks
5120 blocks (5.00%) reserved for the super user
First data block=1
Maximum filesystem blocks=67371008
13 block groups
8192 blocks per group, 8192 fragments per group
1976 inodes per group
Superblock backups stored on blocks:
8193, 24577, 40961, 57345, 73729
Writing inode tables: done
Writing superblocks and filesystem accounting information: done
This filesystem will be automatically checked every 22 mounts or
180 days, whichever comes first. Use tune2fs -c or -i to override.
把新的/dev/loop0
挂载到/mnt/test目录中:
# df -h
Filesystem Size Used Avail Use% Mounted on
/dev/mapper/centos-root 20G 14G 6.4G 68% /
devtmpfs 3.9G 0 3.9G 0% /dev
tmpfs 3.9G 0 3.9G 0% /dev/shm
tmpfs 3.9G 377M 3.5G 10% /run
tmpfs 3.9G 0 3.9G 0% /sys/fs/cgroup
/dev/mapper/centos-home 53G 2.4G 50G 5% /home
/dev/sda1 497M 98M 399M 20% /boot
tmpfs 782M 0 782M 0% /run/user/0
/dev/loop0 97M 1.6M 89M 2% /mnt/test
现在/mnt/test是空的,我们希望用子目录模仿根目录里面的目录结构,以及程序的摆放位置。
- 找到bash所在位置,拷贝过来:
# which bash
/bin/bash
# whereis bash
bash: /bin/bash /usr/share/man/man1/bash.1.gz
# mkdir /mnt/test/bin
# cp /bin/bash /mnt/test/bin/
- 使用ldd分析bash命令所需的动态链接库:
# ldd /bin/bash
linux-vdso.so.1 => (0x00007fffc878b000)
libtinfo.so.5 => /lib64/libtinfo.so.5 (0x00007f1b5b565000)
libdl.so.2 => /lib64/libdl.so.2 (0x00007f1b5b361000)
libc.so.6 => /lib64/libc.so.6 (0x00007f1b5af92000)
/lib64/ld-linux-x86-64.so.2 (0x00007f1b5b797000)
- 将bash需要动态链接的文件,也拷贝过来
# mkdir /mnt/test/lib64
# cp /lib64/{libtinfo.so.5,libdl.so.2,libc.so.6,ld-linux-x86-64.so.2} /mnt/test/lib64
# chroot /mnt/test/
bash-4.2# echo $$
59537
bash-4.2# ls
bash: ls: command not found
bash-4.2# echo 'hello' > /abc.txt
bash-4.2# exit
exit
# ll
total 15
-rw-r--r-- 1 root root 6 Mar 2 11:27 abc.txt
drwxr-xr-x 2 root root 1024 Mar 2 11:24 bin
drwxr-xr-x 2 root root 1024 Mar 2 11:26 lib64
drwx------ 2 root root 12288 Mar 2 11:21 lost+found
可以看到abc.txt被输出到新的根目录下。
输入输出与重定向
<表示输入 >表示输出。
ls 命令的标准输出:ls ./ 1> ls.out
将ls的标准输出1重定向到ls.out。
# ls ./ xxx 1>out
ls: cannot access xxx: No such file or directory
# cat out
./:
ls.out
mydisk.img
ooxx.txt
out
可以发现标准输出重定向到了out,而标准错误还是打印在了命令行上。
cat 命令的标准输入、标准输出:cat 0< test.txt 1> cat.out
将cat的标准输入重定向为 test.txt , 将其标准输出重定向为cat.out。
read命令的标准输入、标准输出:
# read a 0<out
# echo $a
./:
重定向操作符< >的对接:
让两个流写到不同的文件中去:
# ls ./ xxx 1>s.out 2>e.out
# cat s.out
./:
e.out
ls.out
mydisk.img
ooxx.txt
out
s.out
# cat e.out
ls: cannot access xxx: No such file or directory
让两个流写到相同的文件中去:
# ls ./ xxx 1>s.out 2>&1
# cat s.out
ls: cannot access xxx: No such file or directory
./:
e.out
ls.out
mydisk.img
ooxx.txt
out
s.out
注意1>s.out和2>&1
的顺序不能反过来,另外1前面要加&,否则会以为1是一个普通的文件,而不是文件描述符。
管道命令|
管道命令可以将前一个命令的输出作为后一个命令的输入:
head -8 test.txt | tail -1
例如上面的命令就可以只输出文本test.txt中第8行的信息。
进程之间的父子关系
如何查看进程间的父子关系:
# bash
# bash
# pstree
systemd─┬─ECAgent───2*[{ECAgent}]
├─EasyMonitor
├─ManagementAgent───2*[{ManagementAgent}]
├─NetworkManager───3*[{NetworkManager}]
├─VGAuthService
├─agetty
├─auditd───{auditd}
├─avahi-daemon───avahi-daemon
├─chronyd
├─crond
├─dbus-daemon
├─iprdump
├─iprinit
├─iprupdate
├─irqbalance
├─lvmetad
├─master─┬─pickup
│ └─qmgr
├─nginx───2*[nginx]
├─polkitd───5*[{polkitd}]
├─rsyslogd───2*[{rsyslogd}]
├─sshd─┬─sshd───bash───bash───bash───pstree
│ └─sshd───bash
├─systemd-journal
├─systemd-logind
├─systemd-udevd
├─tuned───4*[{tuned}]
└─vmtoolsd───{vmtoolsd}
使用exit
,就会回到父进程。
另外ps命令也能看到父进程的进程号(第三列):
# ps -ef | grep $$
root 61965 61936 0 11:48 pts/1 00:00:00 bash
root 62293 61965 0 11:51 pts/1 00:00:00 ps -ef
root 62294 61965 0 11:51 pts/1 00:00:00 grep --color=auto 61965
关于变量
父进程定义的变量x,不能在子进程取到,除非使用export,这也是为什么在/etc/profile/配环境变量的时候,要添加export的原因。
[root@localhost ~]# echo $$
109743
[root@localhost ~]# x=100
[root@localhost ~]# echo $x
100
[root@localhost ~]# bash
[root@localhost ~]# echo $x
[root@localhost ~]# exit
exit
[root@localhost ~]# export x
[root@localhost ~]# bash
[root@localhost ~]# echo $x
100
关于代码块、管道开启的子进程
在一个花括号中,所有的指令在同一个进程中执行。
bash是解释执行的,如果看到管道,会将管道左侧的命令独立开启一个子进程,管道右侧的命令独立开启一个子进程,用管道进行对接。而进程的隔离级别很高,所以在新开启的进程中对变量a的修改,不会影响父进程a的值。
[root@localhost ~]# echo $$
109879
[root@localhost ~]# a=1
[root@localhost ~]# echo $a
1
[root@localhost ~]# { a=100 ; echo 'hello'; } | cat
hello
[root@localhost ~]# echo $a
1
[root@localhost ~]#
$BASHPID的优先级低于管道|:
[root@localhost ~]# echo $$ | cat
109879
[root@localhost ~]# echo $BASHPID | cat
110543
下面我们使用阻塞命令来观察管道:
[root@localhost ~]# { echo $BASHPID ; read x; } | { cat ; echo $BASHPID ; read y; }
看一下进程号,可以发现管道两侧开启的是独立的子进程:
[root@localhost ~]# ps -ef|grep 109879
root 109879 109743 0 19:11 pts/1 00:00:00 bash
root 111091 109879 0 19:22 pts/1 00:00:00 bash
root 111092 109879 0 19:22 pts/1 00:00:00 bash
下面可以看出管道左侧的进程和右侧的进程通过管道被对接起来:
[root@localhost ~]# ll /proc/111091/fd
total 0
lrwx------ 1 root root 64 Mar 2 19:23 0 -> /dev/pts/1
l-wx------ 1 root root 64 Mar 2 19:23 1 -> pipe:[413130084]
lrwx------ 1 root root 64 Mar 2 19:22 2 -> /dev/pts/1
lrwx------ 1 root root 64 Mar 2 19:23 255 -> /dev/pts/1
[root@localhost ~]# ll /proc/111092/fd
total 0
lr-x------ 1 root root 64 Mar 2 19:23 0 -> pipe:[413130084]
lrwx------ 1 root root 64 Mar 2 19:23 1 -> /dev/pts/1
lrwx------ 1 root root 64 Mar 2 19:22 2 -> /dev/pts/1
lrwx------ 1 root root 64 Mar 2 19:23 255 -> /dev/pts/1
也可以使用lsofl来查看子进程中正在开启的管道:
[root@localhost ~]# lsof -op 111091
COMMAND PID USER FD TYPE DEVICE OFFSET NODE NAME
bash 111091 root cwd DIR 253,1 100663425 /root
bash 111091 root rtd DIR 253,1 128 /
bash 111091 root txt REG 253,1 67152288 /usr/bin/bash
bash 111091 root mem REG 253,1 100665275 /usr/lib64/libnss_files-2.17.so
bash 111091 root mem REG 253,1 100665182 /usr/lib/locale/locale-archive
bash 111091 root mem REG 253,1 100665256 /usr/lib64/libc-2.17.so
bash 111091 root mem REG 253,1 100665262 /usr/lib64/libdl-2.17.so
bash 111091 root mem REG 253,1 100665356 /usr/lib64/libtinfo.so.5.9
bash 111091 root mem REG 253,1 100665250 /usr/lib64/ld-2.17.so
bash 111091 root mem REG 253,1 2712 /usr/lib64/gconv/gconv-modules.cache
bash 111091 root 0u CHR 136,1 0t0 4 /dev/pts/1
bash 111091 root 1w FIFO 0,8 0t0 413130084 pipe
bash 111091 root 2u CHR 136,1 0t0 4 /dev/pts/1
bash 111091 root 255u CHR 136,1 0t0 4 /dev/pts/1
[root@localhost ~]# lsof -op 111092
COMMAND PID USER FD TYPE DEVICE OFFSET NODE NAME
bash 111092 root cwd DIR 253,1 100663425 /root
bash 111092 root rtd DIR 253,1 128 /
bash 111092 root txt REG 253,1 67152288 /usr/bin/bash
bash 111092 root mem REG 253,1 100665275 /usr/lib64/libnss_files-2.17.so
bash 111092 root mem REG 253,1 100665182 /usr/lib/locale/locale-archive
bash 111092 root mem REG 253,1 100665256 /usr/lib64/libc-2.17.so
bash 111092 root mem REG 253,1 100665262 /usr/lib64/libdl-2.17.so
bash 111092 root mem REG 253,1 100665356 /usr/lib64/libtinfo.so.5.9
bash 111092 root mem REG 253,1 100665250 /usr/lib64/ld-2.17.so
bash 111092 root mem REG 253,1 2712 /usr/lib64/gconv/gconv-modules.cache
bash 111092 root 0r FIFO 0,8 0t0 413130084 pipe
bash 111092 root 1u CHR 136,1 0t0 4 /dev/pts/1
bash 111092 root 2u CHR 136,1 0t0 4 /dev/pts/1
bash 111092 root 255u CHR 136,1 0t0 4 /dev/pts/1