2010-06-12 7 views
5

Tôi hiểu rằng execve() và gia đình yêu cầu đối số đầu tiên của mảng đối số của nó giống với thực thi được trỏ đến bởi đối số đầu tiên của nó. Tức là, trong này:Tại sao đầu tiên arg để execve() phải là đường dẫn đến thực thi

execve(prog, args, env); 

args [0] thường sẽ giống như prog. Nhưng tôi dường như không thể tìm thấy thông tin về lý do tại sao điều này xảy ra. Tôi cũng hiểu rằng thực thi (er, ít nhất là shell script) luôn có đường dẫn gọi là đối số đầu tiên khi chạy, nhưng tôi nghĩ rằng shell sẽ thực hiện công việc để đặt nó ở đó, và execve() sẽ chỉ cần gọi thực thi bằng cách sử dụng đường dẫn được đưa ra trong đối số đầu tiên của nó ("prog" từ phía trên), sau đó truyền mảng đối số ("args" từ trên) như một lệnh trên dòng lệnh .... nghĩa là tôi không gọi tập lệnh trên dòng lệnh có đường dẫn thực thi trùng lặp trong danh sách args ....

/bin/ls /bin/ls /home/john 

Ai đó có thể giải thích?

Trả lời

4

Nó cho phép bạn chỉ định đường dẫn chính xác đến tệp thực thi được tải, nhưng cũng cho phép hiển thị tên "đẹp" trong các công cụ như ps hoặc top.

execl("/bin/ls", "ls", "/home/john", (char *)0); 
+2

Bạn cần dấu ngoặc kép; và có lẽ một null char * để chấm dứt danh sách các đối số, giả sử bạn đang sử dụng execl(). –

+0

Tôi sẽ dùng từ ngữ của bạn, nhưng thật thú vị, tôi đã tạo một bài kiểm tra thực thi một tập lệnh bash đơn giản phát ra $ 0, và ngay cả khi tôi sử dụng tên chương trình "được làm sạch", $ 0 vẫn sẽ là đường dẫn đầy đủ (được xây dựng từ execve() của đối số đầu tiên, sau đó, tôi giả định). – EBM

+0

@Johnathan: Đổ lỗi cho Python. –

4

Theo this, đối số đầu tiên là tên chương trình là tùy chỉnh.

theo tập quán, các yếu tố đầu tiên nên tên của chương trình thực hiện (ví dụ , thành phần cuối cùng của con đường)

Điều đó nói rằng, những giá trị này có thể khác nhau. Ví dụ, nếu chương trình được khởi chạy từ một liên kết tượng trưng. Tên chương trình có thể khác với tên của liên kết được sử dụng để khởi chạy nó.

Và bạn đã đúng. Shell thường làm công việc thiết lập đối số đầu tiên. Tuy nhiên, trong trường hợp này, việc sử dụng phép thực thi sẽ phá vỡ toàn bộ vỏ - đó là lý do tại sao bạn cần tự thiết lập nó.

+0

Cảm ơn câu trả lời – EBM

9

Không có yêu cầu rằng người đầu tiên của các đối số chịu bất kỳ liên quan đến tên của file thực thi:

int main(void) 
{ 
    char *args[3] = { "rip van winkle", "30", 0 }; 
    execv("/bin/sleep", args); 
    return 1; 
} 

Hãy thử nó - trên máy Mac (sau ba bài kiểm tra):

make x; ./x & sleep 1; ps 

Kết quả đầu ra thứ ba là:

