聽說linux下一切可執行程序是二進位文件?

win的可執行程序不也是二進位文件嗎?


Linux的一切皆文件是指,Linux世界中的所有、任意、一切東西都可以通過文件的方式訪問、管理。

反過來說,是Linux和GNU世界定的規範,任何東西都掛在文件系統之上,即使它們不是文件,也以文件的形式來呈現。

有一個很關鍵的點,這個一切是單向的,也即所有的東西都單向通過文件系統呈現,反向不一定可行。比如通過新建文件的方式來創建磁碟設備是行不通的。

比如我們經常會講的進程(/proc)、設備(/dev)、Socket等等,實際上都不是文件,但是你可以以文件系統的規範來訪問它,修改屬主和屬性。

Linux下有lsof命令,可以查看所有已經打開的文件,你使用lsof -p [pid]的方式就可以查看對應的進程都打開了什麼文件,而其中的type欄位就是表明它是什麼類型,通過man losf 命令你可以查看到它有下面這麼多種。

TYPE is the type of the node associated with the file - e.g., GDIR, GREG, VDIR, VREG, etc.
or ``IPv4 for an IPv4 socket;
or ``IPv6 for an open IPv6 network file - even if its address is IPv4, mapped in an IPv6 address;
or ``ax25 for a Linux AX.25 socket;
or ``inet for an Internet domain socket;
or ``lla for a HP-UX link level access file;
or ``rte for an AF_ROUTE socket;
or ``sock for a socket of unknown domain;
or ``unix for a UNIX domain socket;
or ``x.25 for an HP-UX x.25 socket;
or ``BLK for a block special file;
or ``CHR for a character special file;
or ``DEL for a Linux map file that has been deleted;
or ``DIR for a directory;
or ``DOOR for a VDOOR file;
or ``FIFO for a FIFO special file;
or ``KQUEUE for a BSD style kernel event queue file;
or ``LINK for a symbolic link file;
or ``MPB for a multiplexed block file;
or ``MPC for a multiplexed character file;
or ``NOFD for a Linux /proc/&/fd directory that cant be opened -- the directory path appears in the NAME column, followed by an error message;
or ``PAS for a /proc/as file;
or ``PAXV for a /proc/auxv file;
or ``PCRE for a /proc/cred file;
or ``PCTL for a /proc control file;
or ``PCUR for the current /proc process;
or ``PCWD for a /proc current working directory;
or ``PDIR for a /proc directory;
or ``PETY for a /proc executable type (etype);
or ``PFD for a /proc file descriptor;
or ``PFDR for a /proc file descriptor directory;
or ``PFIL for an executable /proc file;
or ``PFPR for a /proc FP register set;
or ``PGD for a /proc/pagedata file;
or ``PGID for a /proc group notifier file;
or ``PIPE for pipes;
or ``PLC for a /proc/lwpctl file;
or ``PLDR for a /proc/lpw directory;
or ``PLDT for a /proc/ldt file;
or ``PLPI for a /proc/lpsinfo file;
or ``PLST for a /proc/lstatus file;
or ``PLU for a /proc/lusage file;
or ``PLWG for a /proc/gwindows file;
or ``PLWI for a /proc/lwpsinfo file;
or ``PLWS for a /proc/lwpstatus file;
or ``PLWU for a /proc/lwpusage file;
or ``PLWX for a /proc/xregs file;
or ``PMAP for a /proc map file (map);
or ``PMEM for a /proc memory image file;
or ``PNTF for a /proc process notifier file;
or ``POBJ for a /proc/object file;
or ``PODR for a /proc/object directory;
or ``POLP for an old format /proc light weight process file;
or ``POPF for an old format /proc PID file;
or ``POPG for an old format /proc page data file;
or ``PORT for a SYSV named pipe;
or ``PREG for a /proc register file;
or ``PRMP for a /proc/rmap file;
or ``PRTD for a /proc root directory;
or ``PSGA for a /proc/sigact file;
or ``PSIN for a /proc/psinfo file;
or ``PSTA for a /proc status file;
or ``PSXSEM for a POSIX semaphore file;
or ``PSXSHM for a POSIX shared memory file;
or ``PTS for a /dev/pts file;
or ``PUSG for a /proc/usage file;
or ``PW for a /proc/watch file;
or ``PXMP for a /proc/xmap file;
or ``REG for a regular file;
or ``SMT for a shared memory transport file;
or ``STSO for a stream socket;
or ``UNNM for an unnamed type file;
or ``XNAM for an OpenServer Xenix special file of unknown type;
or ``XSEM for an OpenServer Xenix semaphore file;
or ``XSD for an OpenServer Xenix shared data file;
or the four type number octets if the corresponding name isnt known.

應評論中的同學要求,添加一些示例。

