Làm thế nào để phân bổ nguyên tử một thiết bị vòng lặp?


11

Tôi đang viết một số tập lệnh shell để xử lý một số nội dung hình ảnh đĩa và tôi cần sử dụng các thiết bị lặp để truy cập một số hình ảnh đĩa. Tuy nhiên, tôi không chắc chắn làm thế nào để phân bổ đúng thiết bị vòng lặp mà không để chương trình của tôi gặp tình trạng đua.

Tôi biết rằng tôi có thể sử dụng losetup -fđể có được thiết bị vòng lặp chưa phân bổ tiếp theo và sau đó phân bổ thiết bị vòng lặp như thế này:

ld=$(losetup -f)
sudo losetup $ld myfile.img
dostuffwith $ld

Tuy nhiên, trong trường hợp tôi muốn chạy nhiều phiên bản chương trình cùng một lúc, đây gần như là một ví dụ trong sách giáo khoa về điều kiện cuộc đua và điều đó làm phiền tôi khá nhiều. Nếu tôi có nhiều phiên bản của chương trình này đang chạy hoặc các chương trình khác đang cố gắng có được một thiết bị lặp, thì mỗi quy trình có thể không thể phân bổ thiết bị vòng lặp trước cuộc gọi tiếp theo losetup -f, trong trường hợp cả hai quy trình sẽ nghĩ rằng cùng một vòng lặp thiết bị có sẵn, nhưng chỉ có một người có thể nhận được nó.

Tôi có thể sử dụng đồng bộ hóa bên ngoài cho việc này, nhưng tôi muốn (nếu có thể) tránh sự phức tạp thêm. Ngoài ra, các chương trình khác sử dụng thiết bị lặp sẽ không tôn trọng bất kỳ sự đồng bộ hóa nào tôi có thể đưa ra.

Làm thế nào tôi có thể tránh điều kiện cuộc đua tiềm năng này? Lý tưởng nhất là tôi muốn có thể khám phá và liên kết thiết bị vòng lặp một cách nguyên tử, ví dụ như bằng một lệnh như:

ld=$(sudo losetup -f myfile.img)
dostuffwith $ld

Tuy nhiên, khi tôi làm điều đó, $ldkhông được gán cho đường dẫn thiết bị vòng lặp và di chuyển sudora ngoài, như trong các sudo ld=$(losetup -f myfile.img)lỗi cấp phép.

Câu trả lời:


13

Đây là một vấn đề kinh điển trong đồng thời: khi phân bổ tài nguyên, bạn cần xác định một cách cơ bản rằng tài nguyên đó là miễn phí và dự trữ nó, nếu không, quá trình khác có thể dự trữ tài nguyên giữa thời gian bạn kiểm tra rằng nó miễn phí và thời gian bạn dự trữ.

Sử dụng losetupchế độ phân bổ tự động ( -f) và chuyển --showtùy chọn để làm cho nó in đường dẫn thiết bị vòng lặp.

ld=$(sudo losetup --show -f /tmp/1m)

Tùy chọn này đã có mặt trong linux-linux kể từ phiên bản 2.13 ( ban đầu được thêm dưới dạng-s , nhưng --showđã được hỗ trợ trong tất cả các phiên bản đã phát hành và các phiên bản gần đây đã bỏ -stên tùy chọn). Thật không may, phiên bản BusyBox không có nó.

Phiên bản 3.1 của nhân Linux đã giới thiệu một phương thức để thực hiện thao tác cấp phát thiết bị vòng lặp trực tiếp trong nhân, thông qua /dev/loop-controlthiết bị mới . Phương pháp này chỉ được hỗ trợ kể từ khi sử dụng linux 2.21. Với kernel <3.1 hoặc produc-linux <2.21, losetupchương trình liệt kê các mục thiết bị vòng lặp để dự trữ một. Tôi không thể thấy một điều kiện cuộc đua trong mã mặc dù; nó sẽ an toàn nhưng nó có thể có một cửa sổ nhỏ trong đó nó sẽ báo cáo không chính xác rằng tất cả các thiết bị được phân bổ mặc dù đây không phải là trường hợp.


