วิธีการ“ ถูกต้อง” เริ่มโปรแกรมประยุกต์จากเปลือก


21

ฉันพบว่ามันยากที่จะตอบคำถามอย่างแม่นยำ แต่ฉันจะทำให้ดีที่สุด ฉันใช้dwmเป็นตัวจัดการหน้าต่างเริ่มต้นและdmenuเป็นตัวเรียกใช้แอปพลิเคชันของฉัน ฉันแทบจะไม่ได้ใช้แอปพลิเคชัน GUI นอกเหนือจากเบราว์เซอร์ของฉัน งานส่วนใหญ่ของฉันทำได้โดยตรงจากบรรทัดคำสั่ง นอกจากนี้ฉันเป็นแฟนตัวยงของความเรียบง่ายเกี่ยวกับระบบปฏิบัติการแอปพลิเคชั่นและอื่น ๆ หนึ่งในเครื่องมือที่ฉันไม่เคยได้รับคือตัวเปิดแอปพลิเคชัน สาเหตุหลักมาจากฉันขาดความเข้าใจที่แม่นยำในการทำงานของโปรแกรมเรียกใช้งาน / สิ่งที่พวกเขาทำ แม้การค้นหาทางอินเทอร์เน็ตที่ครอบคลุมจะแสดงคำอธิบายที่คลุมเครือเท่านั้น สิ่งที่ฉันต้องการจะทำคือกำจัดแม้กระทั่งตัวเรียกใช้งานแอปพลิเคชันของฉันเพราะนอกเหนือจากการวางไข่แอปพลิเคชันจริง ๆ แล้วฉันไม่ได้ใช้มันอย่างแน่นอน ในการทำเช่นนี้ฉันอยากจะรู้วิธีการ "เริ่มต้น" แอปพลิเคชันจากเปลือก โดยที่ความหมายของ "ถูกต้อง" สามารถประมาณได้โดย "เหมือนตัวเรียกใช้งานแอปพลิเคชันจะทำ"

ฉันรู้เกี่ยวกับวิธีต่อไปนี้เพื่อวางไข่กระบวนการจากเปลือก:

  1. exec /path/to/Program แทนที่เชลล์ด้วยคำสั่งที่ระบุโดยไม่ต้องสร้างกระบวนการใหม่
  2. sh -c /path/to/Program เรียกใช้กระบวนการที่ขึ้นกับเชลล์
  3. /path/to/Program เรียกใช้กระบวนการที่ขึ้นกับเชลล์
  4. /path/to/Program 2>&1 & เรียกใช้กระบวนการอิสระเชลล์
  5. nohup /path/to/Program & เรียกใช้กระบวนการเชลล์อิสระและเปลี่ยนทิศทางเอาต์พุตไปที่ nohup.out

อัปเดต 1: ฉันสามารถแสดงให้เห็นว่าอะไรเช่นdmenuสร้างใหม่จากการโทรซ้ำไปยังps -eflเงื่อนไขที่แตกต่างกัน มัน spawns เปลือกใหม่และเป็นลูกของนี้เปลือกแอพลิเคชัน/bin/bash /path/to/Programตราบใดที่เด็กยังอยู่ใกล้ ๆ เปลือกหอยจะอยู่รอบ ๆ (วิธีจัดการเรื่องนี้มันเกินกว่าฉัน ... ) ในทางตรงกันข้ามถ้าคุณออกnohup /path/to/Program &จากเชลล์/bin/bashโปรแกรมจะกลายเป็นลูกของเชลล์นี้ แต่ถ้าคุณออกจากเชลล์นี้ผู้ปกครองของโปรแกรมจะเป็นกระบวนการที่สูงที่สุด ดังนั้นหากกระบวนการแรกเป็นเช่น/sbin/init verboseและมีPPID 1แล้วมันจะเป็นผู้ปกครองของโปรแกรม นี่คือสิ่งที่ฉันพยายามอธิบายโดยใช้กราฟ: chromiumเปิดตัวผ่านdmenuและfirefoxเปิดตัวโดยใช้exec firefox & exit:

