Giá trị tối thiểu và tối đa của mã thoát trong Linux là gì?


40

Giá trị tối thiểu và tối đa của các mã thoát sau trong Linux là gì:

  1. Mã thoát được trả về từ một tệp thực thi nhị phân (ví dụ: chương trình C).
  2. Mã thoát được trả về từ tập lệnh bash (khi gọi exit).
  3. Mã thoát được trả về từ một hàm (khi gọi return). Tôi nghĩ rằng đây là giữa 0255.

Đối với phần 3, bạn có nghĩa là trở về từ một hàm shell ? Điều đó có thể phụ thuộc vào vỏ, nhưng tôi lưu ý rằng thủ Bash cho biết " trạng thái Thoát rơi giữa 0255 " và " trạng thái Thoát khỏi builtins vỏ và các lệnh hợp chất này cũng được giới hạn ở phạm vi này. " return, Tất nhiên, một dựng sẵn vỏ.
Toby Speight

Liên quan (có câu trả lời cho hầu hết các câu hỏi của bạn): Mã thoát mặc định khi quá trình kết thúc?
Stéphane Chazelas

@TobySpeight, đó là một hạn chế của bashvỏ. Một số shell khác như zshcó thể trả về bất kỳ giá trị 32 bit nào đã ký như cho exit. Một số thích rchoặc escó thể trả về dữ liệu của bất kỳ loại nào họ hỗ trợ (vô hướng hoặc danh sách). Xem phần hỏi đáp được liên kết để biết chi tiết.
Stéphane Chazelas

Câu trả lời:


74

Số truyền cho _exit()/ exit_group()cuộc gọi hệ thống (đôi khi được gọi là mã thoát để tránh sự nhập nhằng với trạng thái thoát mà cũng là đề cập đến một mã hóa của một trong hai mã thoát hoặc số tín hiệu và thông tin bổ sung tùy thuộc vào việc quá trình này đã bị giết chết hoặc thoát bình thường ) thuộc loại int, vì vậy trên các hệ thống giống Unix như Linux, thường là số nguyên 32 bit với các giá trị từ -2147483648 (-2 31 ) đến 2147483647 (2 31 -1).

Tuy nhiên, trên tất cả các hệ thống, khi quá trình cha mẹ (hoặc subreaper trẻ em hoặc initnếu cha mẹ chết) sử dụng wait(), waitpid(), wait3(), wait4()cuộc gọi hệ thống để lấy nó, chỉ có 8 bit thấp của nó có sẵn (giá trị 0 đến 255 (2 8 - 1)).

Khi sử dụng waitid()API (hoặc trình xử lý tín hiệu trên SIGCHLD), trên hầu hết các hệ thống (và như POSIX hiện yêu cầu rõ ràng hơn trong phiên bản 2016 của tiêu chuẩn (xem _exit()thông số kỹ thuật )), số đầy đủ có sẵn (trong si_statustrường của cấu trúc được trả về ). Tuy nhiên, đó không phải là trường hợp trên Linux, mặc dù cũng cắt ngắn số lượng thành 8 bit với waitid()API, mặc dù điều đó có thể sẽ thay đổi trong tương lai.

Nói chung, bạn chỉ muốn sử dụng các giá trị 0 (thường có nghĩa là thành công) đến 125, vì nhiều shell sử dụng các giá trị trên 128 để $?thể hiện trạng thái thoát để mã hóa số tín hiệu của một quá trình bị giết và đặc biệt là 126 và 127 điều kiện.

Bạn có thể muốn sử dụng 126 đến 255 exit()để có nghĩa tương tự như đối với shell $?(như khi tập lệnh thực hiện ret=$?; ...; exit "$ret"). Sử dụng các giá trị ngoài 0 -> 255 thường không hữu ích. Nói chung, bạn chỉ làm điều đó nếu bạn biết cha mẹ sẽ sử dụng waitid()API trên các hệ thống không cắt bớt và bạn có nhu cầu về phạm vi giá trị 32 bit. Lưu ý rằng nếu bạn làm một exit(2048)ví dụ, đó sẽ được coi là thành công của cha mẹ bằng cách sử dụng các wait*()API truyền thống .

Thêm thông tin tại:

