Google is committed to advancing racial equity for Black communities. See how.
Trang này được dịch bởi Cloud Translation API.
Switch to English

Tránh đảo ngược mức độ ưu tiên

Bài viết này giải thích cách hệ thống âm thanh của Android cố gắng tránh đảo ngược ưu tiên và nêu bật các kỹ thuật mà bạn cũng có thể sử dụng.

Những kỹ thuật này có thể hữu ích cho các nhà phát triển ứng dụng âm thanh hiệu suất cao, OEM và nhà cung cấp SoC đang triển khai HAL âm thanh. Xin lưu ý rằng việc triển khai các kỹ thuật này không được đảm bảo để tránh trục trặc hoặc các lỗi khác, đặc biệt nếu được sử dụng bên ngoài ngữ cảnh âm thanh. Kết quả của bạn có thể khác nhau, và bạn nên tiến hành đánh giá và thử nghiệm của riêng mình.

Lý lịch

Máy chủ âm thanh AudioFlinger Android và triển khai máy khách AudioTrack / AudioRecord đang được tái cấu trúc để giảm độ trễ. Công việc này bắt đầu trong Android 4.1 và tiếp tục với những cải tiến hơn nữa trong 4.2, 4.3, 4.4 và 5.0.

Để đạt được độ trễ thấp hơn này, cần có nhiều thay đổi trên toàn hệ thống. Một thay đổi quan trọng là gán tài nguyên CPU cho các luồng quan trọng về thời gian với chính sách lập lịch dễ đoán hơn. Lập lịch đáng tin cậy cho phép giảm kích thước và số lượng bộ đệm âm thanh trong khi vẫn tránh chạy thiếu và chạy quá mức.

Đảo ngược ưu tiên

Đảo ngược mức độ ưu tiên là một chế độ lỗi cổ điển của hệ thống thời gian thực, trong đó tác vụ có mức độ ưu tiên cao hơn bị chặn trong thời gian không giới hạn để chờ tác vụ có mức độ ưu tiên thấp hơn giải phóng tài nguyên chẳng hạn như (trạng thái được chia sẻ được bảo vệ bởi) mutex .

Trong hệ thống âm thanh, đảo ngược ưu tiên thường biểu hiện dưới dạng trục trặc (nhấp chuột, bật lên, bỏ qua), âm thanh lặp lại khi sử dụng bộ đệm tròn hoặc chậm phản hồi một lệnh.

Một giải pháp phổ biến cho đảo ngược ưu tiên là tăng kích thước bộ đệm âm thanh. Tuy nhiên, phương pháp này làm tăng độ trễ và chỉ ẩn vấn đề thay vì giải quyết nó. Tốt hơn là hiểu và ngăn chặn sự đảo ngược ưu tiên, như được thấy bên dưới.

Trong triển khai âm thanh Android, khả năng đảo ngược mức độ ưu tiên rất dễ xảy ra ở những nơi này. Và vì vậy bạn nên tập trung sự chú ý của mình vào đây:

  • giữa luồng máy trộn thông thường và luồng máy trộn nhanh trong AudioFlinger
  • giữa chuỗi gọi lại ứng dụng cho một chuỗi AudioTrack nhanh và chuỗi bộ trộn nhanh (cả hai đều có mức độ ưu tiên nâng cao, nhưng mức độ ưu tiên hơi khác nhau)
  • giữa chuỗi gọi lại ứng dụng cho một AudioRecord nhanh và chuỗi chụp nhanh (tương tự như trước đó)
  • trong việc triển khai Lớp trừu tượng phần cứng âm thanh (HAL), ví dụ: đối với điện thoại hoặc loại bỏ tiếng vang
  • trong trình điều khiển âm thanh trong nhân
  • giữa chuỗi gọi lại AudioTrack hoặc AudioRecord và các chuỗi ứng dụng khác (điều này nằm ngoài tầm kiểm soát của chúng tôi)

Các giải pháp chung

Các giải pháp điển hình bao gồm:

  • tắt ngắt
  • ưu tiên kế thừa mutexes

Việc tắt ngắt không khả thi trong không gian người dùng Linux và không hoạt động đối với Bộ xử lý đa đối xứng (SMP).

Các futex thừa kế ưu tiên (mutexes không gian người dùng nhanh) có sẵn trong nhân Linux, nhưng thư viện thời gian chạy Android C Bionic hiện chưa được công bố . Chúng không được sử dụng trong hệ thống âm thanh vì chúng tương đối nặng và vì chúng dựa vào một ứng dụng khách đáng tin cậy.

Các kỹ thuật được sử dụng bởi Android

Thử nghiệm bắt đầu với "thử khóa" và khóa khi hết thời gian. Đây là các biến thể chặn không chặn và có giới hạn của hoạt động khóa mutex. Thử khóa và khóa với thời gian chờ hoạt động khá tốt nhưng dễ mắc phải một số chế độ lỗi khó hiểu: máy chủ không được đảm bảo có thể truy cập trạng thái được chia sẻ nếu máy khách đang bận và thời gian chờ tích lũy có thể quá lâu nếu có một chuỗi dài các ổ khóa không liên quan đã hết thời gian.

