Linux 概念随记

句柄

知乎回答

作者:黄兢成
链接:https://www.zhihu.com/question/27656256/answer/943130123
来源:知乎
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

句柄的英文是 handle。在英文中,有操作、处理、控制之类的意义。作为一个名词时,是指某个中间媒介,通过这个中间媒介可控制、操作某样东西。

这样说有点抽象,举个例子。door handle 是指门把手,通过门把手可以去控制门,但 door handle 并非 door 本身,只是一个中间媒介。又比如 knife handle 是刀柄,通过刀柄可以使用刀。

跟 door handle 类似,我们可以用 file handle 去操作 file, 但 file handle 并非 file 本身。这个 file handle 就被翻译成文件句柄,同理还有各种资源句柄。

计算机领域很多英文词,直接从日常词中引申而来。比如 fork,日常用词就是个叉子,在 unix 中引申成创建新进程(进程分叉了)。socket 日常用词是插座(连起来用于通电),引申成联网的标记信息(连起来用于通信)。英文是很日常,很容易理解的词,有时翻译成中文反而难以理解了。

---------------------------------

句柄这个翻译有点奇怪。据维基百科,句柄 的条目。

David Gries所著的《Compiler Construction for Digital Computer》(1971)有句话

A handle of any sentential form is a leftmost simple phrase.

该书中译本,《数字计算机的编译程序构造》(仲萃豪译, 1976 版)翻译成

任一句型的句柄就是此句型的最左简单短语。

这可能是句柄一词最早的出处。

这里确实是在讨论句子。在这里句柄是个意译的合成词,两个字分拆开,“句柄”中的“柄”,用法就类似于,“刀柄”中的“柄”。用在此处是适当的。

但以后将各种资源 handle, 都翻译成句柄时,就有点滥用了。

---------------------------------

具体到代码实现,handle 通常是某个数字标记,通过标记操作资源。这个标记在不同的场合有不同的叫法,有时叫 ID,有时叫描述符(descriptor)。在 Windows 平台,就叫各种 handle 了。

不要将 handle 简单地理解成编号、索引。比如分配 16 位的索引,再用 8 位密码将 16 位索引加密。之后将 4 位类型、4 位权限、8 位密码、16 位加密索引打包成一个 32 位的整数作为 handle。这时说这个 handle 是索引就有点不适当了。

用 handle 如何操作真正的资源,是实现的细节。handle 通常被实现为整数,也可以被实现成其他类型。

广义来说,指针也是某种 handle,可以操作对象。但实际语境中,指针跟句柄是有区别的。初次接触到 handle (或者 id),很多人会有迷惑,为什么要用 handle,而不直接用指针呢?

  1. 指针作用太强,可做的事情太多。可做的事情越多,就会越危险。接口设计中,功能刚刚好就够了,并非越多权限越好的。
  2. handle 通常只是个整数,实现被隐藏起来,假如直接暴露了指针,也就暴露了指针类型(有时也可以暴露 void* 指针作为某种 handle)。用户看到越多细节,其代码就越有可能依赖这些细节。将来情况有变,但又要兼容用户代码,库内部改起来就更麻烦。
  3. 资源在内部管理,通过 handle 作为中间层,可以有效判断 handle 是否合法,也可以通过权限检查防止某种危险操作。
  4. handle 通常只是个整数,所有的语言都有整数这种类型,但并非所有语言都有指针。接口只出现整数,方便同一实现绑定到各种语言。

知乎回答:方便操作系统整理碎片

我从Stanford Programming Paradigm这个在线课程看来的思路。网易公开课上也有。

简单说,Handle就是指针的指针。

在c语言里面,假如你通过malloc申请一段内存空间,heap给你一个指针直接指到你可用内存的起始位置。在你释放之前操作系统无法将这段内存移动。

至于为啥要移动,因为你在程序各处调用大小不一的malloc会产生内存碎片。碎片多了总之不好,最好把碎片移到一块,这样就又有大块的内存空间可以malloc了。这个过程叫defragmentation。跟windows上硬盘碎片整理一样。

malloc直接给你指针的话就不方便heap管理内存。因为只要你不free,它就不敢动那块内存。但是假如给你指针的指针,增加一次跳转。动态内存管理内部维护一个表,第一次对指针dereference进入这个表,再来一次才到你可用的内存块。这样动态内存管理就可以把真正的那段内存定期合并起来,然后只要调整那个表指向新的地址。

作者:鲍牙叔
链接:https://www.zhihu.com/question/27656256/answer/37518415
来源:知乎
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

补充一些关键描述

  • 指针可以直接操作指定内存,很危险。
  • 句柄相当于操作系统"数据库"中一张表的id(索引),这张表存了操作系统的一些资源的直接指针及其索引(id),操作系统通过暴露一些操作句柄的API,而不是直接将指针暴露给用户,可以起到拦截用户操作并做一些校验工作。
  • linux的fd就是类似的一个句柄。

通过父子进程监听同一个端口

At the operating-system level, there shouldn’t be any problem with this. This is essentially the way that pre-forked servers work:

  1. Create socket in main thread
  2. Bind the socket to an address
  3. Call listen() to put the socket into listen mode – at this point, any connection requests will be accepted and queued by the OS
  4. Fork a bunch of children, each of which inherits the open socket
  5. Then the child processes each call accept(), which blocks until there is a connection for them to handle.

If a child chooses not to call accept() on the listening socket, (as your exec’ed proccess won’t) then there shouldn’t be any issues for that process, or for the ones who are still accepting connections.

The only complication that I can see is that the socket can’t be closed – in order for the operating system to actually close it, all processes with a reference to it have to call close() on it, bringing its open descriptor count down to zero.

It is probably best, in your case, if that behaviour is interfering with the rest of your application, to close the listening socket in the child – after you fork, but before you call exec.

Stackoverflow回答

https://stackoverflow.com/questions/9133337/what-happens-when-parent-child-listen-on-the-same-port


   转载规则


《Linux 概念随记》 阿钟 采用 知识共享署名 4.0 国际许可协议 进行许可。
  目录