# 進到proc目錄
[root@k8s ~]# cd /proc
[root@k8s proc]# ls
1 1063 1077 11 12 1504 19 2175 24 280 297 301 317 39 407 413 49 519 726 737 bus devices filesystems kallsyms kpageflags modules partitions softirqs timer_list vmstat
10 1066 1078 1100 13 16 2 2176 25 281 298 302 36 391 408 414 495 52 729 743 cgroups diskstats fs kcore loadavg mounts sched_debug stat timer_stats zoneinfo
102 1068 1083 1102 14 1746 20 2177 26 282 299 303 37 392 409 415 5 53 733 8 cmdline dma interrupts keys locks mpt schedstat swaps tty
1038 1071 1085 1110 15 1748 2005 2178 27 29 3 304 38 4 410 416 50 6 734 9 consoles driver iomem key-users mdstat mtrr scsi sys uptime
1061 1072 1097 1113 1502 1749 2007 22 279 294 30 305 380 405 411 47 51 66 735 acpi cpuinfo execdomains ioports kmsg meminfo net self sysrq-trigger version
1062 1073 1099 1164 1503 18 21 23 28 296 300 31 381 406 412 48 511 7 736 buddyinfo crypto fb irq kpagecount misc pagetypeinfo slabinfo sysvipc vmallocinfo

# 可以看到上面有很多以數字為名稱的目錄,而這些目錄就對應每一個進程
# 使用ps -ef|grep nginx可以找到Nginx主進程的PID是2175
[root@k8s proc]# ps -ef|grep nginx
root 2175 1 0 10:23 ? 00:00:00 nginx: master process /usr/sbin/nginx
nginx 2176 2175 0 10:23 ? 00:00:00 nginx: worker process
nginx 2177 2175 0 10:23 ? 00:00:00 nginx: worker process
root 2182 2007 0 10:24 pts/0 00:00:00 grep --color=auto nginx

# 進入/proc/2175目錄,可以看到這個進程相關的目錄
[root@k8s 2175]# ls
attr cgroup comm cwd fd io map_files mountinfo net oom_adj pagemap projid_map schedstat smaps statm task wchan
autogroup clear_refs coredump_filter environ fdinfo limits maps mounts ns oom_score patch_state root sessionid stack status timers
auxv cmdline cpuset exe gid_map loginuid mem mountstats numa_maps oom_score_adj personality sched setgroups stat syscall uid_map
# 這裡面的內容就是2175進程的全部內容了,非常多的命令實際上都是從這個目錄讀取的信息,比如上面的ps命令

# 上面的文件我舉幾個例子解釋一下
# 1. fd目錄,裡面是此進程打開的文件的情況,每個均鏈接至實際的文件或設備
[root@k8s 2175]# ll fd
總用量 0
lrwx------ 1 root root 64 9月 29 10:53 0 -&> /dev/null
lrwx------ 1 root root 64 9月 29 10:53 1 -&> /dev/null
lrwx------ 1 root root 64 9月 29 10:53 10 -&> socket:[26530]
l-wx------ 1 root root 64 9月 29 10:53 2 -&> /var/log/nginx/error.log
lrwx------ 1 root root 64 9月 29 10:53 3 -&> socket:[26527]
l-wx------ 1 root root 64 9月 29 10:53 4 -&> /var/log/nginx/error.log
l-wx------ 1 root root 64 9月 29 10:53 5 -&> /var/log/nginx/access.log
lrwx------ 1 root root 64 9月 29 10:53 6 -&> socket:[27700]
lrwx------ 1 root root 64 9月 29 10:53 7 -&> socket:[27701]
lrwx------ 1 root root 64 9月 29 10:53 8 -&> socket:[26528]
lrwx------ 1 root root 64 9月 29 10:53 9 -&> socket:[26529]

# 2. limits,內容是此進程的系統限制信息
[root@k8s 2175]# cat limits
Limit Soft Limit Hard Limit Units
Max cpu time unlimited unlimited seconds
Max file size unlimited unlimited bytes
Max data size unlimited unlimited bytes
Max stack size 8388608 unlimited bytes
Max core file size 0 unlimited bytes
Max resident set unlimited unlimited bytes
Max processes 14889 14889 processes
Max open files 1024 4096 files
Max locked memory 65536 65536 bytes
Max address space unlimited unlimited bytes
Max file locks unlimited unlimited locks
Max pending signals 14889 14889 signals
Max msgqueue size 819200 819200 bytes
Max nice priority 0 0
Max realtime priority 0 0
Max realtime timeout unlimited unlimited us

# 3. stack,此進程的內核調用棧信息
[root@k8s 2175]# cat stack
[&] sigsuspend+0x39/0x70
[&] SyS_rt_sigsuspend+0x5e/0x80
[&] system_call_fastpath+0x25/0x2a
[&] 0xffffffffffffffff