Chúng tôi cũng sử dụng các phép toán nguyên tử như:

  • tăng
  • bitwise "hoặc"
  • bitwise "và"

Tất cả những thứ này đều trả về giá trị trước đó và bao gồm các rào cản SMP cần thiết. Điểm bất lợi là họ có thể yêu cầu thử lại không giới hạn. Trong thực tế, chúng tôi nhận thấy rằng việc thử lại không thành vấn đề.

Lưu ý: Các hoạt động nguyên tử và tương tác của chúng với các rào cản bộ nhớ nổi tiếng là bị hiểu nhầm và sử dụng không chính xác. Chúng tôi bao gồm các phương pháp này ở đây để hoàn thiện nhưng khuyên bạn cũng nên đọc bài viết SMP Primer cho Android để biết thêm thông tin.

Chúng tôi vẫn có và sử dụng hầu hết các công cụ trên và gần đây đã thêm các kỹ thuật sau:

  • Sử dụng hàng đợi FIFO một đầu đọc đơn không chặn cho dữ liệu.
  • Cố gắng sao chép trạng thái thay vì chia sẻ trạng thái giữa các mô-đun ưu tiên cao và thấp.
  • Khi trạng thái cần được chia sẻ, hãy giới hạn trạng thái ở kích thước tối đa từ có thể được truy cập nguyên tử trong hoạt động một bus mà không cần thử lại.
  • Đối với trạng thái nhiều từ phức tạp, hãy sử dụng hàng đợi trạng thái. Hàng đợi trạng thái về cơ bản chỉ là một hàng đợi FIFO đơn trình đọc đơn không chặn được sử dụng cho trạng thái chứ không phải dữ liệu, ngoại trừ trình viết thu gọn các lần đẩy liền kề thành một lần đẩy.
  • Chú ý đến các rào cản bộ nhớ để biết tính đúng đắn của SMP.
  • Tin tưởng, nhưng hãy xác minh . Khi chia sẻ trạng thái giữa các tiến trình, đừng cho rằng trạng thái đó được hình thành tốt. Ví dụ: kiểm tra xem các chỉ số có nằm trong giới hạn không. Việc xác minh này không cần thiết giữa các chuỗi trong cùng một quy trình, giữa các quy trình tin cậy lẫn nhau (thường có cùng một UID). Nó cũng không cần thiết đối với dữ liệu được chia sẻ chẳng hạn như âm thanh PCM, nơi mà lỗi là không đáng kể.

Các thuật toán không chặn

Các thuật toán không chặn đã là một chủ đề của nhiều nghiên cứu gần đây. Nhưng ngoại trừ hàng đợi FIFO một người viết đơn, chúng tôi nhận thấy chúng phức tạp và dễ xảy ra lỗi.

Bắt đầu từ Android 4.2, bạn có thể tìm thấy các lớp người đọc / người viết đơn, không chặn của chúng tôi ở các vị trí sau:

  • framework / av / include / media / nbaio /
  • framework / av / media / libnbaio /
  • framework / av / services / audioflinger / StateQueue *

Chúng được thiết kế đặc biệt cho AudioFlinger và không có mục đích chung. Các thuật toán không chặn nổi tiếng là khó gỡ lỗi. Bạn có thể xem mã này như một mô hình. Nhưng lưu ý rằng có thể có lỗi và các lớp không được đảm bảo phù hợp cho các mục đích khác.

Đối với các nhà phát triển, một số mã ứng dụng OpenSL ES mẫu nên được cập nhật để sử dụng các thuật toán không chặn hoặc tham chiếu đến thư viện nguồn mở không phải Android.

Chúng tôi đã xuất bản một ví dụ về triển khai FIFO không chặn được thiết kế đặc biệt cho mã ứng dụng. Xem các tệp này nằm trong frameworks/av/audio_utils thư mục nguồn nền tảng frameworks/av/audio_utils :

Công cụ

Theo hiểu biết tốt nhất của chúng tôi, không có công cụ tự động nào để tìm sự đảo ngược ưu tiên, đặc biệt là trước khi nó xảy ra. Một số công cụ phân tích mã tĩnh nghiên cứu có khả năng tìm ra các đảo ngược ưu tiên nếu có thể truy cập toàn bộ cơ sở mã. Tất nhiên, nếu có liên quan đến mã người dùng tùy ý (như ở đây đối với ứng dụng) hoặc là cơ sở mã lớn (đối với nhân Linux và trình điều khiển thiết bị), phân tích tĩnh có thể không thực tế. Điều quan trọng nhất là phải đọc mã rất cẩn thận và nắm bắt được toàn bộ hệ thống và các tương tác. Các công cụ như systraceps -t -p rất hữu ích để xem đảo ngược ưu tiên sau khi nó xảy ra, nhưng không cho bạn biết trước.

Lời cuối cùng

Sau tất cả cuộc thảo luận này, đừng sợ mutexes. Mutexes là người bạn của bạn cho mục đích sử dụng thông thường, khi được sử dụng và triển khai đúng cách trong các trường hợp sử dụng thông thường, không quan trọng về thời gian. Nhưng giữa các tác vụ có mức độ ưu tiên cao và thấp và trong các hệ thống nhạy cảm với thời gian, các ứng dụng câm có nhiều khả năng gây ra sự cố hơn.