ทำไมเราต้องส่งชื่อไฟล์สองครั้งในฟังก์ชั่น exec


12

ผมอ่านการเขียนโปรแกรมในสภาพแวดล้อมระบบปฏิบัติการยูนิกซ์ขั้นสูงโดยสตีเว่น, 8 THบท ฉันอ่านและเข้าใจทั้งหกฟังก์ชั่นของ exec

สิ่งหนึ่งที่ฉันสังเกตเห็นคือในทุกฟังก์ชั่น exec:

  • อาร์กิวเมนต์แรกคือชื่อไฟล์ / ชื่อพา ธ (ขึ้นอยู่กับฟังก์ชั่น exec)
  • อาร์กิวเมนต์ที่สองคือ argv [0] ที่เราเข้าไปmain()ซึ่งเป็นชื่อไฟล์เอง

ดังนั้นที่นี่เราต้องส่งชื่อไฟล์สองครั้งในฟังก์ชั่น

มีเหตุผลอะไรบ้าง (เช่นเราไม่สามารถรับชื่อไฟล์จากชื่อพา ธ จากอาร์กิวเมนต์แรก)?

คำตอบ:


15

ดังนั้นที่นี่เราต้องส่งชื่อไฟล์สองครั้งในฟังก์ชั่น

พวกเขาไม่เหมือนกับที่คุณสังเกตเห็นโดยสังเกตว่าหนึ่งในนั้นถูกใช้เป็นargv[0]ค่า สิ่งนี้ไม่จำเป็นต้องเหมือนกันกับชื่อฐานของไฟล์สั่งการ หลาย ๆ สิ่งส่วนใหญ่ไม่สนใจมันและคุณสามารถใส่อะไรก็ได้ที่คุณต้องการ

คนแรกคือเส้นทางที่แท้จริงไปยังปฏิบัติการที่มีความจำเป็นที่ชัดเจน คนที่สองจะถูกส่งผ่านไปยังกระบวนการอย่างเห็นได้ชัดเป็นชื่อที่ใช้ในการเรียกมัน แต่เช่น:

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

จะทำงานได้ดีสันนิษฐานว่า/bin/lsเป็นเส้นทางที่ถูกต้อง

บางโปรแกรมทำ argv[0]แต่ทำให้การใช้งานของ เหล่านี้มักจะมีหนึ่งหรือมากกว่า symlinks ใน$PATH; นี่เป็นเรื่องปกติที่มีอรรถประโยชน์การบีบอัด (บางครั้งพวกเขาใช้ shell wrappers แทน) หากคุณxzติดตั้งแล้วstat $(which xzcat)แสดงว่าเป็นลิงก์ไปยังxzและman xzcatเหมือนกับman xzที่อธิบาย "xzcat เทียบเท่ากับ xz --decompress --stdout" วิธีที่ xz สามารถบอกได้ว่ามันถูกเรียกใช้โดยการตรวจสอบargv[0]ทำให้เทียบเท่าเหล่านี้:

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

5
อ่านี่จะอธิบายได้อย่างไรbusyboxว่าคุณต้องการให้มันเป็นอย่างไรขึ้นอยู่กับว่าคุณเรียกมันว่าถูกต้องอย่างไร?
terdon

4
@terdon นั้นเป็นวิธีที่ไบนารีเดียวสำหรับ busybox เป็นไปตามคำสั่งที่แตกต่างกันมากมาย
mah

7
ซึ่งหมายความว่าถ้า/bin/lsเป็น busybox มันจะไม่รู้วิธีดำเนินการbanana!
Riking

6

คุณไม่ต้องส่งชื่อไฟล์สองครั้ง

ไฟล์แรกคือไฟล์ที่ถูกเรียกใช้งานจริง

อาร์กิวเมนต์ที่สองคือสิ่งที่ควรเป็นargv[0]ของกระบวนการเช่นสิ่งที่กระบวนการควรเห็นเป็นชื่อ เช่นถ้าคุณเรียกใช้lsจากเปลือกอาร์กิวเมนต์แรกเป็นที่สองเป็นเพียง/bin/lsls

