Proceso de creación de sockets de pila del protocolo del kernel de Linux

Tabla de contenido

1. Descripción general del zócalo

2. Entrada de creación de sockets

2.1 、 sys_socketcall

2.2 、 sys_socket

3. Proceso de creación de sockets

3.1 、 crear_calcetín (__crear_calcetín)

3.2. Asignación e inicialización de nodos de socket (sock_alloc)

3.3. Continuar con la inicialización de acuerdo con la familia de protocolos especificada

3.3.1, registro inet_create (inet_init)

3.3.2, llamada inet_create (núcleo)


1. Descripción general del zócalo

Para establecer un Socket, el programa puede llamar a la función Socket, la función devuelve un descriptor de archivo, el prototipo es: 

int socket(int domain, int type, int protocol); 

dominio: especifique el grupo de protocolos, los más comunes son AF_IENT, AF_INET6, AF_UNIX / AF_LOCAL, AF_NETLINK, etc.
tipo: especifique el tipo, los más comunes son SOCK_STREAM, SOCK_DGRAM, SOCK_RAW, etc.
protocolo: Especifique el protocolo de transmisión, como TCP / UDP, etc. Los diferentes grupos de protocolos corresponden al protocolo predeterminado. Puede completar 0 para usar el valor predeterminado.

Para el uso de la función de enchufe, consulte "Programación de enchufe: instrucciones de creación de interfaz de enchufe (enchufe original)"

Tome linux kernel 2.6.38 como ejemplo, analice brevemente el proceso de operación del kernel de creación de sockets, la pila de llamadas completa es la siguiente:

sys_socketcall
sys_socket
sock_create
__sock_create
inet_create//err = pf->create(net, sock, protocol);

Cuando el modo de usuario ejecuta la creación del socket, el kernel comienza a ejecutarse desde sys_socketcall a la ejecución de inet_create, poco después de completar la creación del kernel del socket, vuelve al modo de usuario. Por supuesto, también están involucradas algunas verificaciones de parámetros, verificaciones de seguridad y operaciones de mapeo. Las funciones relacionadas con la creación de sockets están básicamente en el archivo socket.c

2. Entrada de creación de sockets

2.1 、 sys_socketcall

En la función sys_socketcall, la transacción de funciones relacionadas con el socket se maneja de manera uniforme juzgando la instrucción de llamada. Para la función socket (...), el procesamiento real está en sys_socket.

/*
 *	System call vectors.
 *
 *	Argument checking cleaned up. Saved 20% in size.
 *  This function doesn't need to set the kernel lock because
 *  it is set by the callees.
 */

asmlinkage long sys_socketcall(int call, unsigned long __user *args)
{
	unsigned long a[6];
	unsigned long a0, a1;
	int err;

	if (call < 1 || call > SYS_ACCEPT4)
		return -EINVAL;

	/* copy_from_user should be SMP safe. */
	if (copy_from_user(a, args, nargs[call]))
		return -EFAULT;

	err = audit_socketcall(nargs[call] / sizeof(unsigned long), a);
	if (err)
		return err;

	a0 = a[0];
	a1 = a[1];

	switch (call) {
	case SYS_SOCKET:
		err = sys_socket(a0, a1, a[2]);
		break;
	case SYS_BIND:
		err = sys_bind(a0, (struct sockaddr __user *)a1, a[2]);
		break;
	case SYS_CONNECT:
		err = sys_connect(a0, (struct sockaddr __user *)a1, a[2]);
		break;
	case SYS_LISTEN:
		err = sys_listen(a0, a1);
		break;
	case SYS_ACCEPT:
		err = sys_accept4(a0, (struct sockaddr __user *)a1,
				  (int __user *)a[2], 0);
		break;
	....
	default:
		err = -EINVAL;
		break;
	}
	return err;
}

2.2 、 sys_socket

Al ingresar a la función sys_socket comienza el proceso real de creación de sockets. Aquí se completan principalmente tres cosas:

  1. Compruebe la validez del tipo. Si se establecen otros parámetros, excepto el básico sock_type, SOCK_CLOEXEC y SOCK_NONBLOCK, se devolverá err directamente.
  2. Llame a sock_create para crear la estructura del socket.
  3. Utilice sock_map_fd para mapear la estructura del socket a un descriptor de archivo y devolverlo al espacio de usuario.

La función sys_socket es la siguiente:

asmlinkage long sys_socket(int family, int type, int protocol)
{
	int retval;
	struct socket *sock;
	int flags;
    ...
	 // SOCK_TYPE_MASK: 0xF; SOCK_STREAM等socket类型位于type字段的低4位
    // 将flag设置为除socket基本类型之外的值
	flags = type & ~SOCK_TYPE_MASK;
	
	// 如果flags中有除SOCK_CLOEXEC或者SOCK_NONBLOCK之外的其他参数,则返回EINVAL
	if (flags & ~(SOCK_CLOEXEC | SOCK_NONBLOCK))
		return -EINVAL;
		
	// 取type中的后4位,即sock_type,socket基本类型定义	
	type &= SOCK_TYPE_MASK;

	// 如果设置了SOCK_NONBLOCK,则不论SOCK_NONBLOCK定义是否与O_NONBLOCK相同,
    // 均将flags中的SOCK_NONBLOCK复位,将O_NONBLOCK置位
	if (SOCK_NONBLOCK != O_NONBLOCK && (flags & SOCK_NONBLOCK))
		flags = (flags & ~SOCK_NONBLOCK) | O_NONBLOCK;

	//根据协议族、套接字类型、协议栈类创建socket
	retval = sock_create(family, type, protocol, &sock);
	if (retval < 0)
		goto out;
	// 将socket结构映射为文件描述符retval并返回,
	retval = sock_map_fd(sock, flags & (O_CLOEXEC | O_NONBLOCK));
	if (retval < 0)
		goto out_release;

out:
	/* It may be already another descriptor 8) Not kernel problem. */
	return retval;

out_release:
	sock_release(sock);
	return retval;
}

3. Proceso de creación de sockets

3.1 、 crear_calcetín (__crear_calcetín)

La estructura de socket define el estado básico, tipo, bandera, cola de espera, puntero de archivo, conjunto de funciones de operación, etc. del socket. Usando la estructura de sock, la operación del socket se separa de la transacción relacionada con el procesamiento real del protocolo de red. La estructura específica es la siguiente:

/**
 *  struct socket - general BSD socket
 *  @state: socket state (%SS_CONNECTED, etc)
 *  @type: socket type (%SOCK_STREAM, etc)
 *  @flags: socket flags (%SOCK_ASYNC_NOSPACE, etc)
 *  @ops: protocol specific socket operations
 *  @fasync_list: Asynchronous wake up list
 *  @file: File back pointer for gc
 *  @sk: internal networking protocol agnostic socket representation
 *  @wait: wait queue for several uses
 */
struct socket {
	socket_state		state;// 连接状态:SS_CONNECTING, SS_CONNECTED 等
	short			type;     // 类型:SOCK_STREAM, SOCK_DGRAM 等
	unsigned long		flags;// 标志位:SOCK_ASYNC_NOSPACE(发送队列是否已满)等
	const struct proto_ops	*ops;// socket操作函数集:bind, connect, accept 等
	struct fasync_struct	*fasync_list;
	struct file		*file; // 该socket结构体对应VFS中的file指针
	struct sock		*sk;   // socket网络层表示,真正处理网络协议的地方
	wait_queue_head_t	wait;// 等待队列
};

De regreso a sock_create para continuar viendo el proceso de creación de sockets, sock_create realmente llama a __sock_create. La interfaz completa principalmente las siguientes cosas:

  1. Verifique la legalidad del tipo de familia de protocolos y el tipo de socket.
  2. Comprobación de permiso de seguridad de operación (SElinux), si el modo de usuario crea el scoket para activar la creación, debe comprobarse y el modo kernel se activa para omitir la creación.
  3. Llame a sock_alloc para configurar el nodo scoket y aplique desde el sistema de archivos socket_fs.
  4. Qué capa de socket (ipv4, ipv6, capa Ethernet) se crea de acuerdo con el tipo de familia de protocolo entrante.
  5. El mecanismo RCU se utiliza para garantizar la sincronización y la exclusión mutua del proceso de creación.

El código __sock_create es el siguiente:

int sock_create(int family, int type, int protocol, struct socket **res)
{
	return __sock_create(current->nsproxy->net_ns, family, type, protocol, res, 0);
}
static int __sock_create(struct net *net, int family, int type, int protocol,
			 struct socket **res, int kern)
{
	int err;
	struct socket *sock;
	const struct net_proto_family *pf;

	if (family < 0 || family >= NPROTO)// 检查是否是支持的地址族,即检查协议
		return -EAFNOSUPPORT;
	if (type < 0 || type >= SOCK_MAX) // 检查是否是支持的socket类型
		return -EINVAL;

    // 检查权限,并考虑协议集、类型、协议,以及 socket 是在内核中创建还是在用户空间中创建
    // 可以参考:https://www.ibm.com/developerworks/cn/linux/l-selinux/
	err = security_socket_create(family, type, protocol, kern);
	if (err)
		return err;
	...
	sock = sock_alloc();// 分配socket结构,这其中创建了socket和关联的inode (重点分析)
	if (!sock) {
		if (net_ratelimit())
			printk(KERN_WARNING "socket: no more sockets\n");
		return -ENFILE;	/* Not exactly a match, but its the
				   closest posix thing */
	}

	...
	rcu_read_lock();//socket通过RCU机制实现同步互斥操作。
	根据API传入的family信息确定是那个协议簇,很据协议族在socket创建此处分流
	pf = rcu_dereference(net_families[family]);
	...
	rcu_read_unlock();
	
	/*
	 * Now to bump the refcnt of the [loadable] module that owns this
	 * socket at sock_release time we decrement its refcnt.
	 */
    /*调用协议簇对应的create函数
	  在IPv4协议族中调用inet_create()对已创建的socket继续进行初始化,同时创建网络层socket*/
	err = pf->create(net, sock, protocol);
	if (err < 0)
		goto out_module_put;

	/*如果proto_ops结构实例所在模块以内核模块方式动态加载进内核,
	 则增加该模块的引用计数,在sock_release时,减小该计数。*/
	if (!try_module_get(sock->ops->owner))
		goto out_module_busy;

	/*
	 * Now that we're done with the ->create function, the [loadable]
	 * module can have its refcnt decremented
	 */
	//调用完inet_create函数后,对此模块的引用计数减一。
	module_put(pf->owner);
	//安全模块对创建后的socket做安全检查
	err = security_socket_post_create(sock, family, type, protocol, kern);
	if (err)
		goto out_sock_release;
	*res = sock;

	return 0;

	....
out_release:
	rcu_read_unlock();
	goto out_sock_release;
}

3.2. Asignación e inicialización de nodos de socket (sock_alloc)

sock_mnt es la estructura vfsmount que se guarda cuando se inicializa el socket para montar sock_fs en el sistema, y ​​su mnt_sb apunta al superbloque correspondiente al sistema de archivos. La función new_inode se utiliza para solicitar un nodo del sistema de archivos sock_fs. Luego, recupere el puntero del nodo a través de SOCKET_I (inodo) , la realización es tomar el desplazamiento de la dirección.

static struct socket *sock_alloc(void)
{
    struct inode *inode;
    struct socket *sock;

    inode = new_inode(sock_mnt->mnt_sb);//向文件系统申请节点
    if (!inode)
        return NULL;

    sock = SOCKET_I(inode);//获取指针返回

    kmemcheck_annotate_bitfield(sock, type);
    inode->i_mode = S_IFSOCK | S_IRWXUGO;
    inode->i_uid = current_fsuid();
    inode->i_gid = current_fsgid();

    percpu_add(sockets_in_use, 1);
    return sock;
}

3.3. Continuar con la inicialización de acuerdo con la familia de protocolos especificada

Después de llamar a sock_alloc para solicitar un socket, el proceso de solicitud de un socket ha finalizado. Continúe analizando la función __sock_create y determine qué grupo de protocolos se basa en la información de la familia transmitida por la API, y luego llame a la función de creación correspondiente al grupo de protocolos: err = pf-> El proceso de creación (net, sock, protocolo) aquí es diferente para diferentes grupos de protocolos.

3.3.1, registro inet_create (inet_init)

static int __init inet_init(void)
{
    ......
    (void)sock_register(&inet_family_ops);//注册协议簇

    for (q = inetsw_array; q < &inetsw_array[INETSW_ARRAY_LEN]; ++q)
        inet_register_protosw(q);//注册 inet_protosw
    ......
}

inet_init es la función de inicialización que se llamará en la inicialización del módulo de red. Esta función realiza una gran cantidad de tareas. Por el momento, prestaremos atención a las dos partes anteriores primero, y el resto volverá más tarde.

static struct net_proto_family inet_family_ops = {
    .family = PF_INET,
    .create = inet_create,
    .owner    = THIS_MODULE,
};
int sock_register(const struct net_proto_family *ops)
{
    ......
    spin_lock(&net_family_lock);
    if (net_families[ops->family])
        err = -EEXIST;
    else {
        net_families[ops->family] = ops;
        err = 0;
    }
    ......
}

3.3.2, llamada inet_create (núcleo)

En sock_register, inet_family_ops se registrará en la matriz net_families y las llamadas de índice se realizarán de acuerdo con el tipo de clúster de protocolo específico. Recordemos que cuando se llama a __sock_create, se llama de la siguiente manera

pf = rcu_dereference(net_families[family]);
err = pf->create(net, sock, protocol);

La función inet_create es el núcleo de la creación del socket por parte del kernel. El código es el siguiente:

static int inet_create(struct net *net, struct socket *sock, int protocol)
{
    ......
    sock->state = SS_UNCONNECTED;
    ......
    list_for_each_entry_rcu(answer, &inetsw[sock->type], list) {
    ......
    }
    ......
    sock->ops = answer->ops;
    answer_prot = answer->prot;
    answer_no_check = answer->no_check;
    answer_flags = answer->flags;
    ......
    sk = sk_alloc(net, PF_INET, GFP_KERNEL, answer_prot);
    if (sk == NULL)
        goto out;

    err = 0;
    sk->sk_no_check = answer_no_check;
    if (INET_PROTOSW_REUSE & answer_flags)
        sk->sk_reuse = 1;

    inet = inet_sk(sk);
    inet->is_icsk = (INET_PROTOSW_ICSK & answer_flags) != 0;

    if (SOCK_RAW == sock->type) {
        inet->num = protocol;
        if (IPPROTO_RAW == protocol)
            inet->hdrincl = 1;
    }

    if (ipv4_config.no_pmtu_disc)
        inet->pmtudisc = IP_PMTUDISC_DONT;
    else
        inet->pmtudisc = IP_PMTUDISC_WANT;

    inet->id = 0;

    sock_init_data(sock, sk);

    sk->sk_destruct     = inet_sock_destruct;
    sk->sk_protocol     = protocol;
    sk->sk_backlog_rcv = sk->sk_prot->backlog_rcv;

    inet->uc_ttl    = -1;
    inet->mc_loop    = 1;
    inet->mc_ttl    = 1;
    inet->mc_all    = 1;
    inet->mc_index    = 0;
    inet->mc_list    = NULL;

    sk_refcnt_debug_inc(sk);

    if (inet->num) {
        /* It assumes that any protocol which allows
         * the user to assign a number at socket
         * creation time automatically
         * shares.
         */
        inet->sport = htons(inet->num);
        /* Add to protocol hash chains. */
        sk->sk_prot->hash(sk);
    }

    if (sk->sk_prot->init) {//具体协议的初始化,例如:tcp_ipv4:tcp_v4_init_sock
        err = sk->sk_prot->init(sk);
        if (err)
            sk_common_release(sk);
    }
}

Primero, sock-> state se establece en SS_UNCONNECTED.

En segundo lugar, busque la estructura inet_protosw correspondiente en inetsw de acuerdo con el tipo () especificado por el usuario. Aquí debe confirmar qué contenido corresponde a inetsw y cómo registrarlo. Ahora puede mirar hacia atrás en inet_init. En la parte superior de este artículo, primero recorra la matriz inetsw_array y luego llame a la función inet_register_protosw para cada elemento a registrar. Vea lo que está almacenado en inetsw_array.

/* Upon startup we insert all the elements in inetsw_array[] into
 * the linked list inetsw.
 */
static struct inet_protosw inetsw_array[] =
{
	{
		.type =       SOCK_STREAM,
		.protocol =   IPPROTO_TCP,
		.prot =       &tcp_prot,
		.ops =        &inet_stream_ops,
		.capability = -1,
		.no_check =   0,
		.flags =      INET_PROTOSW_PERMANENT |
			      INET_PROTOSW_ICSK,
	},

	{
		.type =       SOCK_DGRAM,
		.protocol =   IPPROTO_UDP,
		.prot =       &udp_prot,
		.ops =        &inet_dgram_ops,
		.capability = -1,
		.no_check =   UDP_CSUM_DEFAULT,
		.flags =      INET_PROTOSW_PERMANENT,
       },


       {
	       .type =       SOCK_RAW,
	       .protocol =   IPPROTO_IP,	/* wild card */
	       .prot =       &raw_prot,
	       .ops =        &inet_sockraw_ops,
	       .capability = CAP_NET_RAW,
	       .no_check =   UDP_CSUM_DEFAULT,
	       .flags =      INET_PROTOSW_REUSE,
       }
};

Correspondiendo a los tres tipos comúnmente usados, (SOCK_STREAM / IPPROTO_TCP), (SOCK_DGRAM / IPPROTO_UDP), (SOCK_RAW / IPPROTO_IP), el usuario especifica por tipo. Tome TCP como ejemplo para observar los conjuntos de interfaces de operación de socket relacionados tcp_prot e inet_stream_ops. Este conjunto contiene las operaciones del kernel correspondientes a las llamadas al sistema, como la construcción de enlaces tcp tardía, la transmisión y la desconexión de enlaces. detalles como sigue:

struct proto tcp_prot = {
	.name			= "TCP",
	.owner			= THIS_MODULE,
	.close			= tcp_close,
	.connect		= tcp_v4_connect,
	.disconnect		= tcp_disconnect,
	.accept			= inet_csk_accept,
	.ioctl			= tcp_ioctl,
	.init			= tcp_v4_init_sock,
	.destroy		= tcp_v4_destroy_sock,
	.shutdown		= tcp_shutdown,
	.setsockopt		= tcp_setsockopt,
	.getsockopt		= tcp_getsockopt,
	.recvmsg		= tcp_recvmsg,
	.backlog_rcv		= tcp_v4_do_rcv,
	.hash			= inet_hash,
	.unhash			= inet_unhash,
	.get_port		= inet_csk_get_port,
	.enter_memory_pressure	= tcp_enter_memory_pressure,
	.sockets_allocated	= &tcp_sockets_allocated,
	.orphan_count		= &tcp_orphan_count,
	.memory_allocated	= &tcp_memory_allocated,
	.memory_pressure	= &tcp_memory_pressure,
	.sysctl_mem		= sysctl_tcp_mem,
	.sysctl_wmem		= sysctl_tcp_wmem,
	.sysctl_rmem		= sysctl_tcp_rmem,
	.max_header		= MAX_TCP_HEADER,
	.obj_size		= sizeof(struct tcp_sock),
	.twsk_prot		= &tcp_timewait_sock_ops,
	.rsk_prot		= &tcp_request_sock_ops,
	.h.hashinfo		= &tcp_hashinfo,
#ifdef CONFIG_COMPAT
	.compat_setsockopt	= compat_tcp_setsockopt,
	.compat_getsockopt	= compat_tcp_getsockopt,
#endif
};

const struct proto_ops inet_stream_ops = {
	.family		   = PF_INET,
	.owner		   = THIS_MODULE,
	.release	   = inet_release,
	.bind		   = inet_bind,
	.connect	   = inet_stream_connect,
	.socketpair	   = sock_no_socketpair,
	.accept		   = inet_accept,
	.getname	   = inet_getname,
	.poll		   = tcp_poll,
	.ioctl		   = inet_ioctl,
	.listen		   = inet_listen,
	.shutdown	   = inet_shutdown,
	.setsockopt	   = sock_common_setsockopt,
	.getsockopt	   = sock_common_getsockopt,
	.sendmsg	   = tcp_sendmsg,
	.recvmsg	   = sock_common_recvmsg,
	.mmap		   = sock_no_mmap,
	.sendpage	   = tcp_sendpage,
	.splice_read	   = tcp_splice_read,
#ifdef CONFIG_COMPAT
	.compat_setsockopt = compat_sock_common_setsockopt,
	.compat_getsockopt = compat_sock_common_getsockopt,
#endif
};

Tome como ejemplo la llamada al sistema de enlace completo accept para obtener cp. Cuando la tarea está bloqueada en accept, la pila del kernel es la siguiente:

[root@localhost tcp]# cat /proc/2591/stack
[<ffffffff8147a431>] inet_csk_accept+0x1c1/0x260
[<ffffffff814a07e4>] inet_accept+0x34/0xf0
[<ffffffff81427a95>] sys_accept4+0x155/0x2b0
[<ffffffff81427c00>] sys_accept+0x10/0x20
[<ffffffff8100b0d2>] system_call_fastpath+0x16/0x1b
[<ffffffffffffffff>] 0xffffffffffffffff

inet_init bucle a través de la matriz inetsw_array tiene por separado estos tres tipos de registro, mire registrado desaparecido.

void inet_register_protosw(struct inet_protosw *p)
{
    ......
    answer = NULL;
    last_perm = &inetsw[p->type];
    list_for_each(lh, &inetsw[p->type]) {
        answer = list_entry(lh, struct inet_protosw, list);

        /* Check only the non-wild match. */
        if (INET_PROTOSW_PERMANENT & answer->flags) {
            if (protocol == answer->protocol)
                break;
            last_perm = lh;
        }
        answer = NULL;
    }
}

Obviamente, después de registrarse en  inetsw  , puede volver atrás y continuar analizando la función inet_create, la respuesta corresponde a la estructura inet_protosw de TCP / UDP / RAW, realiza una serie de asignaciones y luego llama a la función sk_alloc.

struct sock *sk_alloc(struct net *net, int family, gfp_t priority, struct proto *prot)
{
    struct sock *sk;

    sk = sk_prot_alloc(prot, priority | __GFP_ZERO, family);
    if (sk) {
        sk->sk_family = family;
        sk->sk_prot = sk->sk_prot_creator = prot;
        sock_lock_init(sk);
        sock_net_set(sk, get_net(net));
        atomic_set(&sk->sk_wmem_alloc, 1);
    }
    return sk;
}

sk_prot_alloc en realidad se aplica a una estructura de calcetín en la losa correspondiente a prot o kmalloc directamente, y luego establece el clúster de protocolo y la información de estructura proto. Cabe señalar aquí que lo que se devuelve actualmente es una estructura de calce de estructura, pero de hecho no es solo esta estructura simple. Regresemos a la función inet_create para continuar con el análisis descendente. Se puede ver que después de solicitar sock, la función inet_create usa inet = inet_sk (sk) para forzar la conversión de sock, y luego realiza una serie de asignaciones en la estructura inet convertida. E inet_sock es una representación de sock dedicada al dominio INET, que se extiende desde el struct sock. Además de los atributos básicos del sock, proporciona atributos específicos del dominio INET, como TTL, dirección IP, puerto, etc. ¿Cómo corresponde esto? Tengo que volver y estudiar de nuevo. La implementación final de la solicitud de sock es la siguiente función. Lo mencioné brevemente antes pero no profundicé.

static struct sock *sk_prot_alloc(struct proto *prot, gfp_t priority,int family)
{
    struct sock *sk;
    struct kmem_cache *slab;

    slab = prot->slab;
    if (slab != NULL) {
        .......
    else
        sk = kmalloc(prot->obj_size, priority);

La aplicación de calcetín se implementa a través de kmalloc. El tamaño de la aplicación se especifica mediante prot-> obj_size, y prot-> obj_size se especifica mediante la estructura proto en la estructura estática inet_protosw inetsw_array [] estructura de miembro de matriz anterior, y esta estructura Proto la distingue Protocolo TCP / UDP / RAW, consulte la definición de matriz anterior. Entre ellos, tcp_prot's .obj_size = sizeof (struct tcp_sock), 
udp_prot's .obj_size = sizeof (struct udp_sock), raw_prot's .obj_size = sizeof (struct raw_sock). Mire las definiciones específicas de estas estructuras:

struct tcp_sock {
    /* inet_connection_sock has to be the first member of tcp_sock */
    struct inet_connection_sock    inet_conn;
    u16    tcp_header_len;    /* Bytes of tcp header to send        */
    u16    xmit_size_goal_segs; /* Goal for segmenting output packets */

/*
 *    Header prediction flags
 *    0x5?10 << 16 + snd_wnd in net byte order
 */
    __be32    pred_flags;
........
struct udp_sock {
    /* inet_sock has to be the first member */
    struct inet_sock inet;
    int         pending;    /* Any pending frames ? */
    unsigned int     corkflag;    /* Cork is required */
      __u16         encap_type;    /* Is this an Encapsulation socket? */
    /*
......
struct raw_sock {
    /* inet_sock has to be the first member */
    struct inet_sock inet;
    struct icmp_filter filter;
};

Excepto que tcp_sock primero corresponde a inet_connection_sock, udp y raw corresponden a inet_sock. Echemos un vistazo a inet_connection_sock

struct inet_connection_sock {
    /* inet_sock has to be the first */
    struct inet_sock     icsk_inet;
    struct request_sock_queue icsk_accept_queue;
    struct inet_bind_bucket     *icsk_bind_hash;
    unsigned long         icsk_timeout;
    ......
}

struct inet_sock {
    /* sk and pinet6 has to be the first two members of inet_sock */
    struct sock        sk;
    ......
}

El primer miembro de inet_connection_sock también es inet_sock, y el primer miembro de inet_sock es struct sock. Esto lo hace más claro. Se puede entender que struct sock es una clase base que es la estructura orientada a la pila de protocolos más básica del socket de Linux. Inet_sock se extiende sobre la base de heredar sock. Además de los atributos básicos del socket, proporciona un dominio específico de INET Atributos, como TTL, etc. Simplemente, udp_sock y raw_sock se extienden de forma privada sobre la base de inet_sock. inet_connection_sock inet_sock sobre la base de la herencia, todos expandieron alguna información relacionada con el protocolo orientado a la conexión, y se expandió aún más inet_connection_sock tcp_sock en base al mayor número de atributos privados de TCP, como el control de congestión de ventana deslizante . Hasta ahora, no hay duda sobre la conversión de inet = inet_sk (sk); en la función inet_create anterior.

Volviendo a inet_create, además de la asignación de la estructura inet_sk, también se llevan a cabo una serie de inicializaciones en la estructura struct sock, que involucran principalmente la cola / búfer de envío y recepción de paquetes. Finalmente, se llama a la función init del tipo de protocolo específico. Esta es principalmente la función correspondiente a tcp_v4_init_sock, y udp no define init. La función init de raw solo borra el campo de filtro a 0. Es decir, la parte final en inet_create:

static int inet_create(struct net *net, struct socket *sock, int protocol)
{
    ...
   if (sk->sk_prot->init) {//具体协议的初始化,tcp_ipv4:tcp_v4_init_sock
        err = sk->sk_prot->init(sk);
        if (err)
            sk_common_release(sk);
    }
    ...
}

 

Supongo que te gusta

Origin blog.csdn.net/wangquan1992/article/details/108526898
Recomendado
Clasificación