Để làm gì </dev/tty?
Stéphane Chazelas

1
Lần trước tôi đã cố gắng, thậm chí là các losetup --find --showcuộc đua. for i in {1..100}; do losetup -f -s $i & doneđã không cho tôi 100 thiết bị vòng lặp. Các thiết bị lặp không đủ phổ biến để nó không thành vấn đề bình thường; nếu nó thực hiện tùy chọn duy nhất của bạn là tạo các khóa riêng của bạn và / hoặc kiểm tra xem thiết bị lặp đúng có được tạo ra như một suy nghĩ sau không.
frostschutz

@frostschutz losetupcó thể thất bại (ví dụ: vì bạn đã hết các mục vòng lặp), nhưng nếu nó báo cáo tên thiết bị, đó là thiết bị được phân bổ thành công. Có phải các phiên bản trước đó có một lỗi khiến nó viết ra một tên thiết bị mặc dù việc phân bổ đã thất bại? Tôi thấy trong mã nguồn giao diện phân bổ trong kernel chỉ tồn tại kể từ kernel 3.1, có phải đó là lỗi với giao diện cũ yêu cầu losetuptiện ích thực hiện tìm kiếm không?
Gilles 'SO- ngừng trở nên xấu xa'

Ồ, có vẻ như nó thực sự được sửa trong các phiên bản gần đây hơn. produc-linux-2.26.2 dường như hoạt động, wi-linux-2.24.1 liên tục in /dev/loop14và như vậy và cuối cùng thiết bị có thể bị thiếu. Có lẽ cách khắc phục là lịch sự /dev/loop-control, nó thường chỉ nhìn vào /proc/partitions...
frostschutz

@frostschutz Kể từ produc-linux 2.21, losetupsử dụng /dev/loop-controlnếu có và điều đó dường như không có điều kiện chạy đua: việc phân bổ xảy ra trong kernel và in đường dẫn thiết bị là điều cuối cùng mà tiện ích thực hiện.
Gilles 'SO- ngừng trở nên xấu xa'

6

Tôi đã hiểu rồi. Mặc dù tôi không chắc vấn đề với sự cho phép là như thế nào, thay vào đó tôi có thể bắn trước và hỏi sau như thế này:

sudo losetup -f myfile.img
ld=$(losetup -j myfile.img | grep -o "/dev/loop[0-9]*")
dostuffwith $ld

2

Bạn có thể sử dụng flock:

  tryagain=1
  while [[ $tryagain -ne 0 ]]; do
    ld=`losetup -f`
    flock -n $ld -c "losetup $ld myfile.img"
    tryagain=$?
  done

Ý tưởng ở đây là bạn thử và flocktập tin thiết bị lặp; nếu một phiên bản khác của cùng một kịch bản có được nó trước, nó sẽ nhận được cuộc gọi losetup $ld myfile.imgflocksẽ trả về 0. Đối với kịch bản mất cuộc đua, losetupsẽ không được gọi và flocksẽ trả về 1, khiến vòng lặp lặp lại.

Để xem thêm man flock.


0

Nếu tất cả những gì bạn muốn làm với hình ảnh như một thiết bị loopback được gắn kết nó như một hệ thống tập tin và làm việc với nội dung, mountlệnh có thể tự động xử lý việc này.

mount -o loop myfile.img /tmp/mountpoint

Trên thực tế, trong trường hợp của tôi, tôi đặc biệt muốn nó không được gắn kết - Tôi đang sử dụng nó như một thiết bị khối chứ không phải là một hệ thống tập tin.
AJMansfield

@AJMansfield Ngoài việc gắn kết nó, bạn có thể làm gì với một thiết bị khối mà bạn không thể làm với một tệp?
Random832

Bạn không thể định dạng nó dưới dạng không gian hoán đổi trên thiết lập btrfs toàn đĩa. Tôi đã có ý định sử dụng tệp cho không gian trao đổi, vì btrfs không hỗ trợ các hoạt động cần thiết cho các tệp hoán đổi và tôi không thể có một phân vùng trao đổi thực sự với thiết lập btrfs toàn đĩa.
AJMansfield
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.