# 這些內容都不是普通的文件,你通過文件系統看,它們的大小都是0,但你看我能輸出上面的這麼多信息
[root@k8s 2175]# ll
總用量 0
dr-xr-xr-x 2 root root 0 9月 29 10:25 attr
-rw-r--r-- 1 root root 0 9月 29 10:25 autogroup
-r-------- 1 root root 0 9月 29 10:25 auxv
-r--r--r-- 1 root root 0 9月 29 10:23 cgroup
--w------- 1 root root 0 9月 29 10:25 clear_refs
-r--r--r-- 1 root root 0 9月 29 10:24 cmdline
-rw-r--r-- 1 root root 0 9月 29 10:25 comm
-rw-r--r-- 1 root root 0 9月 29 10:25 coredump_filter
-r--r--r-- 1 root root 0 9月 29 10:25 cpuset
...
[root@k8s 2175]# du -sh
0

另外,我們經常會使用到的/dev/null, /dev/random,都是可以在文件目錄中看到,但它們都不是普通的文件,而是Linux Kernel為了實現某些功能,同時存在everything is a file的約定,才把它們體現在了文件系統上。

2020.9.30 另外補一個我覺得挺有用的,且應該不少人不知道的一個小技巧

man 5 proc命令會輸出/proc目錄的幫助信息,它裡面包含/proc/[pid]目錄中每個目錄和文件的說明信息

PROC(5) Linux Programmers Manual PROC(5)

NAME
proc - process information pseudo-file system

DESCRIPTION
The proc file system is a pseudo-file system which is used as an interface to kernel data structures. It is commonly mounted at /proc. Most of it is read-only, but some files allow kernel variables to be changed.

The following outline gives a quick tour through the /proc hierarchy.

/proc/[pid]
There is a numerical subdirectory for each running process; the subdirectory is named by the process ID. Each such subdirectory contains the following pseudo-files and directories.

/proc/[pid]/auxv (since 2.6.0-test7)
This contains the contents of the ELF interpreter information passed to the process at exec time. The format is one unsigned long ID plus one unsigned long value for each entry. The last entry contains two zeros.

/proc/[pid]/cgroup (since Linux 2.6.24)
This file describes control groups to which the process/task belongs. For each cgroup hierarchy there is one entry containing colon-separated fields of the form:

5:cpuacct,cpu,cpuset:/daemons

The colon-separated fields are, from left to right:

1. hierarchy ID number

2. set of subsystems bound to the hierarchy

3. control group in the hierarchy to which the process belongs

This file is present only if the CONFIG_CGROUPS kernel configuration option is enabled.

/proc/[pid]/cmdline
This holds the complete command line for the process, unless the process is a zombie. In the latter case, there is nothing in this file: that is, a read on this file will return 0 characters. The command-line arguments appear in this file as a set
of strings separated by null bytes ( ), with a further null byte after the last string.

/proc/[pid]/coredump_filter (since kernel 2.6.23)
See core(5).

/proc/[pid]/cpuset (since kernel 2.6.12)
See cpuset(7).

/proc/[pid]/cwd
This is a symbolic link to the current working directory of the process. To find out the current working directory of process 20, for instance, you can do this:

$ cd /proc/20/cwd; /bin/pwd

Note that the pwd command is often a shell built-in, and might not work properly. In bash(1), you may use pwd -P.

In a multithreaded process, the contents of this symbolic link are not available if the main thread has already terminated (typically by calling pthread_exit(3)).


其實是一種面向對象的設計思想。

就是說將一切外設當做文件,從而可以使用針對文件的那些操作。

串口是文件,內存是文件,usb是文件,進程信息是文件,網卡是文件,建立的每個網路通訊都是文件,藍牙設備也是文件,等等等等。

所有外設都是文件,本質上就是說他們都支持用來訪問文件的那些介面,可以被當做文件來訪問。這個原理與子類都能當做基類訪問是一樣的,就是操作系統層面的oop思想。


Linux 系統把硬體映射成了設備文件。

例如攝像頭 /dev/video0 怎麼操作這個攝像頭呢?思路是這樣的。

用C裡面的 open() 函數,open(/dev/video0) 就可以連接這個設備,然後通過 read() 就可以讀出一幀一幀的圖像。然後讀出的圖像矩陣保存成 BMP/JPG/PNG 文件,就可以查看了。

在舉例音效卡。open()打開音效卡設備, read() 就是錄音, write() 就是播放。


「一切皆文件「其實不是linux專屬的,這是一種設計理念。在linux中大家都在強調這一點,可能是這麼說顯得自己很高端。。。

