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 mức độ ưu tiên, và đánh dấu những kỹ thuật mà bạn có thể sử dụng.
Những kỹ thuật này có thể hữu ích cho những nhà phát triển ứng dụng có hiệu suất cao ứng dụng âm thanh, OEM và nhà cung cấp SoC đang triển khai âm thanh Lớp trừu tượng phần cứng (HAL). Xin lưu ý rằng việc triển khai các kỹ thuật này không đảm bảo ngăn chặn sự cố hoặc lỗi khác, đặc biệt nếu được dùng bên ngoài bối cảnh âm thanh. Kết quả của bạn có thể thay đổi và bạn nên tự tiến hành đánh giá và kiểm thử.
Thông tin khái quát
Máy chủ âm thanh Android AudioFlinger và AudioTrack/AudioRecord quá trình triển khai ứng dụng đang được thiết kế lại để giảm độ trễ. Công việc này bắt đầu từ Android 4.1, và tiếp tục với những cải tiến khác trong 4.2, 4.3, 4.4 và 5.0.
Để đạt được độ trễ thấp hơn này, cần thực hiện nhiều thay đổi trong toàn bộ hệ thống. Một thay đổi quan trọng là chỉ định tài nguyên CPU cho những thời điểm quan trọng các luồng có chính sách lên lịch dễ dự đoán hơn. Lên lịch hẹn đáng tin cậy cho phép giảm kích thước và số lượng vùng đệm âm thanh mà không bị ảnh hưởng tránh tình trạng chạy dưới mức và vượt quá.
Đảo ngược mứ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 theo thời gian thực, trong đó một tác vụ có mức độ ưu tiên cao hơn bị chặn trong thời gian chờ không giới hạn cho 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ư (dùng chung) được bảo vệ bởi) a mutex.
Trong hệ thống âm thanh, đảo ngược ưu tiên thường biểu thị như là một sự cố (nhấp, bật ra, thoát), âm thanh lặp lại khi vùng đệm vòng tròn được sử dụng hoặc trì hoãn phản hồi một lệnh.
Một giải pháp phổ biến cho tính năng đảo ngược mức độ ưu tiên là tăng dung lượng bộ nhớ đệm âm thanh. Tuy nhiên, phương thức này làm tăng độ trễ và chỉ ẩn vấn đề thay vì giải quyết vấn đề. Tốt hơn là bạn nên nắm rõ và ngăn chặn các vấn đề về mức độ ưu tiên đảo ngược, như minh hoạ dưới đây.
Trong khi triển khai âm thanh của Android, đảo ưu tiên được ưu tiên nhất có thể xảy ra ở các địa điểm này. Do đó, bạn nên tập trung chú ý vào đây:
- giữa luồng bộ trộn thông thường và luồng bộ trộn nhanh trong AudioFlinger
- giữa chuỗi gọi lại ứng dụng cho AudioTrack nhanh và luồng kết hợp nhanh (cả hai đều có mức độ ưu tiên cao, nhưng hơi mức độ ưu tiên khác nhau)
- giữa chuỗi gọi lại ứng dụng để có AudioRecord nhanh và chuỗi chụp nhanh (tương tự như trước)
- trong quá trình triển khai Lớp trừu tượng phần cứng (HAL) cho âm thanh, ví dụ: để loại bỏ qua điện thoại hoặc tiếng vọng
- trong trình điều khiển âm thanh trong kernel
- giữa chuỗi gọi lại AudioTrack hoặc AudioRecord và các luồng ứng dụng khác (đây nằm ngoài tầm kiểm soát của chúng tôi)
Giải pháp phổ biến
Sau đây là các giải pháp điển hình:
- tắt các giá trị gián đoạn
- mutex kế thừa ưu tiên
Không thể tắt các gián đoạn trong không gian của người dùng Linux và không hoạt động trên Bộ xử lý đa bộ xử lý đối xứng (SMP).
Kế thừa ưu tiên futexe (tắt tiếng nhanh không gian của người dùng) không được sử dụng trong hệ thống âm thanh vì chúng tương đối nặng, và vì họ dựa vào khách hàng đáng tin cậy.
Các kỹ thuật mà Android sử dụng
Thử nghiệm bắt đầu với tính năng "thử khoá" và khoá bằng thời gian chờ. Đây là các biến thể chặn không bị chặn và bị ràng buộc của khoá mutex hoạt động. Thử khoá và khoá với thời gian chờ hoạt động khá tốt nhưng dễ gặp phải một số chế độ lỗi không rõ ràng: máy chủ không được đảm bảo là có thể truy cập vào trạng thái chia sẻ nếu máy khách bị bận và thời gian chờ tích luỹ có thể quá dài nếu có một chuỗi dài các khoá không liên quan đã hết thời gian chờ.
Chúng tôi cũng sử dụng phép toán nguyên tử chẳng hạn như:
- tăng dần
- bitwise "hoặc"
- bitwise "và"
Tất cả các giá trị này đều trả về giá trị trước đó và bao gồm thông tin cần thiết Rào cản SMP. Nhược điểm là những chế độ này có thể yêu cầu bạn thử lại mà không bị giới hạn. Trên thực tế, chúng tôi nhận thấy rằng việc thử lại không phải là vấn đề.
Lưu ý: Hoạt động nguyên tử và tương tác của chúng với rào cản bộ nhớ bị hiểu lầm và bị sử dụng sai cách. Chúng tôi đưa vào các phương pháp sau: tại đây để cho hoàn chỉnh, nhưng bạn cũng nên đọc bài viết SMP Prime dành cho Android để biết thêm thông tin.
Chúng tôi vẫn sở hữu và sử dụng hầu hết các công cụ nêu trên, và gần đây đã thêm các kỹ thuật này:
- Dùng trình ghi một trình đọc không chặn nhau Hàng đợi FIFO cho dữ liệu.
- Cố gắng sao chép thay vì chia sẻ giữa giá trị cao và mô-đun có mức độ ưu tiên thấp.
- Khi cần chia sẻ trạng thái, hãy giới hạn trạng thái trong kích thước tối đa từ có thể truy cập được theo tỷ lệ trong hoạt động trên 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 FIFO một tác giả một trình đọc không chặn hàng đợi được dùng cho trạng thái thay vì dữ liệu, ngoại trừ trường hợp trình ghi thu gọn các lần đẩy liền kề vào một lần nhấn.
- Chú ý đến rào cản về bộ nhớ để xác định độ chính xác của SMP.
- Tin tưởng, nhưng xác minh. Khi chia sẻ trạng thái giữa các quy trình, đừng giả định rằng trạng thái đó được định dạng đúng. Ví dụ: kiểm tra xem chỉ mục nằm trong giới hạn. Bạn không cần thực hiện quy trình xác minh này giữa các chuỗi tin nhắn diễn ra trong cùng một tiến trình, giữa các quy trình tin cậy lẫn nhau (trong đó thường có cùng UID). Cũng không cần thiết cho việc chia sẻ dữ liệu chẳng hạn như âm thanh PCM trong đó lỗi 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à đối tượng của nhiều nghiên cứu gần đây. Nhưng ngoại trừ hàng đợi FIFO một người đọc duy nhất, chúng tôi nhận thấy rằng các hệ thống này phức tạp và dễ xảy ra lỗi.
Kể từ Android 4.2, bạn có thể tìm thấy tính năng không chặn của chúng tôi, lớp học dành cho một độc giả/người viết ở các địa điểm sau:
- khung/av/include/media/nbaio/
- Frameworks/av/media/libnbaio/
- khung/av/services/audioflinger/StateQueue*
Các trình điều khiển này được thiết kế dành riêng cho AudioFlinger và không phải là 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 hãy biết 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 nhà phát triển, một số mã xử lý ứng dụng OpenSL ES mẫu sẽ được cập nhật thành sử dụng thuật toán không chặn hoặc tham chiếu một thư viện nguồn mở không phải Android.
Chúng tôi đã xuất bản một ví dụ về cách triển khai FIFO không chặn được thiết kế đặc biệt cho
mã xử lý ứng dụng. Xem những tệp này nằm trong thư mục nguồn của nền tảng
frameworks/av/audio_utils
:
Công cụ
Theo hiểu biết của chúng tôi, không có công cụ tự động nào để
tìm tính năng đảo ngược mức độ ưu tiên, đặc biệt là trước khi nó diễn ra. Hơi nhiều
các công cụ phân tích mã tĩnh trong nghiên cứu có khả năng tìm ra mức độ ưu tiên
số lần đảo ngược nếu có thể truy cập vào toàn bộ cơ sở mã. Tất nhiên, nếu
có liên quan đến mã người dùng tuỳ ý (vì đây là mã dành cho ứng dụng)
hoặc là một cơ sở mã lớn (như đối với nhân hệ điều hành Linux và trình điều khiển thiết bị),
thì việc phân tích tĩnh có thể không thiết thực. Điều quan trọng nhất là
hãy đọc thật kỹ các mã và nắm được toàn bộ
hệ thống và các tương tác. Các công cụ như
systrace
và
ps -t -p
rất hữu ích khi xem đảo ngược mức độ ưu tiên sau khi nó xảy ra, nhưng
không thông báo trước cho bạn.
Lời cuối cùng
Sau cuộc thảo luận này, đừng lo lắng về mutex. Disabledx là 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 cao đến các nhiệm vụ có mức độ ưu tiên thấp và trong các hệ thống có giới hạn thời gian, có thể gây ra rắc rối.