Linux xử lý các shell script như thế nào?


22

Đối với câu hỏi này, chúng ta hãy xem xét một tập lệnh shell bash, mặc dù câu hỏi này phải được áp dụng cho tất cả các loại tập lệnh shell.

Khi ai đó thực thi một tập lệnh shell, Linux có tải tất cả tập lệnh cùng một lúc (có thể vào bộ nhớ) không hoặc nó có đọc từng tập lệnh một (từng dòng một) không?

Nói cách khác, nếu tôi thực thi một tập lệnh shell và xóa nó trước khi thực thi hoàn thành, việc thực thi sẽ bị chấm dứt hay nó sẽ tiếp tục như vậy?


3
Thử nó. (Nó sẽ tiếp tục.)
devnull 23/03 '

1
@devnull thực sự có một câu hỏi thú vị ở đây. Cấp, cho dù nó sẽ tiếp tục hay không là tầm thường để kiểm tra nhưng có sự khác biệt giữa các tệp nhị phân (được tải vào bộ nhớ) và các tập lệnh có dòng shebang hoặc tập lệnh không có dòng shebang.
terdon

1
Bạn có thể quan tâm đến câu trả lời này
terdon

23
Với mục đích của mục đích thực tế của bạn, là xóa tập lệnh shell trong quá trình thực thi, sẽ không thành vấn đề nếu nó đọc tất cả cùng một lúc hoặc từng dòng một. Trong Unix, một inode không thực sự bị xóa (ngay cả khi không có liên kết đến nó từ bất kỳ thư mục nào) cho đến khi tệp mở cuối cùng với nó được đóng lại. Nói cách khác, ngay cả khi shell của bạn đọc trong dòng script shell theo từng dòng trong khi thực thi, vẫn an toàn để xóa nó. Ngoại lệ duy nhất là nếu shell của bạn là loại đóng và mở lại tập lệnh shell mỗi lần, nhưng nếu nó làm điều đó, bạn có vấn đề (bảo mật) lớn hơn nhiều.
Chris Jester-Young

Câu trả lời:


33

Nếu bạn sử dụng, stracebạn có thể thấy tập lệnh shell được thực thi khi chạy.

Thí dụ

Nói rằng tôi có kịch bản shell này.

$ cat hello_ul.bash 
#!/bin/bash

echo "Hello Unix & Linux!"

Chạy nó bằng cách sử dụng strace:

$ strace -s 2000 -o strace.log ./hello_ul.bash
Hello Unix & Linux!
$

Nhìn vào bên trong các strace.logtập tin cho thấy sau đây.

...
open("./hello_ul.bash", O_RDONLY)       = 3
ioctl(3, SNDCTL_TMR_TIMEBASE or SNDRV_TIMER_IOCTL_NEXT_DEVICE or TCGETS, 0x7fff0b6e3330) = -1 ENOTTY (Inappropriate ioctl for device)
lseek(3, 0, SEEK_CUR)                   = 0
read(3, "#!/bin/bash\n\necho \"Hello Unix & Linux!\"\n", 80) = 40
lseek(3, 0, SEEK_SET)                   = 0
getrlimit(RLIMIT_NOFILE, {rlim_cur=1024, rlim_max=4*1024}) = 0
fcntl(255, F_GETFD)                     = -1 EBADF (Bad file descriptor)
dup2(3, 255)                            = 255
close(3)     
...

Khi tệp đã được đọc, nó sẽ được thực thi:

...
read(255, "#!/bin/bash\n\necho \"Hello Unix & Linux!\"\n", 40) = 40
rt_sigprocmask(SIG_BLOCK, NULL, [], 8)  = 0
rt_sigprocmask(SIG_BLOCK, NULL, [], 8)  = 0
fstat(1, {st_mode=S_IFCHR|0620, st_rdev=makedev(136, 3), ...}) = 0
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fc0b38ba000
write(1, "Hello Unix & Linux!\n", 20)   = 20
rt_sigprocmask(SIG_BLOCK, NULL, [], 8)  = 0
read(255, "", 40)                       = 0
exit_group(0)                           = ?