「一切皆文件「在可執行文件上可能表現不太明顯,但是在一些奇奇怪怪(誤)的東西上就顯得很清晰了,比如:

我給電腦裝了1T的硬碟,對於系統來說,這不是一個硬碟,這是一個文件;

我給電腦裝了個賊拉風的機械鍵盤,對於系統來說,這不是一個鍵盤,這是一個文件;

我給電腦裝了個騷粉色的滑鼠,對於系統來說,這不是一個粉色滑鼠,這是一個文件。。

嗯,我500+的機械鍵盤在linux上是沒有姓名的

這麼搞有什麼用呢?省心

拿滑鼠來舉例子:

我們平常用的滑鼠,都是雙擊確定,但是突然出現了一個滑鼠,不按常理出牌。。它雙擊表示取消如果沒有「一切皆文件」,那 linux 就會很迷惑,為啥別的滑鼠都是雙擊表示確定,就你是取消呢然後沒辦法,就只能再給這個滑鼠一個單獨的類型,叫——「雙擊表示取消的奇怪滑鼠」然後每次都要先判斷,這是一個正常滑鼠,還是一個奇怪滑鼠。。當各種各樣的東西多起來以後,程序沒別的事了,1w行代碼里9k行是判斷類型的。。所以,linux決定,你雖然是一個滑鼠,但你不能再當一個滑鼠了,因為你可太操心了。

具體操作是啥呢?

我linux不管你滑鼠是雙擊確定,還是單擊確定,還是雙擊取消反正,我收到1就當你是確定,我收到2就當你是取消,我收到83251我就當你是在開玩笑。。

linux不管這到底是什麼高貴的設備,我就當是個文件,硬體驅動到位了我就用,硬體驅動不到位我就不用。

其實不光是硬體設備,系統的進程啦,代碼啦在linux里都是一個文件,linux改了對應的文件,就改了這個設備/進程/代碼,而不用操心是怎麼改的。

就問你省心不省心。

所以,「一切皆文件」=「一切皆玩意,這個玩意在linux里叫文件」

所以題主你說,windows可執行文件是二進位文件,沒錯。因為「一切皆文件」跟「文件」其實沒啥關係。

這個就是個思想,linux把這個思想用「文件系統」實現了。


linux一切皆文件,是基於Linux 進程抽象的定義,實際上對應內核態裡面的一個索引,根據每個文件類型,有可以抽象出以下結構體,感興趣看內核驅動

struct file_operations {
struct module *owner;
loff_t (*llseek) (struct file *, loff_t, int);
ssize_t (*read) (struct file *, char __user *, size_t, loff_t *);
ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *);
ssize_t (*aio_read) (struct kiocb *, const struct iovec *, unsigned long, loff_t);
ssize_t (*aio_write) (struct kiocb *, const struct iovec *, unsigned long, loff_t);
int (*readdir) (struct file *, void *, filldir_t);
int (*iterate) (struct file *, struct dir_context *);
unsigned int (*poll) (struct file *, struct poll_table_struct *);
long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long);
long (*compat_ioctl) (struct file *, unsigned int, unsigned long);
int (*mmap) (struct file *, struct vm_area_struct *);
int (*open) (struct inode *, struct file *);
int (*flush) (struct file *, fl_owner_t id);
int (*release) (struct inode *, struct file *);
int (*fsync) (struct file *, loff_t, loff_t, int datasync);
int (*aio_fsync) (struct kiocb *, int datasync);
int (*fasync) (int, struct file *, int);
int (*lock) (struct file *, int, struct file_lock *);
ssize_t (*sendpage) (struct file *, struct page *, int, size_t, loff_t *, int);
unsigned long (*get_unmapped_area)(struct file *, unsigned long, unsigned long, unsigned long, unsigned long);
int (*check_flags)(int);
int (*flock) (struct file *, int, struct file_lock *);
ssize_t (*splice_write)(struct pipe_inode_info *, struct file *, loff_t *, size_t, unsigned int);
ssize_t (*splice_read)(struct file *, loff_t *, struct pipe_inode_info *, size_t, unsigned int);
int (*setlease)(struct file *, long, struct file_lock **);
long (*fallocate)(struct file *file, int mode, loff_t offset,
loff_t len);
int (*show_fdinfo)(struct seq_file *m, struct file *f);
};

當我們需要打開一個文件,會調用open,open這個函數是一個系統調用,觸發svc中斷,切換到內核態,內核處理真正的打開文件的操作,實際上是抽象成為一個結構體,結構體存放在內核buf中,返回fd,實際上是buf的對應位置,內核態處理完成後返回fd,用於下次系統調用到內核態的參數使用。

一個進程最多可以打開多少個文件? 答案是,1024,這取決於內核態的實現。


推薦閱讀:
相关文章