2008-09-25 24 views
12

Tôi đang lập trình trong C cho RAM giới hạn vi điều khiển nhúng với RTOS.Làm thế nào để bảo tồn không gian ngăn xếp với thiết kế tốt?

Tôi thường xuyên ngắt mã của mình thành các hàm ngắn, nhưng mọi chức năng gọi cần phải có nhiều bộ nhớ hơn. Mọi công việc đều cần ngăn xếp của anh ta, và đây là một trong những người tiêu dùng bộ nhớ đáng kể trong dự án.

Có cách nào khác để giữ mã được tổ chức tốt và có thể đọc được, vẫn bảo toàn bộ nhớ không?

Trả lời

11

Cố gắng làm cho các cuộc gọi stack phẳng hơn, vì vậy thay vì a() gọi b() trong đó kêu gọi c() trong đó kêu gọi d(), có a() gọi b(), c(), và d() riêng của mình.

Nếu một hàm chỉ được tham chiếu một lần, hãy đánh dấu nó inline (giả sử trình biên dịch của bạn hỗ trợ tính năng này).

+0

+1 nội tuyến .... tự hỏi liệu trình biên dịch hiện đại có thực hiện tự động không? – kenny

5

Bật tối ưu hóa, nội tuyến cụ thể tích cực. Trình biên dịch sẽ có thể các phương thức nội tuyến để giảm thiểu các cuộc gọi. Tùy thuộc vào trình biên dịch và các công tắc tối ưu bạn sử dụng, đánh dấu một số phương thức là inline có thể giúp (hoặc nó có thể bị bỏ qua).

Với GCC, hãy thử thêm cờ "-finline-functions" (hoặc -O3) và có thể là cờ "-finline-limit = n".

6

Trong trường hợp bạn có thể dành nhiều bộ nhớ chính nhưng chỉ có một đống nhỏ ngăn xếp, tôi đề nghị đánh giá phân bổ tĩnh.

Trong C, tất cả các biến được khai báo bên trong một hàm được "tự động quản lý" có nghĩa là chúng được cấp phát trên ngăn xếp.

Đủ điều kiện các khai báo là "tĩnh" lưu trữ chúng trong bộ nhớ chính thay vì trên ngăn xếp. Về cơ bản, chúng hoạt động giống như các biến toàn cầu nhưng vẫn cho phép bạn tránh những thói quen xấu đi kèm với các hình cầu quá mức. Bạn có thể tạo một trường hợp tốt để khai báo các bộ đệm/biến tồn tại lâu dài như tĩnh để giảm áp lực lên ngăn xếp.

Hãy coi chừng điều này không hoạt động tốt/ở tất cả nếu ứng dụng của bạn đa luồng hoặc nếu bạn sử dụng đệ quy.

+0

Thường không có sự khác biệt về chất lượng giữa RAM cho ngăn xếp và RAM để phân bổ tĩnh. Bạn nên kiểm soát phân bổ thông qua một thứ gì đó chẳng hạn như tệp kiểm soát trình liên kết. Trừ khi bạn có một bộ xử lý phức tạp với nhiều ngân hàng RAM, chẳng hạn như RAM trên chip và RAM ngoài riêng biệt. –

0

Bạn có thể thay thế một số biến cục bộ của mình bằng hình cầu không? Mảng đặc biệt có thể ăn lên ngăn xếp.

Nếu tình huống cho phép bạn chia sẻ một số hình cầu giữa một số chức năng giữa các chức năng, có khả năng bạn có thể giảm thiểu việc in bộ nhớ.

Việc cắt giảm chi phí làm tăng độ phức tạp và rủi ro lớn hơn về tác dụng phụ không mong muốn giữa các chức năng so với bản in chân có thể nhỏ hơn.

Bạn sắp xếp các biến số nào trong các hàm của mình? Chúng ta đang nói về kích thước và giới hạn nào?

0

Tùy thuộc vào trình biên dịch của bạn và các tùy chọn tối ưu hóa tích cực của bạn như thế nào, bạn sẽ có mức sử dụng ngăn xếp cho mọi cuộc gọi hàm bạn thực hiện. Vì vậy, để bắt đầu với bạn có thể sẽ cần phải giới hạn độ sâu của các cuộc gọi chức năng của bạn. Một số trình biên dịch sử dụng các bước nhảy thay vì các nhánh cho các hàm đơn giản sẽ làm giảm mức sử dụng ngăn xếp. Rõ ràng bạn có thể làm điều tương tự bằng cách sử dụng, nói, một macro lắp ráp để nhảy đến các chức năng của bạn chứ không phải là một cuộc gọi hàm trực tiếp.

Như đã đề cập trong các câu trả lời khác, nội tuyến là một tùy chọn có sẵn mặc dù điều đó có chi phí kích thước mã lớn hơn.

Khu vực khác ăn stack là thông số cục bộ. Khu vực này bạn có một số kiểm soát. Sử dụng statics (file level) sẽ tránh được việc phân bổ stack với chi phí phân bổ ram tĩnh của bạn. Globals tương tự như vậy.

Trong trường hợp cực đoan (thực sự) bạn có thể đưa ra một quy ước cho các hàm sử dụng số biến cố định toàn cục cố định làm bộ nhớ tạm thời thay cho người dân địa phương trên ngăn xếp. Bit khéo léo là đảm bảo rằng không có chức năng nào sử dụng cùng một hình cầu được gọi cùng một lúc. (Vì thế các quy ước)

10

Có 3 thành phần để sử dụng ngăn xếp của bạn:

  • địa chỉ Chức năng Gọi lại
  • Chức năng các thông số cuộc gọi
  • tự động (cục bộ) biến