Câu hỏi và trả lời đó hy vọng sẽ trả lời hầu hết các câu hỏi khác của bạn và làm rõ ý nghĩa của trạng thái thoát . Tôi sẽ thêm một vài điều nữa:

Một quá trình không thể chấm dứt trừ khi nó bị giết hoặc gọi các cuộc gọi _exit()/ exit_group()hệ thống. Khi bạn trở về từ main()trong C, libc gọi hệ thống đó gọi với giá trị trả về.

Hầu hết các ngôn ngữ đều có exit()chức năng bao bọc cuộc gọi hệ thống đó và giá trị mà chúng nhận được, nếu bất kỳ ngôn ngữ nào thường được chuyển như đối với cuộc gọi hệ thống. (lưu ý rằng những người đó thường làm nhiều việc hơn như dọn dẹp được thực hiện bởi exit()chức năng của C để xóa bộ đệm stdio, chạy atexit()hook ...)

Đó là trường hợp của ít nhất:

$ strace -e exit_group awk 'BEGIN{exit(1234)}'
exit_group(1234)                        = ?
$ strace -e exit_group mawk 'BEGIN{exit(1234)}'
exit_group(1234)                        = ?
$ strace -e exit_group busybox awk 'BEGIN{exit(1234)}'
exit_group(1234)                        = ?
$ echo | strace -e exit_group sed 'Q1234'
exit_group(1234)                        = ?
$ strace -e exit_group perl -e 'exit(1234)'
exit_group(1234)                        = ?
$ strace -e exit_group python -c 'exit(1234)'
exit_group(1234)                        = ?
$ strace -e exit_group expect -c 'exit 1234'
exit_group(1234)                        = ?
$ strace -e exit_group php -r 'exit(1234);'
exit_group(1234)                        = ?
$ strace -e exit_group zsh -c 'exit 1234'
exit_group(1234)

Bạn có thể thấy một số người phàn nàn khi bạn sử dụng một giá trị ngoài 0-255:

$ echo 'm4exit(1234)' | strace -e exit_group m4
m4:stdin:1: exit status out of range: `1234'
exit_group(1)                           = ?

Một số shell phàn nàn khi bạn sử dụng giá trị âm:

$ strace -e exit_group dash -c 'exit -1234'
dash: 1: exit: Illegal number: -1234
exit_group(2)                           = ?
$ strace -e exit_group yash -c 'exit -- -1234'
exit: `-1234' is not a valid integer
exit_group(2)                           = ?

POSIX để lại hành vi không xác định nếu giá trị được chuyển đến exitnội dung đặc biệt nằm ngoài 0-> 255.

Một số vỏ cho thấy một số hành vi bất ngờ nếu bạn làm:

  • bash(và mkshkhông pdkshdựa vào đó) dựa vào chính nó để cắt giá trị thành 8 bit:

    $ strace -e exit_group bash -c 'exit 1234'
    exit_group(210)                         = ?
    

    Vì vậy, trong các shell đó, nếu bạn muốn thoát với giá trị ngoài 0-255, bạn phải làm một cái gì đó như:

    exec zsh -c 'exit -- -12345'
    exec perl -e 'exit(-12345)'
    

    Đó là thực thi một lệnh khác trong cùng một quy trình có thể gọi cuộc gọi hệ thống với giá trị bạn muốn.

  • như đã đề cập ở câu hỏi và trả lời khác đó, ksh93có hành vi kỳ lạ nhất đối với các giá trị thoát từ 257 đến 256 + max_signal_number trong đó thay vì gọi exit_group(), nó sẽ tự giết mình với tín hiệu tương ứng¹.

    $ ksh -c 'exit "$((256 + $(kill -l STOP)))"'
    zsh: suspended (signal)  ksh -c 'exit "$((256 + $(kill -l STOP)))"'
    

    và nếu không thì cắt bớt số như bash/ mksh.


Though Điều đó có khả năng thay đổi trong phiên bản tiếp theo. Giờ đây, sự phát triển ksh93đã được thực hiện như một nỗ lực của cộng đồng bên ngoài AT & T, hành vi đó, mặc dù được khuyến khích bằng cách nào đó bởi POSIX, đang được hoàn nguyên


2
Bạn có biết nếu có bất kỳ cuộc thảo luận nào về việc triển khai mã thoát hoàn toàn trong si_statusLinux không?
Ruslan

