container_of()的替代方案

3

我是一个新手,正在尝试编写一个串行驱动程序(基于PCI),我不想使用container_of()函数,因为它缺乏向下兼容性。我可能要编译模块的内核版本将是<2.6.x,所以我希望它能与大多数旧版本和新版本兼容。

我想访问串行卡驱动程序的结构成员。该结构是一个自定义结构,包含原子变量,例如use_count及其相关操作--atomic_inc(&serial_card->use_count)。我不想使用container_of()函数来访问它们,因为它会给我包含结构体。是否有任何替代container_of()函数的方法?如果我没记错的话,Allesandro Roubini的《Linux设备驱动程序》在第174页|第6章:高级字符驱动程序操作中描述了一种方法。但我仍然困惑如何分配类似struct scull_dev *dev =&scull_s_device的东西。如果结构本身包含类型为struct pc_device *dev的变量,则上述语句会填充类似的变量,并将其分配给dev。

在我的情况下,我声明了一个结构和相关函数,如下所示:

struct serial_card
{
  unsigned int      id;     // to identify the each card
  //atomic_t        use_count;      //  variable used to check whether the device is already opened or not  
  wait_queue_head_t rx_queue[64];   // queue in which the process are stored
  unsigned int      data_ready[64]; //  queue in which  the process is ready 
  unsigned int      rx_chan;    // used by interrupt handler 
  unsigned int      base, len; // holds physical base address , holds the total area ( for each card )
  unsigned int      *base;      // holds virtual address 
  /*struct cdev     cdev;           //  kernel uses this structure to represent the EACH char device 
 not using the new method to represent char devices in kernel instead using the old method of register_chrdev();*/

  struct pci_dev        *device;    // pci_dev structure for EACH device.
  //struct semaphore    sem;        //Semaphore needed to handle the co-ordination of processes,use incase need arises
};

static struct serial_card *serial_cards;    // pointer to array of structures [ depending on number of cards ],NO_OF_CARDS #defined in header file


static int serialcard_open(struct inode *inode,struct file *filep)
{

  //getting the structure details of type struct serialcard,using the pointer inode->i_cdev and field type cdev

  //struct serial_card *serial_cards = container_of(inode->i_cdev, struct serial_card, cdev);

  // read the current value of use_count

  static int Device_Open = 0;
  if ( Device_Open )            //Device_Open is static varibale used here for checking the no of times a device is opened
  {
    printk("cPCIserial: Open attempt rejected\n");
    return -EBUSY;
  }
  Device_Open++;


  // using the card so increment use_count
  //atomic_inc(&serial_cards->use_count);
  //filep->private_data = serial_cards;

  return 0;
}

第174-175页的完整描述如下:

单开设备

提供访问控制的暴力方法是只允许一个进程打开设备(单开)。最好避免使用这种技术,因为它会抑制用户的创造力。用户可能希望在同一设备上运行不同的进程,一个读取状态信息,而另一个则写入数据。在某些情况下,用户可以通过运行几个简单的程序来执行一些任务,只要他们能够同时访问设备。换句话说,实现单开行为相当于创建策略,这可能会妨碍用户想要做的事情。虽然只允许一个进程打开设备具有不良属性,但也是最容易为设备驱动程序实现的访问控制方式,因此在这里展示。

scullsingle设备维护了一个名为scull_s_available的atomic_t变量;该变量被初始化为1,表示该设备确实可用。open调用将减少并测试scull_s_available,如果其他人已经打开设备,则拒绝访问:

static atomic_t scull_s_available = ATOMIC_INIT(1);
static int scull_s_open(struct inode *inode, struct file *filp)
{
  struct scull_dev *dev = &scull_s_device; /* device information */
  if (! atomic_dec_and_test (&scull_s_available)) {
    atomic_inc(&scull_s_available);
    return -EBUSY; /* already open */
  }

  /* then, everything else is copied from the bare scull device */
  if ( (filp->f_flags & O_ACCMODE) = = O_WRONLY) {
    scull_trim(dev);
    filp->private_data = dev;
    return 0; /* success */
  }

另一方面,释放调用将设备标记为不再忙碌:
static int scull_s_release(struct inode *inode, struct file *filp)
{
  atomic_inc(&scull_s_available); /* release the device */
  return 0;
}

通常,我们建议您将打开标志scull_s_available放置在设备结构(此处为Scull_Dev)中,因为从概念上讲,它属于设备。然而,scull驱动程序使用独立的变量来保存标志,以便可以使用相同的设备结构和方法作为裸scull设备并最小化代码重复。
请让我知道任何此类的替代方案。
谢谢和问候。
4个回答

4
也许我没有理解到重点,但是"cotainer_of"不是一个函数,而是一个宏。如果您担心移植问题,如果系统头文件没有实现它,您可以安全地自己定义它。这里是一个基本实现:
#ifndef container_of
#define container_of(ptr, type, member) \
 ((type *)                              \
   (  ((char *)(ptr))                   \
    - ((char *)(&((type*)0)->member)) ))

#endif

或者这里是来自最新的Linux头文件的更加准确的实现:

#define container_of(ptr, type, member) ({ \
                const typeof( ((type *)0)->member ) *__mptr = (ptr); 
                (type *)( (char *)__mptr - offsetof(type,member) );})