Mấu chốt để giảm thiểu mức sử dụng ngăn xếp của bạn là giảm thiểu thông số truyền và các biến tự động. Tiêu thụ không gian của chức năng gọi thực tế chính nó là khá nhỏ.

thông số

Một cách để giải quyết vấn đề tham số là để vượt qua một cấu trúc (thông qua con trỏ) thay vì một số lượng lớn các thông số.


foo(int a, int b, int c, int d) 
{ 
... 
    bar(int a, int b); 
} 

làm điều này thay vì:


struct my_params { 
    int a; 
    int b; 
    int c; 
    int d; 
}; 
foo(struct my_params* p) 
{ 
    ... 
    bar(p); 
}; 

Chiến lược này là tốt nếu bạn vượt qua xuống rất nhiều thông số. Nếu các tham số khác nhau, thì nó có thể không hoạt động tốt cho bạn. Bạn sẽ kết thúc với một cấu trúc lớn được truyền xung quanh có chứa nhiều thông số khác nhau.

biến tự động (người dân địa phương)

này có xu hướng người tiêu dùng lớn nhất của ngăn xếp không gian.

  • Mảng là kẻ giết người. Không định nghĩa các mảng trong các hàm cục bộ của bạn!
  • Giảm thiểu số lượng biến cục bộ.
  • Sử dụng loại nhỏ nhất cần thiết.
  • Nếu ủy thác lại không phải là vấn đề, bạn có thể sử dụng biến tĩnh mô-đun.

Hãy nhớ rằng nếu bạn chỉ đơn giản di chuyển tất cả các biến cục bộ từ phạm vi cục bộ sang phạm vi mô-đun, bạn KHÔNG lưu bất kỳ dung lượng nào. Bạn đã giao dịch không gian ngăn xếp cho không gian phân đoạn dữ liệu.

Một số bộ nhớ cục bộ hỗ trợ RTOS hỗ trợ lưu trữ "toàn cầu" trên cơ sở theo từng luồng. Điều này có thể cho phép bạn có nhiều biến toàn cầu độc lập trên cơ sở mỗi tác vụ, nhưng điều này sẽ làm cho mã của bạn không đơn giản.

1

Một mẹo mà tôi đọc ở đâu đó để đánh giá yêu cầu ngăn xếp của mã trong thiết lập được nhúng là lấp đầy khoảng trống khi bắt đầu bằng mẫu đã biết (DEAD trong hex là yêu thích của tôi) và để hệ thống chạy một lúc.

Sau khi chạy bình thường, hãy đọc không gian ngăn xếp và xem lượng không gian ngăn xếp đã không được thay thế trong quá trình hoạt động. Thiết kế như vậy để lại ít nhất 150% số đó để giải quyết tất cả các đường dẫn mã bị ám ảnh mà có thể chưa được thực hiện.

+0

Không, không phải vậy. Quan điểm của tôi là bạn có thể không đạt được phạm vi mã 100% và có thể thiếu một vài đường dẫn mã. Chỉ là một nguyên tắc mà tôi theo dõi. –

0

Nếu bạn cần bắt đầu bảo quản không gian ngăn xếp, bạn nên có trình biên dịch tốt hơn hoặc nhiều bộ nhớ hơn.

Phần mềm của bạn thường sẽ phát triển (các tính năng mới, ...), vì vậy nếu bạn phải bắt đầu một dự án bằng cách suy nghĩ về cách giữ gìn không gian ngăn xếp, nó bị tiêu diệt ngay từ đầu.

0

Có, RTOS thực sự có thể ăn RAM để sử dụng ngăn xếp tác vụ. Kinh nghiệm của tôi là người dùng mới của RTOS, có xu hướng sử dụng nhiều công việc hơn mức cần thiết.

Đối với hệ thống nhúng bằng RTOS, RAM có thể là một mặt hàng quý giá. Để bảo toàn RAM, đối với các tính năng đơn giản, nó vẫn có thể có hiệu quả để thực hiện một số tính năng trong một tác vụ, chạy theo kiểu vòng tròn, với thiết kế đa tác vụ hợp tác. Do đó, giảm tổng số nhiệm vụ.

0

Tôi nghĩ bạn có thể tưởng tượng ra một vấn đề không tồn tại ở đây. Hầu hết các trình biên dịch không thực sự làm bất cứ điều gì khi chúng "phân bổ" các biến tự động trên ngăn xếp.

Ngăn xếp được cấp phát trước khi "main()" được thực thi. Khi bạn gọi hàm b() từ hàm a() địa chỉ của vùng lưu trữ ngay lập tức sau biến cuối cùng được sử dụng bởi một biến được chuyển tới b(). Điều này trở thành bắt đầu của b() stack nếu b() sau đó gọi hàm c() sau đó c của stack bắt đầu sau khi biến tự động cuối cùng được xác định bởi b().

Lưu ý rằng bộ nhớ ngăn xếp đã có và được cấp phát, không có khởi tạo diễn ra và quá trình xử lý duy nhất có liên quan là chuyển một con trỏ ngăn xếp.

Thời gian duy nhất này trở thành một vấn đề sẽ là nơi mà cả ba chức năng sử dụng một lượng lớn dung lượng lưu trữ sau đó phải chứa toàn bộ bộ nhớ của cả ba hàm. Cố gắng giữ các chức năng phân bổ một lượng lớn dung lượng lưu trữ ở cuối ngăn xếp cuộc gọi, tức là không gọi một hàm khác từ chúng.

Một thủ thuật khác cho các hệ thống có bộ nhớ là chia các phần bộ nhớ hogging của một hàm thành các hàm riêng biệt.

Các vấn đề liên quan