systemd-+-acpid
        |-bash---chromium-+-chrome-sandbox---chromium-+-chrome-sandbox---nacl_helper
        |                 |                           `-chromium---5*[chromium-+-{Chrome_ChildIOT}]
        |                 |                                                    |-{Compositor}]
        |                 |                                                    |-{HTMLParserThrea}]
        |                 |                                                    |-{OptimizingCompi}]
        |                 |                                                    `-3*[{v8:SweeperThrea}]]
        |                 |-chromium
        |                 |-chromium-+-chromium
        |                 |          |-{Chrome_ChildIOT}
        |                 |          `-{Watchdog}
        |                 |-{AudioThread}
        |                 |-3*[{BrowserBlocking}]
        |                 |-{BrowserWatchdog}
        |                 |-5*[{CachePoolWorker}]
        |                 |-{Chrome_CacheThr}
        |                 |-{Chrome_DBThread}
        |                 |-{Chrome_FileThre}
        |                 |-{Chrome_FileUser}
        |                 |-{Chrome_HistoryT}
        |                 |-{Chrome_IOThread}
        |                 |-{Chrome_ProcessL}
        |                 |-{Chrome_SafeBrow}
        |                 |-{CrShutdownDetec}
        |                 |-{IndexedDB}
        |                 |-{LevelDBEnv}
        |                 |-{NSS SSL ThreadW}
        |                 |-{NetworkChangeNo}
        |                 |-2*[{Proxy resolver}]
        |                 |-{WorkerPool/1201}
        |                 |-{WorkerPool/2059}
        |                 |-{WorkerPool/2579}
        |                 |-{WorkerPool/2590}
        |                 |-{WorkerPool/2592}
        |                 |-{WorkerPool/2608}
        |                 |-{WorkerPool/2973}
        |                 |-{WorkerPool/2974}
        |                 |-{chromium}
        |                 |-{extension_crash}
        |                 |-{gpu-process_cra}
        |                 |-{handle-watcher-}
        |                 |-{inotify_reader}
        |                 |-{ppapi_crash_upl}
        |                 `-{renderer_crash_}
        |-2*[dbus-daemon]
        |-dbus-launch
        |-dhcpcd
        |-firefox-+-4*[{Analysis Helper}]
        |         |-{Cache I/O}
        |         |-{Cache2 I/O}
        |         |-{Cert Verify}
        |         |-3*[{DOM Worker}]
        |         |-{Gecko_IOThread}
        |         |-{HTML5 Parser}
        |         |-{Hang Monitor}
        |         |-{Image Scaler}
        |         |-{JS GC Helper}
        |         |-{JS Watchdog}
        |         |-{Proxy R~olution}
        |         |-{Socket Thread}
        |         |-{Timer}
        |         |-{URL Classifier}
        |         |-{gmain}
        |         |-{localStorage DB}
        |         |-{mozStorage #1}
        |         |-{mozStorage #2}
        |         |-{mozStorage #3}
        |         |-{mozStorage #4}
        |         `-{mozStorage #5}
        |-gpg-agent
        |-login---bash---startx---xinit-+-Xorg.bin-+-xf86-video-inte
        |                               |          `-{Xorg.bin}
        |                               `-dwm-+-dwmstatus
        |                                     `-xterm---bash-+-bash
        |                                                    `-pstree
        |-systemd---(sd-pam)
        |-systemd-journal
        |-systemd-logind
        |-systemd-udevd
        |-wpa_actiond
        `-wpa_supplicant

อัปเดต 2: ฉันเดาว่าคำถามนี้สามารถถูกต้มลงไปที่: สิ่งที่ควรเป็น parent ของกระบวนการ ควรเช่นเป็นเชลล์หรือควรเป็นinitกระบวนการเช่นกระบวนการด้วยPID 1หรือไม่


3
คำตอบสั้น ๆ สำหรับคำถามของคุณคือ "อะไรก็ได้ที่คุณต้องการผลลัพธ์"
Wayne Werner

1
แดงขยะ - คุณถามคำถามที่ดี แต่ฉันคิดว่าเวย์นอยู่ที่จมูกนี่ - การแก้ไขล่าสุดของคุณถามถึงinit- คำตอบที่อาจจะเป็น ... อาจจะ? มันขึ้นอยู่กับว่าคุณวางแผนที่จะพูดคุยกับมันสิ่งที่initคุณใช้และช่องทางข้อมูล โดยทั่วไปแล้วสิ่งต่าง ๆ จะมีแนวโน้มที่จะทำงานออกเอง - นั่นคือสิ่งที่initมีไว้สำหรับ ในกรณีใด ๆ โดยปกติเมื่อคุณ daemonize initกระบวนการแล้ว หรือถ้าคุณต้องการควบคุมงานเชลล์ปัจจุบัน
mikeserv

ฮ่าฮ่าฮ่าไชโย @mikeserv; 4:37 น. ในตอนเช้าที่นี่แล้วหัวเราะครั้งแรกของวัน จริงอยู่สิ่งนั้นจะได้ผลเสมอ ฉันจะลบdmenuและดูว่าฉันได้รับพร้อมกับสิ่งที่ฉันเรียนรู้ ฉันค้นหาexec /path/to/Program & exitหรือ/bin/bash -c /path/to/Program & exitใช้งานได้ค่อนข้างมาก แต่พวกเขาทั้งหมดทำ1คือinitผู้ปกครองของProgramที่ดีกับฉันตราบเท่าที่นี้เหมาะสมและไม่ละเมิด*nixหลักการพื้นฐานใด ๆ
lord.garbage

@ lord.garbage - นั่นเป็นเพราะคุณexec &ฉันคิดว่า ฉันมักจะเพียงแค่ทำสิ่งที่ฉันจากสถานี ... บางทีคุณอาจจะได้รับการใช้งานบางส่วนออกจากเบนเวลล์ของคำถามที่นี่ ฉันมีคำตอบอยู่ที่นั่น แต่พวกเขาทั้งหมดดีมาก อย่างไรก็ตามเมื่อคุณทำกระบวนการและผู้ปกครองของมันตายตัว: sh -c 'cat & kill $$'คุณกำพร้าและกระบวนการจะได้รับการเก็บเกี่ยวในที่สุด นั่นคืองานของ init - นั่นเป็นสาเหตุว่าทำไมพวกเขาถึงล้มเหลว
mikeserv

systemd--bash--chromiumอาจจะเป็นคำถามที่ง่ายสำหรับตอนนี้คือมันเป็นวิธีการที่เป็นไปได้ที่จะได้รับต้นกระบวนการดังกล่าวจากเปลือก: ทุกวิธีที่ฉันพยายามจะนำไปสู่ต้นไม้กระบวนการในรูปแบบต่อไปนี้systemd--chromiumเมื่อฉันวางไข่ Firefox จากเปลือก เปลือกปีศาจถูกทำลายที่นี่ได้อย่างไร? มันไม่ได้เกี่ยวข้องกับขั้วใด ๆ
lord.garbage

คำตอบ:


7

ดูเหมือนว่าคุณจะมีความเข้าใจที่ดี เพื่อชี้แจงสิ่งที่คุณมี

  • sh -c /path/to/Program ค่อนข้างคล้ายกับ

    $ sh 
    % / path / to / Program 
    % Ctrl+ D                             (หรือคุณสามารถพิมพ์“ exit ”) 
    $

    โดยที่คุณเริ่มกระบวนการเชลล์ใหม่ระบุพา ธ คำสั่งของแอปพลิเคชันไปยังเชลล์ใหม่จากนั้นให้เชลล์ใหม่ยกเลิก ฉันได้แสดงให้เห็นว่าเปลือกใหม่ให้พรอมต์ที่แตกต่างกันเพื่อวัตถุประสงค์ในการภาพประกอบ; สิ่งนี้อาจจะไม่เกิดขึ้นในชีวิตจริง สร้างส่วนใหญ่มีประโยชน์สำหรับการทำสิ่งที่ยุ่งยากเช่นการตัดคำสั่งหลายมัดเพื่อให้พวกเขามีลักษณะเหมือนคำสั่งเดียว (ประเภทเดียวใช้งานสคริปต์ไม่มีชื่อ) หรือการสร้างคำสั่งที่ซับซ้อนอาจจะมาจากตัวแปรเปลือก คุณแทบจะไม่เคยใช้มันเพียงแค่เรียกใช้โปรแกรมเดียวที่มีอาร์กิวเมนต์ง่าย ๆsh -c "command"

  • 2>&1หมายถึงการเปลี่ยนเส้นทางข้อผิดพลาดมาตรฐานไปยังเอาต์พุตมาตรฐาน นี้ไม่ได้จริงๆมีมากจะทำอย่างไรกับ&; แต่คุณใช้เมื่อคำสั่งส่งข้อความแสดงข้อผิดพลาดไปยังหน้าจอแม้ว่าคุณจะพูด และคุณต้องการที่จะจับข้อความผิดพลาดในไฟล์command > file
  • เปลี่ยนเส้นทางออกไปเป็นที่น่ารำคาญผลข้างเคียงของnohup.out nohupจุดประสงค์หลักของ การเรียกใช้แบบอะซิงโครนัส (ที่รู้จักกันทั่วไปว่า“ ในพื้นหลัง” หรือเป็น“ กระบวนการอิสระของเชลล์” เพื่อใช้คำพูดของคุณ) และกำหนดค่าให้มันมีโอกาสที่ดีกว่า ยุติเชลล์ (เช่นออกจากระบบ) ในขณะที่คำสั่งยังคงทำงานอยู่nohup command &command

bash(1)และคู่มืออ้างอิง Bash เป็นแหล่งข้อมูลที่ดี


7

มีวิธีสองสามวิธีในการเรียกใช้โปรแกรมและแยกออกจากเทอร์มินัล หนึ่งคือการเรียกใช้มันในพื้นหลังของ subshellเช่นนี้ (แทนที่firefoxด้วยโปรแกรมที่คุณชื่นชอบ):

(firefox &)

อีกประการหนึ่งคือการปฏิเสธกระบวนการ:

firefox & disown firefox

หากคุณอยากรู้เกี่ยวกับวิธีกล app ที่ทำงานdmenuให้ 1 ไบนารีและ 2 เชลล์สคริปต์: dmenu, dmenu_pathและdmenu_runตามลำดับ

dmenu_runท่อส่งออกของdmenu_pathเป็น dmenu ซึ่งจะเปลี่ยนท่อเป็นสิ่งที่$SHELLตัวแปรของคุณตั้งค่าเป็น /bin/shถ้ามันเป็นความว่างเปล่าก็จะใช้

#!/bin/sh
dmenu_path | dmenu "$@" | ${SHELL:-"/bin/sh"} &

dmenu_pathค่อนข้างซับซ้อนกว่าเล็กน้อย แต่ในระยะสั้นจะแสดงรายการไบนารีใน$PATHตัวแปรสภาพแวดล้อมของคุณและใช้แคชหากเป็นไปได้

#!/bin/sh
cachedir=${XDG_CACHE_HOME:-"$HOME/.cache"}
if [ -d "$cachedir" ]; then
        cache=$cachedir/dmenu_run
else
        cache=$HOME/.dmenu_cache # if no xdg dir, fall back to dotfile in ~
fi
IFS=:
if stest -dqr -n "$cache" $PATH; then
        stest -flx $PATH | sort -u | tee "$cache"
else
        cat "$cache"
fi

ไม่จำเป็นต้องมีโปรแกรมที่ทำงานในเชลล์ อีกวิธีในการเขียนdmenu_runโดยไม่ต้องไพพ์ลงในเชลล์คือ:

#!/bin/sh
$(dmenu_path | dmenu "$@") &

6

ฉันชอบคำตอบของ G-Man มาก แต่ฉันกำลังตอบกลับเพราะฉันคิดว่าคุณกำลังทำให้เกิดความสับสน ดังที่เวย์นชี้ให้เห็นคำตอบที่ดีที่สุดคือ "อะไรก็ได้ที่คุณต้องการผลลัพธ์"

ในการจัดการกระบวนการ Unix ทุกกระบวนการมีผู้ปกครอง ข้อยกเว้นหนึ่งข้อสำหรับเรื่องนี้คือinitกระบวนการที่เริ่มต้นโดยระบบปฏิบัติการเมื่อบูต เป็นพฤติกรรมปกติสำหรับกระบวนการพาเรนต์ที่จะใช้กระบวนการลูกทั้งหมดของมันเมื่อมันตาย สิ่งนี้ทำได้โดยการส่งสัญญาณ SIGHUP ไปยังกระบวนการลูกทั้งหมด การจัดการเริ่มต้นของ SIGHUP จะยุติกระบวนการ

การวางไข่เชลล์ของกระบวนการผู้ใช้ไม่แตกต่างไปจากถ้าคุณเขียนรหัสfork (2) / exec (3)ในภาษาที่คุณเลือก เชลล์คือพาเรนต์ของคุณและหากเชลล์ยกเลิก (เช่นคุณออกจากระบบ) กระบวนการของลูกนั้นจะดำเนินการตามไปด้วย ความแตกต่างที่คุณอธิบายเป็นเพียงวิธีการปรับเปลี่ยนพฤติกรรมนั้น

exec /path/to/programเป็นเหมือนการเรียกexec (3) ใช่มันจะแทนที่เชลล์ของคุณโดยprogramไม่ให้ผู้ปกครองเปิดใช้เชลล์

sh -c /path/to/programชนิดของ pointless นั้นสร้าง child child process ที่จะสร้าง child process programขึ้นมา มันมีค่า/path/to/programก็ต่อเมื่อเป็นลำดับของคำสั่งสคริปต์จริง ๆเท่านั้นไม่ใช่ไฟล์ที่เรียกใช้งานได้ ( sh /path/to/script.shสามารถใช้เพื่อเรียกใช้เชลล์สคริปต์ที่ไม่มีสิทธิ์ดำเนินการในเชลล์ที่ด้อยกว่า)

/path/to/programสร้างกระบวนการ "เบื้องหน้า" ซึ่งหมายความว่าเชลล์รอให้กระบวนการเสร็จสิ้นก่อนที่จะดำเนินการอื่น ๆ ในบริบทเรียกระบบก็เหมือนส้อม (2) / exec (3) / waitpid (2) โปรดทราบว่าเด็กสืบทอด stdin / stdout / stderr จากผู้ปกครอง

/path/to/program &(ไม่สนใจการเปลี่ยนเส้นทาง) สร้าง "กระบวนการพื้นหลัง" กระบวนการนี้ยังคงเป็นลูกของเชลล์ แต่พาเรนต์ไม่ได้รอให้ยุติ

nohup /path/to/programเรียกใช้nohup (1)เพื่อป้องกันการส่ง SIGHUP ไปยังprogramถ้าเทอร์มินัลการควบคุมถูกปิด ไม่ว่าจะเป็นในเบื้องหน้าหรือพื้นหลังเป็นตัวเลือก (แม้ว่าส่วนใหญ่แล้วกระบวนการจะเป็นพื้นหลัง) โปรดทราบว่าnohup.outเป็นเพียงเอาต์พุตถ้าคุณไม่เปลี่ยนเส้นทาง stdout

เมื่อคุณวางกระบวนการในพื้นหลังหากกระบวนการหลักตายกระบวนการหนึ่งในสองสิ่งเกิดขึ้น หากผู้ปกครองเป็นสถานีควบคุมแล้ว SIGHUP จะถูกส่งไปที่เด็ก ๆ หากไม่เป็นเช่นนั้นกระบวนการนั้นอาจเป็น "orphaned" และสืบทอดมาจากinitกระบวนการ

เมื่อคุณเปลี่ยนเส้นทางอินพุต / เอาท์พุต / ข้อผิดพลาดคุณเพียงแค่เชื่อมต่อไฟล์ descriptors ที่ทุกกระบวนการมีไฟล์ต่างจากที่มันสืบทอดมาจากพาเรนต์ สิ่งนี้ไม่ส่งผลกระทบต่อความเป็นเจ้าของกระบวนการหรือความลึกของต้นไม้ (แต่มันก็เหมาะสมเสมอที่จะเปลี่ยนเส้นทางทั้ง 3 ออกจากเทอร์มินัลสำหรับกระบวนการพื้นหลัง)

จากทั้งหมดที่กล่าวมาฉันไม่คิดว่าคุณควรจะกังวลเกี่ยวกับการสร้างกระบวนการโดยเชลล์หรือ sub-shells หรือกระบวนการย่อยยกเว้นว่ามีปัญหาเฉพาะที่คุณจัดการกับการจัดการกระบวนการ


nitpick: sh -c /path/to/programจะไม่เรียกใช้โปรแกรมเป็นเชลล์สคริปต์หากไม่มีบิตที่สามารถเรียกใช้งานsh /path/to/programได้ sh -c /path/to/programจะเปิดเปลือกและเรียกใช้/path/to/programเป็นคำสั่งในเชลล์นั้นซึ่งจะล้มเหลวหากไม่สามารถเรียกใช้งานได้
filbranden

ถ้าเราเป็นคนวางยาเราก็ผิดทั้งคู่ sh -c /path/to/programอ่านคำสั่งจาก/path/to/programเป็นอินพุตไปยังเชลล์ ไม่ต้องการให้ไฟล์มีสิทธิ์ดำเนินการ แต่ควรเป็นเชลล์สคริปต์
jwm

อืมsh /path/to/programทำอย่างนั้น แค่ลองทำเอง: echo echo hello world >abc.shจากนั้นก็sh ./abc.shพิมพ์hello worldขณะที่sh -c ./abc.shพูดว่าsh: ./abc.sh: Permission denied(ซึ่งเหมือนกับว่าคุณเรียกใช้./abc.shในเชลล์ปัจจุบันโดยตรง) ฉันพลาดอะไรไปหรือเปล่า? (หรือบางทีฉันไม่ได้แสดงความคิดเห็นที่ดีในความคิดเห็นก่อนหน้านี้ ... )
filbranden

ความผิดของฉัน. sh -c _something_เหมือนกับการพิมพ์_something_ที่พรอมต์คำสั่งยกเว้นการวางไข่ของเชลล์ที่ด้อยกว่า ดังนั้นคุณถูกต้องว่ามันจะล้มเหลวหากไม่มีบิตรันไทม์ (เป็นไฟล์) ในทางกลับกันคุณสามารถให้ชุดคำสั่งเชลล์เช่นsh -c "echo hello world"และมันจะทำงานได้ดี ดังนั้นจึงไม่จำเป็นต้องให้สิ่งที่คุณพิมพ์มีบิตรันไทม์ (หรือแม้กระทั่งเป็นไฟล์) เฉพาะที่ล่ามเชลล์สามารถทำอะไรกับมันได้
jwm

ฉันเชื่อว่า OP หมายถึงการคอมไพล์หรือเอ็กซีคิ้วท์ของระบบ, ดังนั้นการสันนิษฐานว่าจะอนุญาต
jwm
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.