Tại sao chúng ta phải truyền tên tệp hai lần trong các hàm exec?


11

Tôi đã đọc Lập trình nâng cao trong môi trường UNIX của Stevens, chương thứ 8 . Tôi đọc và hiểu tất cả sáu chức năng exec.

Một điều tôi nhận thấy là, trong tất cả các hàm exec:

  • đối số đầu tiên là tên tệp / tên đường dẫn (phụ thuộc vào hàm exec).
  • đối số thứ hai là argv [0] mà chúng ta nhận được main(), đó là tên tệp.

Vì vậy, ở đây chúng ta phải truyền tên tệp hai lần trong hàm.

Có bất kỳ lý do cho nó (như chúng ta không thể lấy tên tệp từ tên đường dẫn từ đối số đầu tiên)?

Câu trả lời:


14

Vì vậy, ở đây chúng ta phải truyền tên tệp hai lần trong hàm.

Chúng không hoàn toàn giống như bạn nhận thấy bằng cách quan sát rằng một trong số chúng được sử dụng làm argv[0]giá trị. Điều này không phải giống như tên cơ sở của tệp thực thi; nhiều / hầu hết mọi thứ bỏ qua nó và bạn có thể đặt bất cứ thứ gì bạn muốn vào đó.

Đầu tiên là đường dẫn thực tế để thực thi, trong đó có một sự cần thiết rõ ràng. Cái thứ hai được truyền cho quá trình bề ngoài như tên được sử dụng để gọi nó, nhưng, vd:

execl("/bin/ls", "banana", "-l", NULL);

Sẽ làm việc tốt, giả định /bin/lslà con đường chính xác.

Một số ứng dụng, tuy nhiên, sử dụng argv[0]. Thông thường chúng có một hoặc nhiều symlink trong $PATH; điều này là phổ biến với các tiện ích nén (đôi khi họ sử dụng trình bao bọc thay thế). Nếu bạn đã xzcài đặt, stat $(which xzcat)hiển thị đó là một liên kết đến xz, và man xzcatgiống như man xzgiải thích "xzcat tương đương với xz --decompress --stdout". Cách xz có thể cho biết nó được gọi như thế nào bằng cách kiểm tra argv[0], làm cho các giá trị này tương đương:

execl("/bin/xz", "xzcat", "somefile.xz", NULL);
execl("/bin/xz", "xz", "--decompress", "--stdout", "somefile.xz", NULL);

4
Ah, vậy điều này sẽ giải thích làm thế nào busyboxcó thể là những gì bạn muốn nó phụ thuộc vào cách bạn gọi nó phải không?
terdon

3
@terdon chính xác là cách nhị phân đơn cho busybox thỏa mãn rất nhiều lệnh khác nhau.
mah

7
Điều đó có nghĩa là nếu /bin/lslà busybox, nó sẽ không biết cách thực thi banana!
Đi xe đạp

6

Bạn không phải vượt qua tên tệp hai lần.

Cái đầu tiên là tập tin thực sự được thực thi.

Đối số thứ hai là những gì nên là argv[0]của quá trình, tức là những gì quá trình nên xem như tên của nó. Ví dụ, nếu bạn chạy lstừ shell, đối số thứ nhất là /bin/ls, thứ hai là chỉ ls.

Bạn có thể thực thi một tệp nhất định và gọi nó là một cái gì đó khác thông qua đối số thứ hai; chương trình có thể kiểm tra tên của nó và hành xử khác nhau theo tên. Điều này cũng có thể được thực hiện thông qua các liên kết cứng (hoặc liên kết tượng trưng) nhưng cách này mang lại sự linh hoạt hơn.


Trong thực tế, các liên kết là cùng một phương thức kể từ đó đặt argv[0]thành tên liên kết.
goldilocks

Trong đoạn cuối, "Bạn có thể thực thi một tập tin nhất định và gọi nó là một cái gì đó khác thông qua đối số thứ hai; chương trình có thể kiểm tra tên của nó và hành xử" khác nhau "theo tên". bạn có thể vui lòng giải thích hoặc cho tôi một số bài đọc, tôi là một người mới cho môi trường này.
munjal007

Phần cuối của câu trả lời của goldilocks giải thích điều này.
wurtel

1

Việc mua là argv[0]có thể được đặt thành bất cứ điều gì (bao gồm NULL). Theo quy ước , argv[0]sẽ được đặt thành đường dẫn mà tệp thực thi được bắt đầu là (bởi quá trình shell khi nó thực hiện execve()).

Nếu ./foodir/barlà hai liên kết khác nhau (cứng hoặc tượng trưng) cho cùng một tệp thực thi, thì khởi động chương trình từ trình bao bằng hai đường dẫn sẽ được đặt argv[0]thành ./foodir/bar, tương ứng.

Thực tế argv[0]có thể NULLthường bị bỏ qua. NULL argv[0]Ví dụ, đoạn mã sau có thể bị sập (ví dụ: glibc in thứ gì đó như <null> thay cho argv[0]):

if (argc != 3) {
    fprintf(stderr, "%s: expected 2 arguments\n", argv[0]);
    exit(EXIT_FAILURE);
}

Một thay thế trên Linux là sử dụng /proc/self/execho các trường hợp như vậy.


làm thế nào bạn có thể đặt argv [0] thành cả ./foo và dir / bar
munjal007

@ munjal007 Tôi xin lỗi nếu tôi không rõ ràng. Tôi có nghĩa là chạy chương trình hai lần: một lần ./foovà một lần như dir/bar. argv[0]sẽ khác nhau cho hai trường hợp đó (trong mỗi trường hợp, nó sẽ giống với đường dẫn bạn đã sử dụng).
Ulfalizer

@ munjal007 Điều đó giả sử rằng bạn chạy nó từ vỏ tất nhiên. Vấn đề là bạn có thể thiết lập argv[0]bất cứ điều gì khi bạn exec*()tự lập trình. Đó là một quy ước của trình bao để đặt thành argv[0]đường dẫn được sử dụng để bắt đầu chương trình (và thật khôn ngoan khi bạn exec*()làm chương trình, vì nhiều chương trình kiểm tra argv[0]và mong đợi nó giữ đường dẫn).
Ulfalizer
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.