/*
* 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);
}
/*
* 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);
/*
* 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
/*
* 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
/*
* 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
/*
* 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);
}
/*
* 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的分析存在如下特征: