전체 흐름
이 글은 socket() 호출이 파일 디스크립터를 만들고, struct socket을 프로토콜 연산에 연결하는 경계를 다룬다. NIC·NAPI·qdisc는 이후 패킷 경로의 다른 층이며 소켓 생성의 직접 단계가 아니다.
- socket(family,type,protocol)사용자가 주소 계열·종류·프로토콜을 요청한다.
- __sys_socket_createtype 플래그를 검사하고
sock_create()를 호출한다. - __sock_create → pf->create주소 계열의 생성 연산이
socket을 준비한다.sk연결은 계열 구현에 따른다. - sock_map_fd
sock_alloc_file()이 file을 연결하고fd_install()이 fd를 노출한다. - sendmsg / recvmsg공통 소켓 층이 보안 검사 뒤
sock->ops콜백으로 전달한다. - close → file release마지막 file 참조 정리 시
sock_close()가 프로토콜release에 연결된다.
분기: 유효하지 않은 family/type, 보안 검사 실패, 계열 생성 실패, fd/file 할당 실패에는 정상 fd가 생기지 않는다. bind·connect는 별도 호출이며 생성 함수 자체가 연결 상태를 만들지는 않는다.
소스 코드 위치
net/socket.c가 시스템 호출, fd/file 연결, send/recv 공통 디스패치, close 경계를 포함한다. include/linux/net.h는 struct socket과 proto_ops를 정의한다. net/ipv4/af_inet.c는 AF_INET 계열의 구현 예시이며 모든 주소 계열의 공통 구현은 아니다.
Linux v6.6 원본: net/socket.c include/linux/net.h include/net/sock.h net/ipv4/af_inet.c
| 위치 | 읽을 경계 |
|---|---|
__sys_socket | 프로토콜 선택 후 __sys_socket_create → sock_map_fd. |
__sock_create | family/type 검사, 보안 검사, pf->create, 실패 시 정리. |
sock_sendmsg / sock_recvmsg | 보안 검사와 sock->ops 콜백. 버퍼링·대기 조건은 선택된 프로토콜 구현에서 확인한다. |
socket_file_ops.release | sock_close → __sock_release → 프로토콜 release. |
대표 코드
__sys_socket_create / __sys_socket_file 발췌
{
return __sock_create(net, family, type, protocol, res, 1);
}
EXPORT_SYMBOL(sock_create_kern);
static struct socket *__sys_socket_create(int family, int type, int protocol)
{
struct socket *sock;
int retval;
/* Check the SOCK_* constants for consistency. */
BUILD_BUG_ON(SOCK_CLOEXEC != O_CLOEXEC);
BUILD_BUG_ON((SOCK_MAX | SOCK_TYPE_MASK) != SOCK_TYPE_MASK);
BUILD_BUG_ON(SOCK_CLOEXEC & SOCK_TYPE_MASK);
BUILD_BUG_ON(SOCK_NONBLOCK & SOCK_TYPE_MASK);
if ((type & ~SOCK_TYPE_MASK) & ~(SOCK_CLOEXEC | SOCK_NONBLOCK))
return ERR_PTR(-EINVAL);
type &= SOCK_TYPE_MASK;
retval = sock_create(family, type, protocol, &sock);
if (retval < 0)
return ERR_PTR(retval);
return sock;
}
struct file *__sys_socket_file(int family, int type, int protocol)
{
struct socket *sock;
int flags;
sock = __sys_socket_create(family, type, protocol);
if (IS_ERR(sock))
return ERR_CAST(sock);
flags = type & ~SOCK_TYPE_MASK;
if (SOCK_NONBLOCK != O_NONBLOCK && (flags & SOCK_NONBLOCK))
flags = (flags & ~SOCK_NONBLOCK) | O_NONBLOCK;읽는 법: 시작 중괄호와 sock_create_kern 끝부분은 앞 함수의 꼬리다. 이어지는 본문은 type 비트 검사와 sock_create() 호출을 보여 주지만, 실제 사용자 fd를 설치하는 __sys_socket() 전체는 이 발췌 밖에 있다.
sock_create_lite 발췌
}
return err;
}
/**
* sock_create_lite - creates a socket
* @family: protocol family (AF_INET, ...)
* @type: communication type (SOCK_STREAM, ...)
* @protocol: protocol (0, ...)
* @res: new socket
*
* Creates a new socket and assigns it to @res, passing through LSM.
* The new socket initialization is not complete, see kernel_accept().
* Returns 0 or an error. On failure @res is set to %NULL.
* This function internally uses GFP_KERNEL.
*/
int sock_create_lite(int family, int type, int protocol, struct socket **res)
{
int err;
struct socket *sock = NULL;
err = security_socket_create(family, type, protocol, 1);
if (err)
goto out;
sock = sock_alloc();
if (!sock) {
err = -ENOMEM;
goto out;
}
sock->type = type;
err = security_socket_post_create(sock, family, type, protocol, 1);
if (err)
goto out_release;
out:
*res = sock;
return err;읽는 법: 이 함수는 일반 socket() 생성 경로의 sock_create()와 다르다. 주석대로 초기화가 아직 완전하지 않은 가벼운 socket을 준비하며, 수락 경로 등에서 사용된다. 마지막 out_release 정리는 발췌 밖에 있다.
함수별 분석
기존 코드 블록의 제목을 바로잡았다. 일반 사용자 socket() 경로는 아래 순서이며, 기존 두 번째 발췌인 sock_create_lite()를 이 직선 경로에 끼워 넣지 않는다.
| 단계 | 함수 | 결과 |
|---|---|---|
| 입력 | __sys_socket | type의 별도 플래그를 해석하고 프로토콜 선택 훅을 거친다. |
| 객체 생성 | __sys_socket_create → sock_create → __sock_create | 주소 계열의 pf->create가 socket용 구현을 설정한다. 이 시점에는 사용자 fd가 없다. |
| 파일 연결 | sock_map_fd → sock_alloc_file | file->private_data가 socket을 가리키고 성공 시 fd_install한다. |
| 이후 I/O | sock_sendmsg / sock_recvmsg | 공통 층은 보안 검사를 하고 프로토콜 연산으로 전달한다. |
| 정리 | sock_close → __sock_release | sock->ops->release 후 연결 상태를 정리한다. |
구조체와 필드
struct file을 참조한다.private_data → struct socket; socket_file_ops가 파일 연산을 연결한다.ops → proto_ops, sk → 프로토콜 상태가 있을 수 있다.sendmsg, recvmsg, release 동작과 대기·버퍼 의미를 정한다.fd, file, socket, sock는 같은 객체가 아니다. fd가 복제되면 한 fd의 close()가 곧바로 공통 file의 마지막 참조 해제를 뜻하지 않는다.
| 대상 | 핵심 연결 | 혼동 방지 |
|---|---|---|
| fd | fd_install로 file에 매핑 | 정수 번호 자체가 protocol socket은 아니다. |
struct file | private_data가 socket을 가리킴 | fd 공유·복제와 file 수명을 구분한다. |
struct socket | ops, sk, file, wq | sk 연결은 계열별 구현과 단계에 좌우된다. |
proto_ops / struct sock | 연산 콜백 / 프로토콜 상태 | TCP 큐 동작을 모든 socket의 공통 속성으로 일반화하지 않는다. |
실행 단계와 실패 분기
오류가 나면 fd 생성 완료처럼 그릴 수 없다. 반환 코드와 socket/file의 정리 위치를 분리한다.
| 조건 | 코드 위치 | 결과 |
|---|---|---|
| type 플래그 오류 | __sys_socket_create | -EINVAL 오류 포인터; sock_create 호출 전. |
| family 미등록·보안 검사 실패 | __sock_create | 오류 반환과 필요한 socket/module 참조 정리; fd는 없다. |
pf->create 실패 | __sock_create | 프로토콜 구현의 부분 생성 정리와 공통 에러 경로를 함께 확인한다. |
| fd 할당 실패 | sock_map_fd | sock_release() 후 오류 반환. |
| file 할당 실패 | sock_alloc_file / sock_map_fd | socket 정리 및 예약한 fd 반환; 설치된 fd는 없다. |
| file 최종 정리 | socket_file_ops.release | sock_close에서 프로토콜 release 호출. |
v6.6의 __sock_create는 pf->create 실패 시 out_module_put으로 이동한다. v6.18.37에는 실패 뒤 sock->sk 잔존 여부를 확인하는 경고가 추가됐다.
불변 조건과 실패 사례
사용자 fd는 socket 생성과 file 연결이 성공한 뒤 fd_install에서 노출된다. 그 전의 실패는 설치되지 않은 객체를 정리한다.
fd와 file 참조, struct socket, 프로토콜의 struct sock 수명을 같은 하나로 보지 않는다. 공유된 file의 마지막 정리와 protocol release의 경계를 확인한다.
공통 sock_sendmsg/sock_recvmsg는 보안 검사를 거쳐 proto_ops로 보낸다. 대기·수신 큐·송신 버퍼는 프로토콜별 구현을 따로 읽어야 한다.
생성 자체가 bind나 connect를 수행한다고 해석하지 않는다. 해당 연산은 별도 API와 sock->ops 콜백이다.
계측과 검증
tracepoint의 존재는 빌드 설정에 따라 다르다. syscall 결과, socket 층 send/recv 길이, 프로토콜 상태는 서로 다른 관찰 지점이다.
| 관찰 지점 | 알 수 있는 것 | 한계 |
|---|---|---|
syscalls:sys_enter_socket / exit | 사용자 호출 인자와 반환 fd 또는 오류 | 프로토콜 큐 상태나 이후 I/O까지 보여 주지 않는다. |
sock_send_length / sock_recv_length | 활성화된 빌드에서 socket 층의 반환 길이·오류 | 버퍼·NIC·NAPI의 세부 원인을 단독으로 판정하지 못한다. |
ss -tinp | 지원하는 TCP 소켓 상태의 예시 | 모든 socket family의 공통 진단은 아니다. |
ls /sys/kernel/tracing/events/syscalls/*socket*
ls /sys/kernel/tracing/events/sock/추가 확인 사항
__sys_socket_create()성공과 사용자 fd 설치는 어느 함수에서 갈라지는가?sock_create_lite()를 일반socket()생성 경로로 그리면 어떤 초기화 단계가 빠지는가?- fd를 복제한 뒤 하나만 닫았을 때
socket_file_ops.release의 실행 시점을 어떻게 확인할 것인가? - send/recv가 프로토콜 콜백으로 넘어간 뒤 버퍼링과 대기 조건은 어느 구현에서 확인해야 하는가?