17

为什么大多数设备驱动程序中的每个函数都是静态的?因为静态函数在文件范围之外是不可见的。那么,这些驱动函数是如何被用户空间应用程序调用的呢?

4

3 回答 3

13

请记住,在 C 中,一切都是地址。这意味着如果你有地址,你可以调用一个函数。内核有一个名为的宏EXPORT_SYMBOL就是这样做的。它导出函数的地址,以便可以调用驱动程序函数而不必放置头声明,因为这些函数有时在编译时是不知道的。在这种情况下,静态限定符只是为了确保它们仅通过此方法调用,而不是从可能包含该驱动程序代码的其他文件中调用(在某些情况下,包含驱动程序代码头并直接调用它们不是一个好主意) .

编辑:因为有人指出我没有涵盖用户空间。

驱动程序函数通常不直接通过用户空间调用(除了 SYSCALL 指令的 x86 实现,它有时会做一些小技巧来保存上下文切换)。所以这里的 static 关键字没有区别。它只会在内核空间上有所不同。正如@Cong Wang 所指出的,函数通常被放入函数指针的结构中,以便可以通过简单地让结构指向该结构(例如file_ops、调度程序、文件系统、网络代码等)来调用它们。

于 2013-01-20T09:52:02.043 回答
9

因为这些静态函数不应该直接在模块之外使用。它们由模块中的其他函数调用,其中可以是 ioctl 的接口或任何回调。这就是为什么它们可以从用户空间调用,它们只是在调用路径中。

看一下网络虚拟模块:

dummy_dev_init() 显然是静态的:

static int dummy_dev_init(struct net_device *dev)
{
        dev->dstats = alloc_percpu(struct pcpu_dstats);
        if (!dev->dstats)
                return -ENOMEM;

        return 0;
}

但它是注册此网络设备时调用的 ->ndo_init() 的回调。

static const struct net_device_ops dummy_netdev_ops = {
        .ndo_init               = dummy_dev_init,
        .ndo_uninit             = dummy_dev_uninit,
        .ndo_start_xmit         = dummy_xmit,
        .ndo_validate_addr      = eth_validate_addr,
        .ndo_set_rx_mode        = set_multicast_list,
        .ndo_set_mac_address    = eth_mac_addr,
        .ndo_get_stats64        = dummy_get_stats64,
        .ndo_change_carrier     = dummy_change_carrier,
};

显然没有人应该直接调用 dummy_dev_init() 。

于 2013-01-21T02:54:04.277 回答
2

内核有数千个模块,它们是(或曾经是)所有目标文件,通过类似于链接的过程动态加载 - 或者实际上是链接 - 到可执行文件中。你能想象如果他们都导出所有函数名会有多少名称冲突,除非static指定,否则默认 C 行为是这样吗?

用户空间应用程序不能直接调用驱动程序函数,但还有其他交互方式。

于 2013-01-21T03:35:56.173 回答