2
@Ruslan, không nhiều hơn austingroupbugs.net/view.php?id=594#c1318 (từ Eric Blake (RedHat)) tại liên kết tôi đã đưa ra
Stéphane Chazelas

1
"là kiểu int, nên số nguyên 32 bit". Linux thực sự đảm bảo rằng một int sẽ luôn là 32 bit? Ngay cả khi chạy trên một số vi điều khiển nhỏ? Điều đó đánh tôi như là thực sự kỳ lạ. POSIX chắc chắn không.
Voo

@Voo, những bộ vi điều khiển nhỏ bé đó không thể chạy Linux. Trong khi C yêu cầu intít nhất 16 bit, POSIX ít nhiều yêu cầu nó phải có ít nhất 32 bitmôi trường lập trình để có uint32_t . Tôi không biết nếu Linux hỗ trợ bất kỳ môi trường lập trình nào trong đó ints là bất cứ thứ gì ngoại trừ 32 bit, tôi chưa bao giờ gặp phải bất kỳ.
Stéphane Chazelas

1
Trên hệ điều hành POSIX comliant, bạn có thể nhận được mã thoát 32 bit đầy đủ trong phiên bản gần đây của Bourne Shell, xem: schillix.sourceforge.net/man/man1/bosh.1.html
schily 18/10/18

12

Tối thiểu là 0, và đó được coi là giá trị thành công. Tất cả những cái khác là thất bại. Tối đa 255còn được gọi là -1.

Các quy tắc này áp dụng cho cả tập lệnh và các tệp thực thi khác, cũng như các hàm shell.

Giá trị lớn hơn dẫn đến modulo 256.


2
Nói chính xác, trong một số shell giống như Bourne (nhưng không phải bashhoặc các loại được sử dụng phổ biến nhất khác), mã thoát được chuyển đến phần exitdựng sẵn không được coi là modulo-256, và thay vào đó gây ra lỗi. (Ví dụ, điểm chung exit -1thực sự không phải là một thiết bị cầm tay tương đương với exit 255hầu hết các shell). Và liệu exit(-1)ở cấp độ C có tương đương với exit(255)một chi tiết thực tế không hoạt động hay không, nhưng phụ thuộc vào hành vi được xác định thực hiện (mặc dù điều này không phải là vấn đề trên các hệ thống hiện đại mà bạn có thể sử dụng trong thực tế).
mtraceur

Từ những gì tôi biết, chỉ ksh93 giới hạn exit(1)tham số là 8 bit.
schily

6

Điều này có vẻ đơn giản, nhưng oh teh tai.

Ngôn ngữ C (và theo sau là hầu hết các ngôn ngữ khác trực tiếp hoặc gián tiếp) yêu cầu trả về từ maintương đương với việc gọi exitvới cùng một đối số là giá trị trả về. Đây là một số nguyên (kiểu trả về là rất rõ ràng int), do đó về nguyên tắc phạm vi sẽ INT_MINđến INT_MAX.

Tuy nhiên, POSIX tuyên bố rằng chỉ 8 bit thấp nhất được truyền vào exitsẽ được cung cấp cho quy trình cha mẹ đang chờ, theo nghĩa đen như thể đó là "trạng thái & 0xFF" .
Vì vậy, trong thực tế, mã thoát là một số nguyên (vẫn được ký) trong đó chỉ có 8 bit thấp nhất được đặt.

Do đó tối thiểu sẽ là -128 và tối đa 127 . Đợi đã, điều đó không đúng. Nó sẽ là 0 đến 255.

Nhưng than ôi, tất nhiên nó không thể đơn giản như vậy . Trong thực tế, Linux (hay đúng hơn là bash) làm điều đó khác đi . Phạm vi mã trả về hợp lệ là 0 đến 255 (tức là không dấu).

