mach port分配的过程分析

1.mach_port_allocate(osfmk\ipc\Mach_port.c)

/*
 *  Routine:    mach_port_allocate [kernel call]
 *  Purpose:
 *      Allocates a right in a space.  Like mach_port_allocate_name,
 *      except that the implementation picks a name for the right.
 *      The name may be any legal name in the space that doesn't
 *      currently denote a right.
 *  Conditions:
 *      Nothing locked.
 *  Returns:
 *      KERN_SUCCESS        The right is allocated.
 *      KERN_INVALID_TASK   The space is null.
 *      KERN_INVALID_TASK   The space is dead.
 *      KERN_INVALID_VALUE  "right" isn't a legal kind of right.
 *      KERN_RESOURCE_SHORTAGE  Couldn't allocate memory.
 *      KERN_NO_SPACE       No room in space for another right.
 */

kern_return_t
mach_port_allocate(
    ipc_space_t     space,
    mach_port_right_t   right,
    mach_port_name_t    *namep)
{
    kern_return_t       kr;
    mach_port_qos_t     qos = qos_template;

    kr = mach_port_allocate_full (space, right, MACH_PORT_NULL,
                    &qos, namep);
    return (kr);
}

2. mach_port_allocate_full(osfmk\ipc\Mach_port.c)

/*
 *  Routine:    mach_port_allocate_full [kernel call]
 *  Purpose:
 *      Allocates a right in a space.  Supports all of the
 *      special cases, such as specifying a subsystem,
 *      a specific name, a real-time port, etc.
 *      The name may be any legal name in the space that doesn't
 *      currently denote a right.
 *  Conditions:
 *      Nothing locked.
 *  Returns:
 *      KERN_SUCCESS        The right is allocated.
 *      KERN_INVALID_TASK   The space is null.
 *      KERN_INVALID_TASK   The space is dead.
 *      KERN_INVALID_VALUE  "right" isn't a legal kind of right.
 *      KERN_RESOURCE_SHORTAGE  Couldn't allocate memory.
 *      KERN_NO_SPACE       No room in space for another right.
 */

kern_return_t
mach_port_allocate_full(
    ipc_space_t     space,
    mach_port_right_t   right,
    mach_port_t     proto,
    mach_port_qos_t     *qosp,
    mach_port_name_t    *namep)
{
    ipc_kmsg_t      kmsg = IKM_NULL;
    kern_return_t       kr;

    if (space == IS_NULL)
        return (KERN_INVALID_TASK);

    if (proto != MACH_PORT_NULL)
        return (KERN_INVALID_VALUE);

    if (qosp->name) {
        if (!MACH_PORT_VALID (*namep))
            return (KERN_INVALID_VALUE);
    }

    if (qosp->prealloc) {
        if (qosp->len > MACH_MSG_SIZE_MAX - MAX_TRAILER_SIZE) {
            return KERN_RESOURCE_SHORTAGE;
        } else {
            mach_msg_size_t size = qosp->len + MAX_TRAILER_SIZE;

            if (right != MACH_PORT_RIGHT_RECEIVE)
                return (KERN_INVALID_VALUE);

            kmsg = (ipc_kmsg_t)ipc_kmsg_prealloc(size);
            if (kmsg == IKM_NULL)
                return (KERN_RESOURCE_SHORTAGE);
        }
    }

    switch (right) {
        case MACH_PORT_RIGHT_RECEIVE:
        {
        ipc_port_t  port;

        if (qosp->name)
            kr = ipc_port_alloc_name(space, *namep, &port);
        else
            kr = ipc_port_alloc(space, namep, &port);
        if (kr == KERN_SUCCESS) {
            if (kmsg != IKM_NULL) 
                ipc_kmsg_set_prealloc(kmsg, port);

            ip_unlock(port);

        } else if (kmsg != IKM_NULL)
            ipc_kmsg_free(kmsg);
        break;
        }

        case MACH_PORT_RIGHT_PORT_SET:
        {
        ipc_pset_t  pset;

        if (qosp->name)
            kr = ipc_pset_alloc_name(space, *namep, &pset);
        else
            kr = ipc_pset_alloc(space, namep, &pset);
        if (kr == KERN_SUCCESS)
            ips_unlock(pset);
        break;
        }

        case MACH_PORT_RIGHT_DEAD_NAME:
        kr = ipc_object_alloc_dead(space, namep);
        break;

        default:
        kr = KERN_INVALID_VALUE;
        break;
    }

    return (kr);
}