MiniMac JL: make x; ./x & sleep 1; ps 
make: `x' is up to date. 
[3] 5557 
    PID TTY   TIME CMD 
5532 ttys000 0:00.04 -bash 
5549 ttys000 0:00.00 rip van winkle 30 
5553 ttys000 0:00.00 rip van winkle 30 
5557 ttys000 0:00.00 rip van winkle 30 
MiniMac JL: 

comments EBM:

Yeah, và điều này làm cho nó thậm chí còn kỳ lạ hơn. Trong kịch bản bash thử nghiệm của tôi (đích của execve), tôi không thấy giá trị của những gì execve có trong arg [0] bất cứ nơi nào - không phải trong môi trường, và không phải là $ 0.

Sửa đổi thử nghiệm - tập lệnh có tên 'bash'.kịch bản ':

#!/bin/bash 

echo "bash script at sleep (0: $0; *: $*)" 
sleep 30 

Và một chương trình sửa đổi:

int main(void) 
{ 
    char *args[3] = { "rip van winkle", "30", 0 }; 
    execv("./bash.script", args); 
    return 1; 
} 

Điều này mang lại sản lượng ps:

bash script at sleep (0: ./bash.script; *: 30) 
    PID TTY   TIME CMD 
7804 ttys000 0:00.11 -bash 
7829 ttys000 0:00.00 /bin/bash ./bash.script 30 
7832 ttys000 0:00.00 sleep 30 

Có hai khả năng như tôi nhìn thấy nó:

  1. Các hạt nhân juggles dòng lệnh khi thực hiện kịch bản thông qua dòng shebang ('#!/bin/bash'), hoặc
  2. Bản thân Bash sẽ liên kết với danh sách đối số của nó.

Làm thế nào để thiết lập sự khác biệt? Tôi cho rằng việc sao chép vỏ để một cái tên thay thế, và sau đó sử dụng cái tên thay thế trong công việc sẽ cho chúng ta biết điều gì đó:

$ cp /bin/bash jiminy.cricket 
$ sed "s%/bin/bash%$PWD/jiminy.cricket%" bash.script > tmp 
$ mv tmp bash.script 
$ chmod +w bash.script 
$ ./x & sleep 1; ps 
[1] 7851 
bash script at sleep (0: ./bash.script; *: 30) 
    PID TTY   TIME CMD 
7804 ttys000 0:00.12 -bash 
7851 ttys000 0:00.01 /Users/jleffler/tmp/soq/jiminy.cricket ./bash.script 30 
7854 ttys000 0:00.00 sleep 30 
$ 

này, tôi nghĩ rằng, chỉ ra rằng hạt nhân ghi đè argv[0] khi cơ chế công việc được sử dụng.


Phát biểu bình luận bằng nategoose: '#!/Path/to/chương trình'

MiniMac JL: pwd 
/Users/jleffler/tmp/soq 
MiniMac JL: cat al.c 
#include <stdio.h> 
int main(int argc, char **argv) 
{ 
    while (*argv) 
     puts(*argv++); 
    return 0; 
} 
MiniMac JL: make al.c 
cc  al.c -o al 
MiniMac JL: ./al a b 'c d' e 
./al 
a 
b 
c d 
e 
MiniMac JL: cat bash.script 
#!/Users/jleffler/tmp/soq/al 

echo "bash script at sleep (0: $0; *: $*)" 
sleep 30 
MiniMac JL: ./x 
/Users/jleffler/tmp/soq/al 
./bash.script 
30 
MiniMac JL: 

Điều đó cho thấy rằng đó là công việc cơ chế, chứ không phải bất kỳ chương trình như Bash, mà điều chỉnh giá trị của argv[0]. Vì vậy, khi một nhị phân được thực thi, giá trị của argv[0] không được điều chỉnh; khi một kịch bản được thực hiện thông qua shebang, danh sách đối số được điều chỉnh bởi hạt nhân; argv[0] là nhị phân được liệt kê trên shebang; nếu có một đối số sau shebang, nó sẽ trở thành argv[1]; đối số tiếp theo là tên của tệp tập lệnh, theo sau là bất kỳ đối số nào còn lại từ cuộc gọi execv() hoặc tương đương.

MiniMac JL: cat bash.script 
#!/Users/jleffler/tmp/soq/al -arg0 
#!/bin/bash 
#!/Users/jleffler/tmp/soq/jiminy.cricket 

echo "bash script at sleep (0: $0; *: $*)" 
sleep 30 
MiniMac JL: ./x 
/Users/jleffler/tmp/soq/al 
-arg0 
./bash.script 
30 
MiniMac JL: 
+0

Vâng, và điều này làm cho nó thậm chí còn kỳ lạ hơn. Trong kịch bản bash thử nghiệm của tôi (đích của execve), tôi không thấy giá trị của những gì execve có trong arg [0] bất cứ nơi nào - không phải trong môi trường, và không phải là $ 0. – EBM

+0

Tạo một kịch bản «#!/Home/me/print_args' và viết một chương trình arg đơn giản để chắc chắn rằng ai đang sử dụng' argv [0] ' – nategoose

0

Điều đó cho phép chương trình có nhiều tên và hoạt động hơi khác nhau tùy thuộc vào việc sử dụng tên được gọi.

Chương trình tầm thường hình ảnh, ví dụ: print0.c biên dịch vào print0:

#include <stdio.h> 
int main(int argc, char **argv) 
{ 
    printf("%s\n",argv[0]); 
    return 0; 
} 

Chạy nó như ./print0 sẽ in ./print0 Thực hiện một liên kết tượng trưng ví dụ print1 đến nó và bây giờ sử dụng tên ./print1 để chạy nó - nó sẽ in "./print1".

Bây giờ đã có liên kết tượng trưng. Nhưng với hàm exec *(), bạn có thể cho biết tên chương trình của nó một cách rõ ràng.

Tạo phẩm từ * NIX, nhưng rất hay để có.

+0

Xem nhận xét của tôi cho Jonathan Leffler ở trên - sử dụng tập lệnh bash thử nghiệm làm mục tiêu của cuộc gọi execve, arg [0] KHÔNG hiển thị ở bất kỳ đâu. Có lẽ một chương trình C được lập luận của nó khác nhau, nhưng ít nhất là cho một kịch bản bash, tính năng này bạn giải thích có vẻ vắng mặt. – EBM

+0

Có, thực thi của shell không cho phép điều đó. Người ta sẽ phải sử dụng một liên kết tượng trưng để gọi chương trình dưới tên khác. – Dummy00001