Để đảm bảo an toàn trong việc tránh nhầm lẫn, có lẽ nên giả sử rằng mã trả về là không dấu và bỏ bất cứ thứ gì bạn nhận được từ waitkhông dấu. Bằng cách đó, nó phù hợp với những gì bạn nhìn thấy trong một cái vỏ. Vì các bit trên cùng (bao gồm cả bit quan trọng nhất) bị xóa, điều đó thậm chí không "sai" vì mặc dù đã được ký kỹ thuật, các giá trị thực tế luôn không được ký (vì bit dấu không bao giờ được đặt).
Nó cũng giúp tránh lỗi phổ biến khi so sánh mã thoát -1, vì một lý do lạ nào đó dường như không bao giờ xuất hiện ngay cả khi chương trình thoát với -1(tốt, hãy đoán tại sao!).

Về điểm cuối cùng của bạn, trở về từ một chức năng, nếu chức năng này xảy ra main, thì xem ở trên. Mặt khác, nó phụ thuộc vào kiểu trả về của hàm là gì, về nguyên tắc nó có thể là bất cứ thứ gì (bao gồm void).


Bạn đã đúng trước năm 1989 khi waitid()được giới thiệu.
schily

@schily: Không chắc ý bạn là gì? waitid()không chỉ là điều tương tự, hơi khác nhau. Nó chờ đợi một id cụ thể hoặc bất kỳ chủ đề, và nó ghi kết quả cho nhọn để siginfo_tcấu trúc nơi si_statusint(như vậy ... , chỉ cần giống nhau). Tuy nhiên, exit()chỉ vượt qua 8 bit thấp nhất, vì vậy ... hoàn toàn giống như vậy dưới mui xe.
Damon

exit()chuyển tất cả 32 bit của tham số cho kernel và waitid()trả về tất cả 32 bit từ mã thoát. Có thể bạn đã kiểm tra trên Linux, nơi không ai quan tâm để sửa lỗi. Nếu bạn không tin tôi, hãy kiểm tra nó trên hệ điều hành POSIX phù hợp ...
minh vào

@schily: Nếu đó là sự thật (tôi không nghĩ vậy, nhưng dù sao đi nữa), thì Linux đã bị hỏng . Vui lòng đọc thông số kỹ thuật POSIX được liên kết để trả lời của exit, đặc biệt là dòng thứ hai trong "Mô tả", trong đó nêu rõ: "mặc dù chỉ có 8 bit có ý nghĩa nhất (nghĩa là trạng thái & 0377) sẽ có sẵn cho quy trình cha mẹ đang chờ " . Đó là cách một triển khai tuân thủ hoạt động - 8 bit thấp nhất, không phải 32. Bạn có tham chiếu cho 32 bit được truyền không?
Damon

Tôi nghĩ rằng tôi đã đề cập rằng Linux bị hỏng. Tệ hơn nữa: nhân nhân Linux từ chối sửa lỗi. Nếu bạn đọc tiêu chuẩn POSIX, bạn sẽ thấy rằng phiên bản 1995 (SUSv1) giải thích chính xác tính năng ban đầu được giới thiệu bởi SVr4 vào năm 1989 và các phiên bản gần đây (ví dụ: SUSv7tc2) của tiêu chuẩn thậm chí giải thích rõ ràng rằng waitid()siginfo_tcấu trúc được chuyển cho SIGCHLDtrình xử lý trở lại tất cả 32 bit từ exit()tham số.
schily

2
  1. Mã thoát được trả về từ một tệp thực thi nhị phân (ví dụ: chương trình C).
  2. Mã thoát được trả về từ tập lệnh bash (khi gọi exit).

Thoát mã khỏi bất kỳ quy trình nào - cho dù đó là mã thực thi nhị phân, tập lệnh shell hay bất cứ thứ gì khác - nằm trong khoảng từ 0 đến 255. Có thể chuyển một giá trị lớn hơn sang exit(), nhưng chỉ có 8 bit trạng thái thấp hơn được cung cấp cho các quá trình khác thông qua wait().

  1. Mã thoát được trả về từ một hàm (khi gọi return). Tôi nghĩ rằng đây là từ 0 đến 255.

Hàm AC có thể được khai báo là trả về hầu hết mọi loại. Các giới hạn của giá trị trả về của nó được xác định hoàn toàn theo loại đó: ví dụ: -128 đến 127 cho hàm trả về signed charhoặc 0 đến 4.2 tỷ cho hàm trả về unsigned inthoặc bất kỳ số dấu phẩy động nào lên đến và bao gồm cả infhàm trả về double. Và đó không phải là đếm các loại không phải là số, như void *hoặc struct...

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.