当right为MACH_PORT_RIGHT_RECEIVE时,调用kr = ipc_port_alloc(space, namep, &port);

3. ipc_port_alloc(osfmk\ipc\Ipc_port.c)

/*
 *  Routine:    ipc_port_alloc
 *  Purpose:
 *      Allocate a port.
 *  Conditions:
 *      Nothing locked.  If successful, the port is returned
 *      locked.  (The caller doesn't have a reference.)
 *  Returns:
 *      KERN_SUCCESS        The port is allocated.
 *      KERN_INVALID_TASK   The space is dead.
 *      KERN_NO_SPACE       No room for an entry in the space.
 *      KERN_RESOURCE_SHORTAGE  Couldn't allocate memory.
 */

kern_return_t
ipc_port_alloc(
    ipc_space_t     space,
    mach_port_name_t    *namep,
    ipc_port_t      *portp)
{
    ipc_port_t port;
    mach_port_name_t name;
    kern_return_t kr;

#if     MACH_ASSERT
    uintptr_t buf[IP_CALLSTACK_MAX];
    ipc_port_callstack_init_debug(&buf[0], IP_CALLSTACK_MAX);
#endif /* MACH_ASSERT */

    kr = ipc_object_alloc(space, IOT_PORT,
                  MACH_PORT_TYPE_RECEIVE, 0,
                  &name, (ipc_object_t *) &port);
    if (kr != KERN_SUCCESS)
        return kr;

    /* port and space are locked */
    ipc_port_init(port, space, name);

#if     MACH_ASSERT
    ipc_port_init_debug(port, &buf[0], IP_CALLSTACK_MAX);
#endif  /* MACH_ASSERT */

    /* unlock space after init */
    is_write_unlock(space);

    *namep = name;
    *portp = port;

    return KERN_SUCCESS;
}

在函数ipc_object_alloc中得到port和name变量,调用ipc_port_init函数初始化port

4.ipc_object_alloc()(osfmk\ipc\Ipc_object.c)

/*
 *  Routine:    ipc_object_alloc
 *  Purpose:
 *      Allocate an object.
 *  Conditions:
 *      Nothing locked.  If successful, the object is returned locked.
 *      The space is write locked on successful return. 
 *      The caller doesn't get a reference for the object.
 *  Returns:
 *      KERN_SUCCESS        The object is allocated.
 *      KERN_INVALID_TASK   The space is dead.
 *      KERN_NO_SPACE       No room for an entry in the space.
 *      KERN_RESOURCE_SHORTAGE  Couldn't allocate memory.
 */

kern_return_t
ipc_object_alloc(
    ipc_space_t     space,
    ipc_object_type_t   otype,
    mach_port_type_t    type,
    mach_port_urefs_t   urefs,
    mach_port_name_t    *namep,
    ipc_object_t        *objectp)
{
    ipc_object_t object;
    ipc_entry_t entry;
    kern_return_t kr;

    assert(otype < IOT_NUMBER);
    assert((type & MACH_PORT_TYPE_ALL_RIGHTS) == type);
    assert(type != MACH_PORT_TYPE_NONE);
    assert(urefs <= MACH_PORT_UREFS_MAX);

    object = io_alloc(otype);
    if (object == IO_NULL)
        return KERN_RESOURCE_SHORTAGE;

    if (otype == IOT_PORT) {
        ipc_port_t port = (ipc_port_t)object;

        bzero((char *)port, sizeof(*port));
    } else if (otype == IOT_PORT_SET) {
        ipc_pset_t pset = (ipc_pset_t)object;

        bzero((char *)pset, sizeof(*pset));
    }

    io_lock_init(object);
    *namep = CAST_MACH_PORT_TO_NAME(object);
    kr = ipc_entry_alloc(space, namep, &entry);
    if (kr != KERN_SUCCESS) {
        io_free(otype, object);
        return kr;
    }
    /* space is write-locked */

    entry->ie_bits |= type | urefs;
    entry->ie_object = object;
    ipc_entry_modified(space, *namep, entry);

    io_lock(object);

    object->io_references = 1; /* for entry, not caller */
    object->io_bits = io_makebits(TRUE, otype, 0);

    *objectp = object;
    return KERN_SUCCESS;
}

