在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开始的,所以在不同的进程中你会看到相同的文件描述符,这种情况下相同文件描述符有可能指向同一个文件,也有可能指向不同的文件。当程序打开一个现有文件或者创建一个新文件时,内核向进程返回一个文件描述符。

每一个进程都会有下面三个文件描述符:

  1. 0:标准输入(standard input)
  2. 1:标准输出(standard output)
  3. 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

下面通过一个例子来模拟文件的读与写:

  1. 创建一个文件描述符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。
  1. 使用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\n14个字符,offset的位置变成了14。
  1. 新开一个标签页,用一个新的文件描述符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

文件删除了会标红。

Linux虚拟文件系统、文件描述符、管道_内核

从上面的演示可以发现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是空的,我们希望用子目录模仿根目录里面的目录结构,以及程序的摆放位置。

  1. 找到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/
  1. 使用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)
  1. 将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 ~]#

$Linux虚拟文件系统、文件描述符、管道_内核_02BASHPID的优先级低于管道|:

[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