嗨,Giuseppe,感谢您的回复,但您忽略的主要问题是:我在我的struct serial_card中没有使用cdev结构,因此我无法使用宏container_of(),但我需要通过任何其他替代方法了解cdev结构信息,以便我可以像atomic_inc(&serial_cards->use_count)一样使用它。只有当我拥有struct serial_card信息时,才能实现这个语句。获得该信息的方法是container_of(),但由于我没有使用显式cdev结构,而是使用register_chrdev()方式创建默认的cdev结构。谢谢。 - linuxstack
据我所知,“旧方法”是基于大量使用次设备号,作为预分配结构数组的索引。你不是从i_cdev中提取结构体,而是提取次设备号,然后从数组中取出正确的项...哦,那是很久以前的事情了...;-) - Giuseppe Guerrini
你好!你解决了你的问题吗?我也对这个问题很感兴趣。谢谢! - Giuseppe Guerrini
你好,Giuseppe Guerrini,感谢您理解这个复杂的问题。您要求我使用minor提取并从数组中获取正确的项...但是怎么做?请详细说明...我是新手...很抱歉回复晚了,我的工作让我很忙。另外,您能否帮助我了解如何计算设备打开的次数...假设有12个带有次要编号的设备...如果一个设备第二次打开,我不应该允许它发生,我尝试使用具有2个字段(一个用于计数,另一个用于次要编号)的结构体数组,任何代码...将非常有帮助。 - linuxstack
嗨,Giuseppe Guerrini,结构体中的一个字段可能包含一个计数器...如果您有container_of()替代方法和手动打开计数方法的代码,请告诉我...我仍然无法弄清楚,我刚开始学习LDD。提前感谢您。 - linuxstack
显示剩余6条评论

2
在这段代码中,containerof被用来轻松存储和检索正确的dev结构。在read和write函数内,也可以不访问private_data而检索到正确的结构。我不知道为什么你不想使用private->data和containerof,但你总是可以从struct file指针中检索到你的次要编号。
int minor=MINOR(filp->f_dentry_d_inode->i__rdev);

然后使用类似以下方式访问您的多个设备向量:
struct scull_dev* dev = &scull_devices[minor]:

并使用它。


0

@Giuseppe Guerrini,有些编译器不能识别 container_of 在 Linux 实现上的写法。 基本定义没问题,但我想知道这种实现是否存在安全或兼容性风险:

 #ifndef container_of
#define container_of(ptr, type, member) \
 ((type *)                              \
   (  ((char *)(ptr))                   \
    - ((char *)(&((type*)0)->member)) ))

#endif

0

您需要使用filp->privatedata来存储在读/写中也使用的“每个打开”信息。 您需要决定要存储什么以确保可用正确的信息。

可能您想要两个结构。 一个是“设备结构”,另一个是“打开结构”。 打开结构可以在“打开”中动态分配并存储在private_data中。 在释放时,它会被释放。 它应该具有成员,以便您可以在读/写中使用它们来访问所需的数据。

设备结构将针对每个“卡”进行。 在驱动程序初始化中,您可能希望循环处理卡的数量,并为每个创建一个新的设备结构(serial_card)。 您可以将它们设置为静态数组或动态分配,这无关紧要。 我还会在结构中存储次要编号。 次要编号由您选择,因此从1开始并通过#cards进行。 如果您愿意,可以将“0”保留给系统级接口,或者只需从卡片开始即可。

在 open 函数中,您将得到用户打开的次要号。遍历您的 serial_card 列表以寻找匹配项。如果找不到,则退出 open。否则,您已经获得了信息,可以使用它来分配 “打开结构”,填充它,并将其存储在 filp->private_data 中。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接