Linux内核101:fd,syscall,socket

原视频地址: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 中是这样使用的:bindconnect的现在不是 IP 地址和端口号了,而是文件。因为是文件,所以可以设置读写权限! 比如你可以限制某个特定组的用户才能从这个 socket 里面读数据。

如果你像我一样真正热爱计算机科学,喜欢研究底层逻辑,欢迎关注我的微信公众号:

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值