คุณสามารถเรียกใช้ไฟล์บางไฟล์และเรียกมันอย่างอื่นผ่านทางอาร์กิวเมนต์ที่สอง โปรแกรมสามารถตรวจสอบชื่อและทำงานแตกต่างกันไปตามชื่อ สิ่งนี้สามารถทำได้ผ่านการเชื่อมโยงอย่างหนัก (หรือลิงก์สัญลักษณ์) แต่วิธีนี้ให้ความยืดหยุ่นมากขึ้น


ในความเป็นจริงการเชื่อมโยงเป็นวิธีการเดียวกันตั้งแต่ที่ตั้งargv[0]เป็นชื่อลิงค์
goldilocks

ในย่อหน้าสุดท้าย "คุณสามารถเรียกใช้ไฟล์บางไฟล์และเรียกมันอย่างอื่นผ่านทางอาร์กิวเมนต์ที่สองโปรแกรมสามารถตรวจสอบชื่อของมันและทำงาน 'แตกต่าง' ตามชื่อ" คุณช่วยกรุณาอธิบายเพิ่มเติมหรือให้ฉันอ่านบางฉันใหม่กับสภาพแวดล้อมนี้
munjal007

ส่วนสุดท้ายของคำตอบของ goldilocks จะอธิบายสิ่งนี้
wurtel

1

Takeaway ที่argv[0]สามารถตั้งค่าเป็นอะไรก็ได้ (รวมถึงNULL) โดยการประชุม , argv[0]จะถูกตั้งค่าไปยังเส้นทางที่ปฏิบัติการเริ่มต้นเป็น (โดยกระบวนการเปลือกเมื่อมันไม่execve())

หาก./fooและdir/barเป็นสองลิงก์ที่แตกต่างกัน (ฮาร์ดหรือสัญลักษณ์) ไปยังไฟล์เรียกทำงานเดียวกันการเริ่มต้นโปรแกรมจากเชลล์โดยใช้สองพา ธ จะถูกตั้งค่าargv[0]เป็น./fooและdir/barตามลำดับ

ความจริงที่ว่าargv[0]อาจจะNULLเป็นมักจะมองข้าม รหัสต่อไปนี้อาจมีปัญหาสำหรับNULL argv[0]ตัวอย่าง (แม้ว่า glibc พิมพ์บางอย่างเช่น<null>แทนargv[0]):

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

ทางเลือกอื่นบน Linux คือใช้/proc/self/exeสำหรับกรณีเช่นนี้


คุณสามารถตั้งค่า argv [0] เป็น. /foo และ dir / bar ได้อย่างไร
munjal007

@ munjal007 ฉันขอโทษถ้าฉันไม่ชัดเจน ผมหมายถึงการใช้โปรแกรมครั้งที่สอง: ครั้งเดียวและครั้งเดียว./foo จะแตกต่างกันสำหรับทั้งสองกรณี (ในแต่ละกรณีมันจะเหมือนกับเส้นทางที่คุณใช้) dir/barargv[0]
Ulfalizer

@ munjal007 นั่นคือสมมติว่าคุณเรียกใช้จากเชลล์แน่นอน ประเด็นก็คือคุณสามารถตั้งค่าargv[0]เป็นอะไรก็ได้เมื่อคุณexec*()โปรแกรมด้วยตัวเอง มันเป็นแบบแผนของเชลล์ที่จะตั้งค่าargv[0]พา ธ ที่ใช้ในการเริ่มโปรแกรม (และก็ควรที่จะทำเช่นเดียวกันเมื่อคุณexec*()โปรแกรมเนื่องจากโปรแกรมหลายโปรแกรมตรวจสอบargv[0]และคาดว่ามันจะเก็บพา ธ )
Ulfalizer
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.