Ở trên chúng ta có thể thấy rõ rằng toàn bộ tập lệnh dường như được đọc trong một thực thể duy nhất, và sau đó được thực thi ở đó sau đó. Vì vậy, nó sẽ "xuất hiện" ít nhất là trong trường hợp của Bash rằng nó đọc tệp trong đó, và sau đó thực thi nó. Vì vậy, bạn nghĩ rằng bạn có thể chỉnh sửa tập lệnh trong khi nó đang chạy?

LƯU Ý: Đừng, mặc dù! Đọc tiếp để hiểu lý do tại sao bạn không nên lộn xộn với tệp tập lệnh đang chạy.

Những người phiên dịch khác thì sao?

Nhưng câu hỏi của bạn hơi tắt. Không phải Linux nhất thiết phải tải nội dung của tệp, đó là trình thông dịch đang tải nội dung, vì vậy nó thực sự phụ thuộc vào cách trình thông dịch thực hiện cho dù tải toàn bộ tệp hoặc theo khối hoặc dòng tại một thời điểm.

Vậy tại sao chúng ta không thể chỉnh sửa tập tin?

Tuy nhiên, nếu bạn sử dụng tập lệnh lớn hơn nhiều, bạn sẽ nhận thấy rằng bài kiểm tra trên có một chút sai lệch. Trong thực tế, hầu hết các thông dịch viên tải các tập tin của họ trong các khối. Điều này là khá chuẩn với nhiều công cụ Unix nơi họ tải các khối của một tệp, xử lý nó và sau đó tải một khối khác. Bạn có thể thấy hành vi này với Hỏi & Đáp về U & L mà tôi đã viết cách đây một thời gian liên quan grep, có tiêu đề: grep / egrep tiêu thụ bao nhiêu văn bản mỗi lần? .

Thí dụ

Nói rằng chúng tôi thực hiện các kịch bản shell sau đây.

$ ( 
    echo '#!/bin/bash'; 
    for i in {1..100000}; do printf "%s\n" "echo \"$i\""; done 
  ) > ascript.bash;
$ chmod +x ascript.bash

Kết quả trong tập tin này:

$ ll ascript.bash 
-rwxrwxr-x. 1 saml saml 1288907 Mar 23 18:59 ascript.bash

Trong đó có chứa loại nội dung sau:

$ head -3 ascript.bash ; echo "..."; tail -3 ascript.bash 
#!/bin/bash
echo "1"
echo "2"
...
echo "99998"
echo "99999"
echo "100000"

Bây giờ khi bạn chạy nó bằng cách sử dụng kỹ thuật tương tự ở trên với strace:

$ strace -s 2000 -o strace_ascript.log ./ascript.bash
...    
read(255, "#!/bin/bash\necho \"1\"\necho \"2\"\necho \"3\"\necho \"4\"\necho \"5\"\necho \"6\"\necho \"7\"\necho \"8\"\necho \"9\"\necho \"10\"\necho 
...
...
\"181\"\necho \"182\"\necho \"183\"\necho \"184\"\necho \"185\"\necho \"186\"\necho \"187\"\necho \"188\"\necho \"189\"\necho \"190\"\necho \""..., 8192) = 8192

Bạn sẽ nhận thấy rằng tệp đang được đọc với gia số 8KB, vì vậy Bash và các shell khác có thể sẽ không tải toàn bộ tệp, thay vào đó chúng đọc chúng theo khối.

Tài liệu tham khảo


@terdon - vâng tôi nhớ đã xem câu hỏi đó trước đây.
slm

5
Với một tập lệnh 40 byte, chắc chắn, nó được đọc thành một khối. Hãy thử với tập lệnh> 8kB.
Gilles 'SO- ngừng trở nên xấu xa'

Tôi chưa bao giờ thử, nhưng tôi nghĩ việc xóa các tệp không thực sự được thực hiện cho đến khi tất cả các quy trình đóng bộ mô tả tệp được liên kết với tệp đã xóa, vì vậy bash có thể tiếp tục đọc từ tệp đã xóa.
Farid Nouri Neshat

@Gilles - vâng tôi đã thêm một ví dụ, đã nhận được nó.
slm

2
Hành vi này phụ thuộc vào phiên bản. Tôi đã thử nghiệm với bash phiên bản 3.2.51 (1) và thấy rằng nó không đệm qua dòng hiện tại (xem câu trả lời stackoverflow này ).
Gordon Davisson

