Tôi có thể gửi một số văn bản tới STDIN của một quy trình đang hoạt động đang chạy trong phiên màn hình không?


69

Tôi có một quy trình máy chủ chạy dài bên trong một phiên màn hình trên máy chủ Linux của tôi. Nó hơi không ổn định (và đáng buồn là phần mềm của tôi không thể sửa được!), Vì vậy tôi muốn kịch bản khởi động lại hàng đêm của quy trình để giúp ổn định. Cách duy nhất để làm cho nó tắt máy một cách duyên dáng là vào quy trình màn hình, chuyển sang cửa sổ nó đang chạy và nhập chuỗi "dừng" trên bảng điều khiển của nó.

Có bất kỳ mâu thuẫn chuyển hướng thông minh nào tôi có thể làm để thực hiện một lệnh cronjob gửi lệnh dừng đó vào một thời điểm cố định mỗi ngày không?

Câu trả lời:


85

Câu trả lời này không giải quyết được vấn đề, nhưng nó bị bỏ lại ở đây vì hơn 30 người thấy nó hữu ích , nếu không tôi đã xóa nó từ lâu.

Viết thư cho /proc/*pid of the program*/fd/0. Thư fdmục con chứa các bộ mô tả của tất cả các tệp đã mở và bộ mô tả tệp 0là đầu vào tiêu chuẩn (1 là thiết bị xuất chuẩn và 2 là tiêu chuẩn).

Bạn có thể sử dụng điều này để xuất thông báo trên tty nơi chương trình đang chạy, mặc dù nó không cho phép bạn ghi vào chính chương trình.

Thí dụ

Nhà ga 1:

[ciupicri@hermes ~]$ cat
shows on the tty but bypasses cat

Nhà ga 2:

[ciupicri@hermes ~]$ pidof cat
7417
[ciupicri@hermes ~]$ echo "shows on the tty but bypasses cat" > /proc/7417/fd/0

3
@James Lawrie: sau đó hãy xem Proc (5)Proc.txt .
Cristian Ciupitu

2
+2 cho dù bạn nghĩ bạn biết bao nhiêu, vẫn luôn có nhiều thứ để học :) lắt léo.
troyengel

3
Mặc dù vậy, hãy lưu ý rằng Proc fd chỉ chuyển hướng đến những gì được sử dụng làm nguồn stdin. Trong ví dụ của bạn, nếu bạn nhập một cái gì đó vào thiết bị đầu cuối 1, nó sẽ in lại (nó được gửi cho mèo stdin và mèo in nó), do đó dẫn đến việc bạn nhìn thấy nó hai lần. Mặt khác, nếu bạn gửi một cái gì đó đến fd / 0, nó sẽ được gửi đến bàn điều khiển nhưng không gửi cho mèo, và do đó chỉ được hiển thị một lần. Vì mèo chỉ đơn giản là in lại đầu ra với ví dụ này, bạn thực sự không thể biết liệu đầu vào hoặc đầu ra của bạn có được in hay không, do đó, quan niệm sai lầm này. / fd / 0 điểm đến bàn điều khiển / pts; thấy ls -l /proc/7417/fd/0.
Kissaki

4
ví dụ thực tế: Tôi đã bắt đầu gphoto2 --get-all-files và nó yêu cầu xác nhận 100 lần. Khi tôi lặp lại "y"> / Proc / PID / fd / 0, gphoto2 không tiếp tục, tuy nhiên, "y" được in trong thiết bị đầu cuối.
Thorsten Staerk

2
@ThorstenStaerk, tôi biết, đó là lý do tại sao tôi thêm ghi chú đó. Bạn chỉ ghi vào tệp thiết bị tương ứng với thiết bị đầu cuối mà gphoto2 chạy (ví dụ /dev/pts/19), yký tự không tiếp cận ứng dụng. Nó tương tự như những gì xảy ra khi bạn sử dụng lệnh write (1) . Dù sao, hãy thử câu trả lời khác của tôi hoặc một công cụ tự động hóa đồ họa như xdotool .
Cristian Ciupitu

36

Giải pháp dựa trên màn hình

Khởi động máy chủ như thế này:

# screen -d -m -S ServerFault tr a-z A-Z # replace with your server

