Najważniejsze wskazówki dotyczące zarządzania użyciem pamięci RAM w aplikacjach wysokiego poziomu

Chociaż system operacyjny Azure Sphere używa jądra systemu Linux jako podstawy, ważne jest, aby pamiętać, że nadal piszesz aplikacje dla osadzonego urządzenia z istotnymi ograniczeniami pamięci RAM. Stosowanie dobrych praktyk dotyczących programowania osadzonego ułatwia tworzenie niezawodnych aplikacji Azure Sphere.

Ważne

Aby uzyskać dokładne informacje dotyczące użycia pamięci RAM dla aplikacji, należy uruchomić aplikację bez debugowania. Uruchomienie aplikacji pod debugerem spowoduje nadmierne użycie pamięci RAM, ponieważ pamięć RAM zużywana przez serwer debugowania zostanie uwzględniona w zgłoszonych statystykach użycia pamięci RAM. Aby uzyskać więcej informacji na temat statystyk pamięci dla aplikacji działających na dołączonym urządzeniu, zobacz Używanie pamięci w aplikacjach wysokiego poziomu.

Oto kilka najważniejszych wskazówek do naśladowania:

  • Przydziel pamięć z góry (najlepiej statycznie) i pozostaw ją przydzieloną przez cały okres istnienia aplikacji, gdy tylko jest to możliwe. Znacznie zwiększy to wyznaczność użycia pamięci RAM w aplikacji i zmniejszy ryzyko wzrostu i fragmentacji pamięci w ciągu całego okresu działania aplikacji.
  • Gdy alokacja dynamiczna jest absolutnie niezbędna:
    • Spróbuj zminimalizować częstotliwość alokacji pamięci stosu i deallocations, które są wykonywane przez aplikację w celu zmniejszenia ryzyka fragmentacji pamięci sterty, na przykład poprzez wykorzystanie technik alokacji fragmentów/puli pamięci.
    • Przejrzyj strony stosowe i, jeśli to możliwe, zawijaj połączenia malloc() z wywołaniami memset() wymuszania zatwierdzenia stron. Dzięki temu można zagwarantować, że jeśli alokacja spowoduje, że aplikacja przekroczy limit pamięci RAM, system operacyjny natychmiast ją zakończy i przewidywalnie zakończy. Oczekiwanie na dostęp do przydzielonych stron spowoduje wprowadzenie ryzyka opóźnionej awarii poza pamięcią, która jest trudniejsza do odtworzenia i zdiagnozowania.
    • Włącz śledzenie alokacji pamięci stosu w trybie dewelopera.
  • Unikaj używania Log_Debug dużych ciągów znaków i usuwaj te wywołania (na przykład z literą ) w trybie dewelopera #ifdef. Log_Debug powoduje przydzielenie tymczasowych buforów, co prowadzi do nagłych wybuchów użycia pamięci RAM w przypadku użycia z dużymi ciągami.
  • Używaj interfejsu API EventLoop , jeśli to możliwe, do wykonywania okresowych zadań asynchronicznych (takich jak interakcja z urządzeniami peryferyjnymi) zamiast tworzenia wątków. Tworzenie wątków powoduje, że jądro systemu Linux przydziela dodatkową pamięć przypisaną do aplikacji. Zmniejsza to wyznaczność aplikacji, ponieważ zwiększa prawdopodobieństwo przełączenia harmonogramu systemu operacyjnego między wieloma odrębnymi operacjami, które mogą spowodować, że aplikacja przekroczy limit pamięci RAM. Wiele przykładowych aplikacji Azure Sphere, takich jak GPIO_HighLevelApp, pokazuje, jak używać funkcji EventLoop.
  • Unikaj przedwczesnego stosowania pamięci podręcznej dla wartości, które można ponownie skompilować w czasie wykonywania.
  • Podczas korzystania z libcurl:
    • Dostosuj maksymalne rozmiary buforu gniazda podczas korzystania z libcurl. System operacyjny Azure Sphere przydzieli bufory gniazd przypisane do użycia pamięci RAM w aplikacji. Zmniejszenie tych rozmiarów buforów może być dobrym sposobem na zmniejszenie ilości pamięci RAM w aplikacji. Należy zauważyć, że zbyt małe bufory gniazda negatywnie wpłyną na wydajność libcurl. Zamiast tego dostosuj maksymalne rozmiary buforu dla danego scenariusza:

          static int sockopt_callback(void* clientp, curl_socket_t curlfd, curlsocktype purpose)
          {
              int size = /*specify max buffer sizes here (in bytes)*/
              int size_size = sizeof(size);
              setsockopt(curlfd, SOL_SOCKET, SO_SNDBUF, &size, &size_size);
              setsockopt(curlfd, SOL_SOCKET, SO_RCVBUF, &size, &size_size);
              return CURL_SOCKOPT_OK;
          }
      
          // Place the following along with other calls to curl_easy_setopt
          curl_easy_setopt(curl, CURLOPT_SOCKOPTFUNCTION, &sockopt_callback);
      

      Zapoznaj się z dokumentacją CURLOPT_SOCKOPTFUNCTION libcurl.

      • Podobnie można dostosować parametry CURLOPT_BUFFERSIZE i CURLOPT_UPLOAD_BUFFERSIZE wyższego poziomu.

      • Libcurl obsługuje również zastępowanie funkcji pamięci wewnętrznej przez używanie curl_global_init_mem i przekazywanie funkcji wywołania zwrotnej dla malloc, , freerealloc, , strdupi calloc. Ta funkcja umożliwia śledzenie dynamicznych alokacji, a nawet zmienianie zachowania. Na przykład można przydzielić pulę pamięci z góry, a następnie użyć tych wywołań zwrotnych do przydzielenia pamięci libcurl z tej puli. Może to być skuteczna technika ustawiania poręczy ochronnych i zwiększania determinizmu aplikacji. Więcej informacji na temat korzystania z tych wywołań zwrotnych można znaleźć w dokumentacji curl_global_init_mem libcurl.

        Uwaga

        Ten mechanizm wywołania zwrotne nie obejmuje wszystkich alokacji pamięci spowodowanych przez libcurl, tylko te wykonane bezpośrednio przez samego libcurl. W szczególności alokacje dokonywane przez wilkówSSL pod spodem nie są śledzone.