11

Đây là phụ thuộc nhiều hơn so với phụ thuộc hệ điều hành.

Tùy thuộc vào phiên bản, kshđọc tập lệnh theo yêu cầu theo khối 8k hoặc 64k byte.

bashđọc các dòng script theo dòng. Tuy nhiên, do các dòng thực tế có thể có chiều dài tùy ý, nó đọc mỗi lần 8176 byte từ đầu dòng tiếp theo để phân tích cú pháp.

Điều này là cho các công trình đơn giản, tức là một bộ các lệnh đơn giản.

Nếu các lệnh có cấu trúc shell được sử dụng ( một trường hợp câu trả lời được chấp nhận bỏ qua để xem xét ) như for/do/donevòng lặp, case/esaccông tắc, tài liệu ở đây, một lớp con được bao quanh bởi dấu ngoặc đơn, định nghĩa hàm, v.v. và bất kỳ sự kết hợp nào ở trên, trình thông dịch shell sẽ đọc lên đến cuối công trình để đảm bảo không có lỗi cú pháp.

Điều này hơi không hiệu quả vì cùng một mã có thể được đọc đi đọc lại nhiều lần nhưng được giảm nhẹ bởi thực tế nội dung này thường được lưu trữ.

Dù trình thông dịch shell là gì, sẽ rất không khôn ngoan khi sửa đổi tập lệnh shell trong khi nó đang được thực thi vì trình bao không thể đọc lại bất kỳ phần nào của tập lệnh và điều này có thể dẫn đến lỗi cú pháp không mong muốn nếu không đồng bộ.

Cũng lưu ý rằng bash có thể gặp sự cố với vi phạm phân đoạn khi không thể lưu trữ một tập lệnh xây dựng tập lệnh quá lớn ksh93 có thể đọc hoàn hảo.


7

Điều đó phụ thuộc vào cách trình thông dịch chạy tập lệnh hoạt động. Tất cả các kernel làm là để ý tệp bắt đầu thực hiện #!, về cơ bản chạy phần còn lại của dòng dưới dạng một chương trình và cung cấp cho nó khả năng thực thi dưới dạng đối số. Nếu trình thông dịch được liệt kê ở đó đọc từng dòng tệp đó (như các shell tương tác sẽ làm gì với những gì bạn nhập), đó là những gì bạn nhận được (nhưng các cấu trúc vòng lặp nhiều dòng được đọc và giữ xung quanh để lặp lại); nếu trình thông dịch đưa tập tin vào bộ nhớ, xử lý nó (có thể biên dịch nó thành biểu diễn trung gian, giống như Perl và Pyton), tập tin sẽ được đọc đầy đủ trước khi thực thi.

Nếu bạn xóa tệp trong khi đó, tệp sẽ không bị xóa cho đến khi trình thông dịch đóng nó (như mọi khi, các tệp sẽ biến mất khi tham chiếu cuối cùng, có thể là một mục nhập thư mục hoặc một quá trình giữ cho nó mở) biến mất.


4

Tệp 'x':

cat<<'dog' >xyzzy
LANG=C
T=`tty`
( sleep 2 ; ls -l xyzzy >$T ) &
( sleep 4 ; rm -v xyzzy >$T ) &
( sleep 4 ; ls -l xyzzy >$T ) &
echo alive. ; sleep 1
echo alive. ; sleep 1
echo alive. ; sleep 1
echo alive. ; sleep 1
echo alive. ; sleep 1
echo alive. ; sleep 1
echo alive. ; sleep 1
echo alive. ; sleep 1
dog

sh xyzzy

Việc chạy:

~/wrk/tmp$ sh x
alive.
alive.
alive.
-rw-r--r-- 1 yeti yeti 287 Mar 23 16:57 xyzzy
alive.
removed `xyzzy'
ls: cannot access xyzzy: No such file or directory
alive.
alive.
alive.
alive.
~/wrk/tmp$ _

IIRC một tập tin không bị xóa miễn là một quá trình giữ cho nó mở. Việc xóa chỉ cần xóa TRỰC TIẾP.

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.