Не удается удалить модуль ядра netlink, пока к нему подключено пользовательское приложение

#c #linux #sockets #kernel #netlink

#c #linux #сокеты #ядро #netlink

Вопрос:

У меня есть модуль ядра, который взаимодействует с пользовательским пространством с помощью Netlink, это выглядит так

Модуль ядра

 #define NETLINK_USER 31
struct sock *nl_sk = NULL;

int init_module()
{
  struct netlink_kernel_cfg cfg = {
    .input = nl_recv_msg,
  };
    
  nl_sk = netlink_kernel_create(amp;init_net, NETLINK_USER, amp;cfg);
  if (!nl_sk) return -1;
  return 0;
}
void cleanup_module()
{
  netlink_kernel_release(nl_sk);
}
 

И это приложение пользовательского пространства C , которое взаимодействует с ним

Пользовательский C

 #define NETLINK_USER 31
#define APP_PORT 5007
#define TIMEOUT_SEC 3
int sock_fd = 0;

void Listen()
{
  nlmsghdr *nlh = NULL;
  iovec iov;
  msghdr msg;
  sockaddr_nl dest_addr, src_addr;

  sock_fd = socket(PF_NETLINK, SOCK_RAW, NETLINK_USER);
  InitSocketParams(src_addr, dest_addr, nlh, iov, msg, sizeof(LD_EVENT));

  timeval timeout;
  timeout.tv_sec = TIMEOUT_SEC;
  timeout.tv_usec = 0;
  setsockopt(sock_fd, SOL_SOCKET, SO_RCVTIMEO, (const char *)amp;timeout, sizeof(timeout));

  while (isrunning)
  {
    int num_recv = recvmsg(sock_fd, amp;msg, 0);
    if (num_recv <= 0)
    {
      if (errno == EAGAIN) // EAGAIN is a periodic Timeout
        continue;
    }

    void *recv_st = NLMSG_DATA(nlh);
    ProcessMsg(recv_st);
  }

  free(nlh);
  close(sock_fd);
}

int Send(void *msg)
{
  sockaddr_nl src_addr, dest_addr;
  nlmsghdr *nlh = NULL;
  iovec iov;
  msghdr _msghdr;
  int ret = 0;

  sock_fd = socket(PF_NETLINK, SOCK_RAW, NETLINK_USER);
  InitSocketParams(src_addr, dest_addr, nlh, iov, _msghdr, sizeof(*msg));

  memcpy(NLMSG_DATA(nlh), msg, sizeof(*msg));
  int bytes_sent = sendmsg(sock_fd, amp;_msghdr, 0);
  if (bytes_sent < 0)
    ret = errno;

  free(nlh);
  return ret;
}

void InitSocketParams(sockaddr_nl amp;src_addr, sockaddr_nl amp;dest_addr,
                      nlmsghdr *amp;nlh, iovec amp;iov, msghdr amp;msg, const int max_payload_size)
{
  memset(amp;msg, 0, sizeof(msg));
  memset(amp;src_addr, 0, sizeof(src_addr));
  memset(amp;dest_addr, 0, sizeof(dest_addr));

  src_addr.nl_family = AF_NETLINK;
  src_addr.nl_pid = APP_PORT; /* self pid */
  bind(sock_fd, (struct sockaddr *)amp;src_addr, sizeof(src_addr));

  dest_addr.nl_family = AF_NETLINK;
  dest_addr.nl_pid = 0;    /* For Linux Kernel */
  dest_addr.nl_groups = 0; /* unicast */

  if (nlh) free(nlh);
  nlh = (struct nlmsghdr *)malloc(NLMSG_SPACE(max_payload_size));
  memset(nlh, 0, NLMSG_SPACE(max_payload_size));
  nlh->nlmsg_len = NLMSG_SPACE(max_payload_size);
  nlh->nlmsg_pid = DAEMON_PORT;
  nlh->nlmsg_flags = 0;

  iov.iov_base = (void *)nlh;
  iov.iov_len = nlh->nlmsg_len;
  msg.msg_name = (void *)amp;dest_addr;
  msg.msg_namelen = sizeof(dest_addr);
  msg.msg_iov = amp;iov;
  msg.msg_iovlen = 1;
}

Close()
{
  if (sock_fd > 0)
    close(sock_fd);
}

int StartListener()
{
  isrunning = true;
  std::thread th(amp;Listen, this);
  m_watcherthread = std::move(th);
}
 

Проблема в том, что когда приложение пользовательского пространства подключено к модулю ядра, я вообще не могу использовать rmmod модуль ядра. Это показывает rmmod: ERROR: Module my_module is in use . Я должен сначала закрыть приложение пользовательского пространства, чтобы получить доступ к rmmod модулю ядра. Есть ли способ сделать это, не закрывая приложение пользовательского пространства?

Комментарии:

1. Почему вы хотите это сделать? Я не эксперт по ядру, но это звучит невероятно странно — вы почти наверняка не хотите, чтобы модуль исчез, пока с вашим приложением связан дескриптор открытого файла. Вероятно, лучше всего либо заставить ваше приложение закрыть соединение, чтобы вы могли перезагрузить модуль и повторно открыть его, либо просто смириться с накладными расходами на перезагрузку вашего приложения.

2. @JohnGraham Мне нужны как модуль ядра, так и приложение пользовательского пространства, чтобы иметь возможность отключаться / закрываться в любое время. Я могу обработать необходимую логику для другой части, когда она обнаружит отключение.

3. Похоже, вы можете просто закрыть дескрипторы файлов из своего пользовательского приложения — тогда вы можете удалить модуль, если хотите