原视频地址:www.youtube.com/watch?v=Ftg…
本文内容概览:
- 文件描述符
- 操作系统基础
- 文件描述符和进程
- 网络和 sockets
文件描述符
相信你对"On Unix, Everything is a file"已经耳熟能详了,比如/dev
目录、/proc
目录等,你几乎可以通过操作文件的方式获取系统所有能够获取的data。而和文件进行交互,就需要用到文件描述符(file descriptors,简称 fd,就是一个数字,在某个进程内唯一)。
通过文件你能完成这些事。
形象点来说,文件描述符就像是一张门票,每次你需要完成某种操作的时候,就把这张门票给操作系统,然后操作系统为你完成你想要做的。
有几个系统定义的标准文件描述符:
- 0:标准输入,对应 Python 的
sys.stdin
- 1:标准输出,对应
sys.stdout
- 2:标准错误输出,对应
sys.stderr
然后我们通过几个例子来巩固一下:
with open('example.txt') as f: print(f.read())
上面这个程序用到了两个 fd,一个是 open 函数创建的,另一个是 print 的时候用到了标准输出的 fd,也就是1。
你还能根据 fd 修改文件的权限:
with open('example.txt') as f: stat = os.stat(f.fileno()) os.chmod(f.fileno(), 0o640)
甚至还能控制硬件:下面的这段代码可以用来弹出 CDROM。
操作系统基础
在黑暗的中世纪。。。应用程序是可以直接操控硬件的,这意味着程序如果crash 了,会把整个系统搞垮,然后就需要重启电脑。
现在多亏了操作系统这一环,实现了隔离效果,应用程序将不能直接访问硬件,必须通过系统调用的方式向操作系统申请对硬件资源的访问。操作系统有一套权限管理机制,可以选择不接受你的请求。
syscall
每次 syscall,都会从 user space 转到 kernel space,运行完了再转回 user space。这意味着需要进行 context switch,所以速度相对而言是较低的(需要的时钟周期数为百数量级)。
回到之前的那个例子:
# read.py with open('example.txt') as f: print(f.read())
有一个工具可以查看某个程序调用了syscall:
$ strace ./read.py
open file table 和 fd table
open file table 是一个全局的,所有进程打开的文件都在这里。每个进程有一个自己的 fd table,实际上也是指向了 open file table:
同一个进程第二次打开同一个文件,fd 不一样。
fd table的内容很简单,实际的详细数据保存在open file table。
fork()
fork 创建一个子进程,子进程会继承父进程的 fd,同时以copy opn write
的方式基础父进程的内存,事实上可以看作是内容和父进程完全一致。继承 fd 意味着:
pid = os.fork()
之后,子进程继承了父进程的文件描述符:上面的 f
,他们指向的是 open file table中同一个文件,这意味着他们的 offset 都是一样的:
exec()
替换当前程序的内存,fd 也是会被继承的,除非设置了cloexec
。子进程继承父进程的 fd 是存在一定问题的,这实际上相等于是把父进程的 fd 泄露给了子进程,会带来一定的安全隐患。所以有人起草了pwp446提案,提供了一个关闭父进程 fds 的选项:subprocess
模块有一个close_fds
参数,设置为True
就表示调用exec
的时候,把除了0,1,2
三个 fds 之外的 fds 全部关闭掉。详细信息可以看看pep446
原文和subprocess的官方文档。
网络和 sockets
要进行网络通信,需要一对 network socket,一个运行在服务器,一个在客户端。服务器端的 socket进程一般绑定在特定的端口,比如80,3306, 5432等等,它会一直监听在这个端口,监听到请求之后触发相应的逻辑,。最后会把结果返回给客户端。客户端的 socket 一般会临时绑定一个高段位的端口,请求结束之后(也就是进程结束)就会取消榜单,释放珍贵的端口资源。
Python 中的socket
模块,主要需要指定两个参数:选择 IPV4还是 IPV6,使用 UDP 还是 TCP?一个简单的 server 示例:
client 示例:
unix sockets
unix sockets 和 network sockets 其实差不多,只不过只能让同一台 machine 上的进程进行通信。UNIX domain socket的一方知道对方进程在一台机器上,所以可以不用诸如检验措施、路由操作等操作,所以相对而言更加快和轻量级。
在 Python 中是这样使用的:bind
和connect
的现在不是 IP 地址和端口号了,而是文件。因为是文件,所以可以设置读写权限! 比如你可以限制某个特定组的用户才能从这个 socket 里面读数据。
如果你像我一样真正热爱计算机科学,喜欢研究底层逻辑,欢迎关注我的微信公众号:
版权声明:
本文来源网络,所有图片文章版权属于原作者,如有侵权,联系删除。
本文网址:https://www.mushiming.com/mjyfx/16077.html