màn hình sẽ bắt đầu ở chế độ tách rời, vì vậy nếu bạn muốn xem những gì đang diễn ra, hãy chạy:

# screen -r ServerFault

Điều khiển máy chủ như thế này:

# screen -S ServerFault -p 0 -X stuff "stop^M"
# screen -S ServerFault -p 0 -X stuff "start^M"
# screen -S ServerFault -p 0 -X stuff "^D" # send EOF

(câu trả lời này dựa trên việc gửi văn bản nhập vào màn hình tách rời từ trang web anh chị em Unix & Linux )

Giải thích về các tham số:

-d -m
   Start screen in "detached" mode. This creates a new session but doesn't
   attach to it.  This is useful for system startup scripts.
-S sessionname
   When creating a new session, this option can be used to specify a meaningful
   name for the session.
-r [pid.tty.host]
-r sessionowner/[pid.tty.host]
   resumes a detached screen session.
-p number_or_name|-|=|+
   Preselect a window. This is useful when you want to reattach to a specific
   window or you want to send a command via the "-X" option to a specific
   window.
-X
   Send the specified command to a running screen session e.g. stuff.

thứ [chuỗi]

   Stuff the string string in the input  buffer of the current window.
   This is like the "paste" command but with much less overhead.  Without
   a parameter, screen will prompt for a string to stuff.

giải pháp dựa trên tmux

Khởi động máy chủ như thế này:

# tmux new-session -d -s ServerFault 'tr a-z A-Z' # replace with your server

tmux sẽ bắt đầu ở chế độ tách rời, vì vậy nếu bạn muốn xem những gì đang diễn ra, hãy chạy:

# tmux attach-session -t ServerFault

Điều khiển máy chủ như thế này:

# tmux send-keys -t ServerFault -l stop
# tmux send-keys -t ServerFault Enter
# tmux send-keys -t ServerFault -l start
# tmux send-keys -t ServerFault Enter
# tmux send-keys -t ServerFault C-d # send EOF