首先分配一个ipc_object_t 类型的object,然后通过ipc_entry_alloc分配一个entry

5**.ipc_entry_alloc()**(osfmk\ipc\Ipc_entry.c)

/*
 *  Routine:    ipc_entry_alloc
 *  Purpose:
 *      Allocate an entry out of the space.
 *  Conditions:
 *      The space is not locked before, but it is write-locked after
 *      if the call is successful.  May allocate memory.
 *  Returns:
 *      KERN_SUCCESS        An entry was allocated.
 *      KERN_INVALID_TASK   The space is dead.
 *      KERN_NO_SPACE       No room for an entry in the space.
 *      KERN_RESOURCE_SHORTAGE  Couldn't allocate memory for an entry.
 */

kern_return_t
ipc_entry_alloc(
    ipc_space_t     space,
    mach_port_name_t    *namep,
    ipc_entry_t     *entryp)
{
    kern_return_t kr;

    is_write_lock(space);

    for (;;) {
        if (!is_active(space)) {
            is_write_unlock(space);
            return KERN_INVALID_TASK;
        }

        kr = ipc_entry_get(space, namep, entryp);
        if (kr == KERN_SUCCESS)
            return kr;

        kr = ipc_entry_grow_table(space, ITS_SIZE_NONE);
        if (kr != KERN_SUCCESS)
            return kr; /* space is unlocked */
    }
}

调用ipc_entry_get在space中分配一个entry

6. ipc_entry_get()(osfmk\ipc\Ipc_entry.c)

/*
 *  Routine:    ipc_entry_get
 *  Purpose:
 *      Tries to allocate an entry out of the space.
 *  Conditions:
 *      The space is write-locked and active throughout.
 *      An object may be locked.  Will not allocate memory.
 *  Returns:
 *      KERN_SUCCESS        A free entry was found.
 *      KERN_NO_SPACE       No entry allocated.
 */

kern_return_t
ipc_entry_get(
    ipc_space_t     space,
    mach_port_name_t    *namep,
    ipc_entry_t     *entryp)
{
    kern_return_t kr;

    kr = ipc_entries_hold(space, 1);
    if (KERN_SUCCESS != kr)
        return kr;

    return ipc_entry_claim(space, namep, entryp);
}

7. ipc_entry_claim()(osfmk\ipc\Ipc_entry.c)

/*
 *  Routine:    ipc_entry_claim
 *  Purpose:
 *      Take formal ownership of a held entry.
 *  Conditions:
 *      The space is write-locked and active throughout.
 *      An object may be locked.  Will not allocate memory.
 *
 *  Note: The returned entry must be marked as modified before
 *        releasing the space lock
 */

kern_return_t
ipc_entry_claim(
    ipc_space_t     space,
    mach_port_name_t    *namep,
    ipc_entry_t     *entryp)
{
    ipc_entry_t entry;
    ipc_entry_t table;
    mach_port_index_t first_free;
    mach_port_gen_t gen;
    mach_port_name_t new_name;

    table = &space->is_table[0];

    first_free = table->ie_next;
    assert(first_free != 0);

    entry = &table[first_free];
    table->ie_next = entry->ie_next;
    space->is_table_free--;

    assert(table->ie_next < space->is_table_size);

    /*
     *  Initialize the new entry.  We need only
     *  increment the generation number and clear ie_request.
     */
    gen = IE_BITS_NEW_GEN(entry->ie_bits);
    entry->ie_bits = gen;
    entry->ie_request = IE_REQ_NONE;

    /*
     *  The new name can't be MACH_PORT_NULL because index
     *  is non-zero.  It can't be MACH_PORT_DEAD because
     *  the table isn't allowed to grow big enough.
     *  (See comment in ipc/ipc_table.h.)
     */
    new_name = MACH_PORT_MAKE(first_free, gen);
    assert(MACH_PORT_VALID(new_name));
    *namep = new_name;
    *entryp = entry;

    return KERN_SUCCESS;
}

最终在此处得到的namep和entry,在ipc_object_alloc中得到的port,在ipc_port_alloc中初始化的port
另外,mach port的分析存在如下特征:

  • entry在table中的index是不变的。
  • 每次使用entry来存放一个新port时,gen的值会加1。
  • namep就是通过这两个参数生成的。
  • entry的ie_bits是gen|type | urefs的结果;

你可能感兴趣的:(macOS源码分析)