Giải thích về các tham số:

 new-session [-AdDP] [-c start-directory] [-F format] [-n window-name] [-s
         session-name] [-t target-session] [-x width] [-y height]
         [shell-command]
         Create a new session with name session-name.

         The new session is attached to the current terminal unless -d is
         given.  window-name and shell-command are the name of and shell
         command to execute in the initial window.  If -d is used, -x and
         -y specify the size of the initial window (80 by 24 if not
         given).

 send-keys [-lR] [-t target-pane] key ...
               (alias: send)
         Send a key or keys to a window.  Each argument key is the name of
         the key (such as `C-a' or `npage' ) to send; if the string is not
         recognised as a key, it is sent as a series of characters.  The
         -l flag disables key name lookup and sends the keys literally.

4

Hãy thử điều này để bắt đầu:

# screen
# cd /path/to/wd
# mkfifo cmd
# my_cmd <cmd
C-A d

Và điều này để giết:

# cd /path/to/wd
# echo "stop" > cmd
# rm cmd

3
Điều này là tốt, nhưng nó có thể có nhược điểm là không thể gửi các lệnh khác trong khi chương trình đang chạy. Nếu chương trình dừng khi chạm EOF trên stdin thì vào lần đầu tiên echo "xxx" > cmdchương trình sẽ dừng (vì đường ống sẽ bị đóng). Mặc dù một số chương trình đủ thông minh để mở lại ( rewind(3)) stdin của họ khi gặp EOF.
Cristian Ciupitu

2

Có thể gửi văn bản đầu vào cho một quy trình đang chạy mà không cần chạy screentiện ích hoặc bất kỳ tiện ích ưa thích nào khác. Và nó có thể được thực hiện bằng cách gửi văn bản đầu vào này đến "tệp" đầu vào tiêu chuẩn /proc/PID#/fd/0.

Tuy nhiên, văn bản đầu vào cần phải được gửi theo một cách đặc biệt để được đọc theo quy trình. Gửi văn bản đầu vào thông qua writephương thức tệp thông thường sẽ không khiến quá trình nhận văn bản. Điều này là do làm như vậy sẽ chỉ nối vào "tệp" đó, nhưng sẽ không kích hoạt quá trình đọc byte.

Để kích hoạt quá trình đọc các byte, cần phải thực hiện một IOCTLthao tác loại TIOCSTIcho mỗi byte đơn được gửi. Điều này sẽ đặt byte vào hàng đợi đầu vào tiêu chuẩn của quá trình.

Điều này được thảo luận ở đây với một số ví dụ trong C, Perl và Python:

https://unix.stackexchange.com/questions/48103/construct-a-command-by-pocking-a-opes-into-a-tty/48221

-

Vì vậy, để trả lời câu hỏi ban đầu được hỏi gần 9 năm trước, công việc định kỳ sẽ cần chạy một số tập lệnh / chương trình tiện ích nhỏ tương tự như các ví dụ mà mọi người đã viết cho câu hỏi khác, sẽ gửi chuỗi "stop \ n" cho quy trình máy chủ đó trong câu hỏi, bằng cách gửi từng byte trong số 5 byte thông qua IOCTLthao tác loại TIOCSTI.

Tất nhiên, điều này sẽ chỉ hoạt động trên các hệ thống hỗ trợ TIOCSTI IOCTLloại hoạt động (như Linux) và chỉ từ roottài khoản người dùng, vì các "tệp" bên dưới /proc/được "sở hữu" bởi root.


1

Trong trường hợp nó giúp được bất cứ ai:
Tôi gặp vấn đề tương tự, và vì quá trình tôi đang sử dụng không theo screenhoặc tmux, tôi phải thực hiện một cách tiếp cận khác.

Tôi gắn gdbvới xtermquá trình của tôi đã chạy trong, và sử dụng call write(5, "stop\n", 5)từ gdbghi vào mô tả chủ tập tin pty.
Tôi đã tìm ra bộ mô tả tệp nào để gửi dữ liệu đến bằng cách xem /proc/<pid>/fdliên kết đến /dev/ptmxrồi dùng thử và lỗi giữa hai tùy chọn (gửi chuỗi của tôi đến cả hai bộ mô tả tệp phù hợp dường như không gây hại).

BIÊN TẬP

Hóa ra xtermquy trình tôi đã đính kèm được sinh ra với spawn-new-terminal() xtermhành động từ khóa phím, và bộ ptmxmô tả tệp thứ hai mở chỉ đơn giản là quy trình ptmxcha mẹ xtermđã bị đóng.
Do đó, các cuộc gọi thử nghiệm và lỗi đã gửi đầu ra đến thiết bị đầu cuối khác đó.
Hầu hết các xtermquy trình không có hai ptmxmô tả tập tin.

EDIT KẾT THÚC

Điều này có hiệu quả gõ chuỗi đó vào thiết bị đầu cuối, và do đó đã gửi nó cùng với quá trình chạy dưới nó.

nb bạn có thể cần phải cho phép gắn vào một quy trình đang chạy với một cái gì đó như
sudo bash -c "echo 0 > /proc/sys/kernel/yama/ptrace_scope"


0

Vì tôi không thể nhận xét câu trả lời được chấp nhận nhất của Cristian Ciupitu (năm 2010), tôi phải đặt câu trả lời này vào một câu trả lời riêng:

Câu hỏi này đã được giải quyết trong chủ đề này: https://stackoverflow.com/questions/5374255/how-to-write-data-to-ex hiện- Processes-stdin-from-external- process

Nói ngắn gọn:

Bạn phải bắt đầu quá trình của mình với một đường ống cho stdin không chặn cũng không đóng khi đầu vào hiện tại được ghi thông qua. Điều này có thể được thực hiện bằng một vòng lặp vô tận đơn giản sẽ được dẫn đến quy trình được đề cập:

$ (while [ 1 ]; do sleep 1; done) | yourProgramToStart

Tôi có thể xác nhận rằng đây là cách khác nhau của krissi để mở một đường ống không hoạt động trong trường hợp của tôi. Các giải pháp hiển thị đã làm việc thay thế.

Sau đó, bạn có thể ghi vào tệp ... / fd / 0 của quy trình để gửi hướng dẫn đến nó. Hạn chế duy nhất là bạn cần chấm dứt quá trình bash cũng đang thực hiện vòng lặp vô tận sau khi máy chủ đã tắt.

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.