[HN Gopher] Ctrl-C ___________________________________________________________________ Ctrl-C Author : kcl Score : 188 points Date : 2022-08-05 11:26 UTC (1 days ago) (HTM) web link (kevinlawler.com) (TXT) w3m dump (kevinlawler.com) | ghoward wrote: | Surprisingly, it _is_ possible to do exactly what the author | wants. I know because I 've done it. However, it _is_ as | complicated as the author says it is. | | The project in question is my `bc` [1]. | | Until version 3.0.0 [2], it used a "yield" architecture: every | loop it could enter had a check for a signal. This got tedious, | so I decided to make the jump to instant-ish reset. | | I was lucky in several ways. First, `bc` is a really good program | to reset; you just stop it executing, wipe all data away, and ask | for more input with a blank slate. Second, it is single-threaded. | | Nevertheless, it was still really difficult, especially to have | no memory leaks. | | First, I had to learn how to use `sigsetjmp()` and | `siglongjmp()`. Yep, that was how I was going to do this. Once I | learned, I implemented a stack of `sigjmp_buf`'s. Then, when a | signal happens, each individual `sigjmp_buf` is used. This | allowed me to properly free memory on the way. | | In essence, if a function had allocated memory, then it would | push a `sigjmp_buf` on the stack, and then when a `siglongjmp()` | happened, execution would go to a label where that memory would | be freed before continuing the jump series. | | Then I implemented signal locks. It is safe to `siglongjmp()` out | of signal handler, as long as it didn't interrupt code that was | non-async-signal-safe. So I used signal locks for that, and when | "unlocking" the lock, it would check for a signal and jump. And | if the signal handler sees a lock, it just sets a flag and | returns. | | Then I had to go through my codebase and protect every bit of | non-async-signal-safe code with locks. It was tedious, but the | result is fantastic. | | Edit: I forgot to add that there is more information at [3] and | [4]. | | Nowadays, I'm working on a threaded build system, and when it | gets SIGINT, it sends a message to threads to stop as soon as | their children are done. If it receives a second, it just exits. | | So yeah, every application is different, but it _is_ possible. | | [1]: https://git.yzena.com/gavin/bc | | [2]: | https://git.yzena.com/gavin/bc/src/branch/master/NEWS.md#3-0... | | [3]: | https://git.yzena.com/gavin/bc/src/branch/master/manuals/dev... | | [4]: | https://git.yzena.com/gavin/bc/src/branch/master/manuals/dev... | [deleted] | nrabulinski wrote: | I don't think I've ever encountered a CLI application which I | couldn't kill with ^C other than defunct processes | vladvasiliu wrote: | vi? | hprotagonist wrote: | it's the prefix for user keybinds in emacs. | | it shows the current line number in nano. | | etc. | krallja wrote: | irb, python, bash, psql | rgbrgb wrote: | Would love if prompts fixed this so it was easy to implement in | my CLI app: https://github.com/terkelg/prompts/issues/252 | untitaker_ wrote: | I feel this. Signal handling in Python code is especially | complicated. I'm not even talking about multithreading here (not | like you get anything out of it anyway). | | Python registers POSIX signal handlers, that upon SIGTERM/SIGINT, | set a flag. Next time the interpreter loop runs, the flag is | checked before the next instruction is being run and the stack is | unwinded. | | When you call out to some C code, that C code may run for a long | time. During that time, there is no interpreter loop actually | looping. Therefore, all signals are ignored in principle while C | code runs. | | It's possible for Python code to become uninterruptible while it | is calling something like pthread_join. | | See https://stackoverflow.com/questions/39930722/how-do-i- | catch-... | | Then of course, you have that on top of all the other problems | mentioned by the blogpost. | actionfromafar wrote: | This explains so much. | hkgjjgjfjfjfjf wrote: | pdw wrote: | I'm confused. Which software is he talking about? I can't think | of any program screwing up Ctrl-C in a bad way. | quickthrower2 wrote: | I use Firebase emulator and man that likes to carry on running | (or limping?) after Ctrl-C alot, hogging the port so you need | to hunt it down and kill it before you can start it again. Both | on Linux and Windows. | | I think it is a Java (or more to the point JVM) program, not | sure if that has anything to do with it. In addition I believe | it is a lot of parallel programs running at once, or they could | be different threads. As there are lots of Firebase services it | needs to emulate. | M9HF8wwiaAdZKEZ wrote: | As far as I can tell, this appears to be confusing Ctrl+C | (SIGINT, which terminates a process, and is usually _not_ | restartable), with Ctrl+Z (SIGTSTP, which pauses a process, and | is thus restartable). | | The only software I can think of that could "restart" after a | Ctrl+C is usually daemons or other long-lived processes (which | already need to be able to "restart" after any kind of shutdown | and thus have significant amounts of code dedicated to | serializing and unserializing their internal state). | | TFA even goes so far as to talk about memory leaks - which are | completely irrelevant when your process is about to exit anyway! | drdec wrote: | > this appears to be confusing Ctrl+C (SIGINT, which terminates | a process, and is usually not restartable), | | Respectfully, you seem to be confusing SIGINT, which is an | interrupt signal and SIGTERM, which is a terminate signal. Many | processes interpret SIGINT in a way which is indistinguishable | from SIGTERM, but others do not (e.g. most REPLs). | wruza wrote: | This article is quite a roller coaster to get what it is about. | As far as I understand it, the author wants SIGINT to become | some sort of a universal "cancel" button which may or may not | exit a process, because the idea is to stop and rollback to a | nearest sensible restart point. E.g. an interactive disk | formatting tool may stop lenghty formatting on SIGINT but | wouldn't just exit. It would clean up the mess and return to | its menu where e.g. batch configuration happens, so a user | doesn't lose next steps. The author basically wants modern gui | features in console via signals. | mattarm wrote: | Yeah, and as others have pointed out already, many existing | interactive terminal programs handle SIGINT in this way. E.g. | programming language repls interrupt running code and return | to the top level prompt. E.g. mutt (SIGINT will cause mutt to | politely asks if you want to exit before doing so). | | I think of it this way: we have both SIGINT and SIGTERM for a | reason. One "interrupts" and the other "terminates" and there | are often good reasons to handle "interrupt" differently from | "terminate" -- at least in interactive programs. | krallja wrote: | No, you misread the article. | | Open a Ruby interpreter (`irb`). Type `i=0; loop { i += 1 }`. | Press Ctrl+C. | | * Irb is still running. | | * Your infinite loop has been stopped. | | Type `i`: | | * The REPL state preserved as much progress as it could when | you aborted the run. | | Now do the same thing in `sh`. Now `python`. Now `psql`. All | handle Ctrl+C in the way the article mentioned! | thayne wrote: | So what is an example of an application that doesn't do this, | that you would want to? | jillesvangurp wrote: | A lot of multi threaded server software handles ctr+c just fine. | A lot of Java based server software have a shutdown hook, which | is something that you can easily add to any jvm based program | because it is part of the standard library. If you use Spring | Boot, for example, it has one and it will start shutting down | your Spring Context and call any destroy functions on | DestroyingBean implementations, which is how you can add your own | shut down logic in Spring. | | Good explanation here of shutdown hooks: | https://www.baeldung.com/jvm-shutdown-hooks | xyzzy_plugh wrote: | This makes no sense. Ctrl-C is just a way to tell your terminal | to send a SIGINT signal to the current process. How that process | handles the signal is up to it! It's by definition ignorable, as | the author points out, but it's not rocket science to handle it | in a sane fashion even in a multi-threaded application. Modern | languages make this trivial. The author makes it sound like some | dark art but in reality you just have to read the manpages. | | SIGINT is really designed for interactive applications. Most | processes should simply treat it like a SIGTERM unless they have | some sort of REPL. Unless you need graceful shutdown, most | processes shouldn't mask either signal. If they do, the polite | thing is to unmask after receiving the first signal so subsequent | signals immediately terminate. | colanderman wrote: | Agreed -- if signal handlers are too messy, `sem_post(3)`, | `sigwait(2)`, or `signalfd(2)` will get that control flow where | you want it. Then the problem is reduced to "my application | needs to handle a graceful shutdown event", which, though | possibly complex, isn't really that novel. | cryptonector wrote: | > it's not rocket science to handle it in a sane fashion even | in a multi-threaded application | | It's not, though you need to be careful if you want to exit | cleanly -- you can't just exit() or _exit(). You have to get | all the threads to exit, and that requires a global volatile | sig_atomic_t flag and some way to signal all threads that might | be sleeping in event loops or what have you. | [deleted] | FeepingCreature wrote: | Sure you can just exit(), you just have to be sure that all | your on-disk state changes are atomic. Which you should make | sure of anyways. | quietbritishjim wrote: | A separate thread with sigwait() may be easier. Though I must | admit, I've rarely had to do that manually, as I'm usually | using a language or framework that gives an easier way to get | notified about signals (e.g. Python KeyboardInterrupt or | listeners in Boost ASIO or libuv). Aside from saving some | boilerplate, those also emulate equivalent signals in | Windows. | | Threads waiting on event loops is exactly what you want on | shutdown: that's what you use to notify them to exit. | vonwoodson wrote: | Tried searching for your username name, but, nothing happens. | kcl wrote: | There is an entire world here beyond registering for a signal | that comment seems unaware of. Even the simplest of | preliminaries: registering for a signal is arguably non-trivial | and incorrectly specified in many places since sigaction() | supersedes signal(). | | > it's not rocket science to handle it in a sane fashion even | in a multi-threaded application. Modern languages make this | trivial. The author makes it sound like some dark art | | Which language? I'll specify one so we can begin the process of | picking each apart. Python? There is a sibling thread | indicating Python issues. I don't know what the actual internal | status is with Python signal handling but I am guessing the | interpreter actually doesn't handle it correctly if I spent any | time digging. Do you mean apps implemented in Python? They will | almost certainly not be internally data-consistent. Exposing a | signal handling wrapper _means very little_ particularly when | they frequently do this by ignoring all of the bad | implications. I just checked Python 's docs, and not | surprisingly, Python guarantees you'll be stuck in tight loops: | https://docs.python.org/3/library/signal.html That's just one | gotcha of many that they probably aren't treating. This | dialogue is going to play out the same way regardless of which | language you choose. | | Do you mean Postgres? I haven't used it recently but the last | comment I read on HN seemed to indicate you needed to kill it | in order to stop ongoing queries in at least some situations. | If by a stroke of luck it does support SIGINT recovery (which | would be great), what about the hundreds of other db | applications that have appeared recently? You can't just call | the signal handler wrapper and declare victory. | klez wrote: | > SIGINT is really designed for interactive applications. | | Which are the applications the article is talking about anyway. | nickez wrote: | It mentions ACID compliant databases for one. | tolciho wrote: | The SIGINT (if the terminal is configured to generate one) goes | to the foreground process group, not the current process. See | the termios man page, look for INTR. This gets complicated in | shell pipelines (oh look, a process group) where one or more of | the tools involved are fiddling with the global terminal state, | in which case there may be a process group signal, or there | might instead be a key for some random program of the pipeline | to to read, which it may ignore. | | With an important productivity app like rogue(6) there is | (probably) only one process in the foreground process group, | and curses has (probably) set the terminal to have control+c | either ignored or delivered to the rogue process as a key | event. The player probably does not want to have their rogue | process vanish because they hit control+c by habit, like | trek(6) likes to do, but someone wrote blocksig(1) as an exec | wrapper so that SIGINT can be ignored. With a complicated shell | pipeline, the player probably does want the whole thing to go | away, but that may be difficult depending on exactly how | complicated the shell pipeline is and whether any processes in | that pipeline are fiddling with the global terminal state. | (Global to the process groups and their PIDs under that | particular terminal, not the whole system or universe or | whatever. But global enough to cause problems.) | | Opinions also vary here, some want that LISP image to never go | away (those emacs users, probably), others may want control+c | to murder the LISP image so they can go back to hacking in vi. | POSIX probably says something about shell pipelines and | control+c and such, which various shells may or may not follow, | exactly. Etc... | jwilk wrote: | > someone wrote blocksig(1) | | Link? | tolciho wrote: | https://thrig.github.io/2022/08/06/control+c.html probably | gets you close enough | intelVISA wrote: | Not really sure what the authorial intent is here tbh. | pixelbeat__ wrote: | I agree that this is an awkward but very desirable property for a | system to have. | | I was calling this "responsive idempotence" when discussing how | the GNU coreutils are tested: | | https://www.pixelbeat.org/docs/coreutils-testing.html | ruslan wrote: | Just wonder if author is aware of SIGSTOP/SIGCONT that allows to | pause/resume any process gracefully ? Both signals can be caught | and handled. | | Crtl-C (SIGINT), as far as I know, was used to "gracefully | terminate" interactive process from day zero of Unix. I cannot | find any use in that of what author proposes: suspend execution | by sending SIGINT, but then what ? Get to some process built-in | debugging shell ? Isn't that what GDB was made for ? | taf2 wrote: | I prefer when programs listen for SIGQUIT... it makes more sense | that this would be used to quit a process then SIGINT - IMO ... | dmarinus wrote: | After decades of experience I learned to use ctrl-\ (break) or | ctrl-z and then kill -9 %1. Hope this helps someone. | dingdingdang wrote: | Excellent advice, thanks for sharing. Would in turn recommend | using CopyQ to store this tips (and other like it) as a pinned | items in folder with explanations for use two years later, | that's how I personally stay on top of terminal kung-fu without | overloading the consciousness-in-meat* | | * | https://www.mit.edu/people/dpolicar/writing/prose/text/think... | drdec wrote: | I wouldn't call this excellent advice - kill -9 will rob the | process of the opportunity to clean up after itself and leave | everything in a good state (e.g. any binary files being | manipulated by the application). So I would use this as a | last resort - start with Ctrl+C and then "kill INT %1" and | then "kill TERM %1" before "kill KILL %1". | | (For those who don't know "kill KILL" is equivalent to "kill | -9". And despite the name "kill" is a tool for sending | signals to processes.) | klez wrote: | Which is exactly what the author is saying shouldn't be needed. | dmarinus wrote: | Author is talking about looking up PIDs, kill -9 %1 saves you | from that. | kcl wrote: | It is true this improves the bad path. It ignores desired | happy path cases: downstream processes, custom debugging, | graceful shutdown, preserved workspaces, and so on. | awild wrote: | Kill9 can keep ports locked for a bit after exiting which is a | quite annoying | M9HF8wwiaAdZKEZ wrote: | Anything can keep ports locked for a bit (if either side | doesn't properly close the connection). That's how TCP works. | Set reuseaddr on your daemon's sockets. | AshamedCaptain wrote: | Laugh all you want, but this is is precisely why I like "old- | fashioned" asynchronous exceptions (the ones which unwind the | stack), and ensure most programs are ready to handle a clean | stack unwind at practically any point inside the program (e.g. | asynchronous-unwind-tables). | kcl wrote: | The way exceptions are handled as a result of siglongjmp'ing | out of a signal handler is currently platform-inconsistent and | one of the many dark areas I alluded to. It isn't even | consistent on Linux between compilers. | fmajid wrote: | I don't have any expectations of a program doing an orderly | shutdown and trying to avoid corrupting files on disk when | interrupted by Ctrl-C. | teddyh wrote: | This is a _far_ better resource on the subject: | | _Proper handling of SIGINT /SIGQUIT_: | https://www.cons.org/cracauer/sigint.html | aumerle wrote: | Write your program around an event loop which if its an | interactive program it already has. And read man signalfd. | ape4 wrote: | I thought this was going to be another C++ replacement - actually | not a bad name. | jesprenj wrote: | Even though it makes sense from the name, SIGINT, to interrupt, | I've rarely seen console software "return control" to the user | when the signal is received. | | What I've mostly seen in programs is a clean exit from the | running application, if live user input is not intended to be | used. Clearing a line or something similar like redrawing the | terminal (that's mostly Ctrl-L though) is what interactive | programs do, let's say shells or ncurses UI programs. | | Whenever I made some hobby scripts that exit cleanly when | receiving a SIGINT, I've made a global counter of interrupts. | When SIGINT is received, the counter is incremented, which tells | the main loop to stop as soon as possible. But if this counter | exceeds three signals, the application would exit immediatley. | This may not be ideal, but CTRL-C CTRL-C CTRL-C is easier than | kill -9 `pgrep a.out`. | | Like the top comment says, expecting a concrete and general | behaviour on different types of software for such a broad signal | doesn't gain wide approval. | | What "return of control" did the author mean, on what kinds | software? | emmelaich wrote: | REPLs for one. | rmetzler wrote: | Currently I find kubectl often don't really respond for CTRL-C | and also can't be removed with kill -9 on MacOS. | e63f67dd-065b wrote: | I'm having trouble judging what exactly the author wants here. My | best reading is that he wants interactive programs to respond to | SIGINT not by bailing out but by terminating the current task and | returning to user input. | | I'm having trouble, however, thinking of programs to which this | applies. I just scrolled through my shell history, and the most | common interactive program I've used in my history file is a | debugger, which handles killing the active program correctly with | no issues, followed by resource monitoring applications, shells, | etc. | | Can somebody tell me an example command-line application where | there's a high degree of interactivity but is also multithreaded, | has DB consistency guarantees, network requests in-flight, etc? | I'm genuinely having trouble thinking of anything that's not a | REPL or vim/emacs. | drdec wrote: | I'm not sure that the author is exclusively talking about | command-line applications. The expected behaviors would make | sense inside IDEs, particularly if they are blocked by a modal | window. | mike-the-mikado wrote: | I think the author makes of common mistake of talking so | generally that readers cannot think of any specific examples. | | He would help his case by giving specific examples of | problematic programs. | codethief wrote: | > We don't want our ctrl-c to leak memory. [...] If you allocate | a piece of memory, you need to store a pointer to that memory | from the object graph, and both of those operations need to occur | inside of a critical section. Otherwise, if you get interrupted | right after the allocation, there won't be any way to reach your | memory and it will leak. | | Maybe I'm missing something here but... so what? If at the end of | your Ctrl+C signal handler you exit() as expected, then the OS | will clean up your process's memory anyway. | klez wrote: | That's the point: Ctrl-C shouldn't just gracefully kill the | process, it should interrupt the current computation and let | you resume your work without exiting the application. The use | case here is interactive applications (think a REPL, for | example), not commands you run, simply expect an output from | and then they just exit (like, say, curl). | M9HF8wwiaAdZKEZ wrote: | > it should interrupt the current computation and let you | resume your work without exiting the application | | That's not what Ctrl+C is meant for or used for. It's used to | terminate the running application, not the running task | within that application. | | If you want to be able to "resume your work" then you should | press Ctrl+Z. | | If you want something else then the application should | probably be listening for some other keystroke. "Catch Ctrl+C | and do something else" is a pretty awful idea for the very | reason mentioned at the top of TFA (when you press Ctrl+C, | it's to get out of whatever you're stuck in, so that you | don't have to go open another terminal and type in killall | ...) | macleginn wrote: | > That's not what Ctrl+C is meant for or used for. It's | used to terminate the running application, not the running | task within that application. | | I spend a lot of time running computations in REPL, and | sometimes I realise that I made a mistake and I don't want | to wait for the current operation to complete, or the | mistake itself is such that the operation will complete | only after I become old and die. In this case, I expect | Ctrl+C to abort the current computation and return to the | REPL, with the previous state (all the variable | assignments) intact (modulo assignments made inside the | loop I killed). I think a lot of people have the same | expectation, and it's usually satisfied in modern REPLs. | snet0 wrote: | You might have killed enough programs with Ctrl-C, but | SIGINT is an interrupt, not a kill, terminate or quit. | duped wrote: | If the application is a shell or REPL (an application | running other programs) then that is exactly what you want | to use CTRL-C for. | klez wrote: | > That's not what Ctrl+C is meant for or used for. It's | used to terminate the running application, not the running | task within that application. | | If that's not how it should behave, how come any REPL I | have handy handles Ctrl-C the exact same way? i.e. it | doesn't exit the interpreter, it gets me back to the REPL. | You can try yourself by getting stuck in a while loop and | pressing Ctrl-C | | Python (3) does it; | | jshell does it; | | guile does it; | | csi (chicken scheme) does it; | | sbcl does it; | | bash does it | hnlmorg wrote: | Because they either fork their processes so the running | task is it's own process (which is how classic shells, | like Bash, work) or they capture ^c and interpret it to | behave like the classic shells do because that's the | behaviour people expect from shells. | | You have to remember that Bash isn't a language like | Python in the sense that it's core libraries are built | into the Python runtime. in classic shells like bash | literally every command is an executable. Granted they'll | ship some "builtins" but they're still invoked via fork() | to behave like external commands. So literally every | 'if', 'echo' and 'for' (etc) has its own process ID in | Linux/UNIX. Thus you can 'kill' an 'echo'. | klez wrote: | > or they capture ^c and interpret it to behave like the | classic shells do because that's the behaviour people | expect from shells. | | Which is kinda the point. | | > So literally every 'if', 'echo' and 'for' (etc) has its | own process ID in Linux/UNIX. Thus you can 'kill' an | 'echo'. | | Do they? Because if I try like this `while :; do echo; | done` and in another terminal I do `ps -x --forest` I can | see the original bash running but it doesn't have any | child process. | | Besides, is it relevant to the discussion at hand? | hnlmorg wrote: | > Which is kinda the point. | | I thought I'd get picked up on that part. My point was | that shells are just a UI for invoking other applications | (like a desktop shell but CLI). That's the precedence and | anything that's shell-like but doesn't follow POSIX is | still inclined to emulate the same behaviour of killing | applications because that's the behaviour that people | expect after decades of POSIX. | | So it really is more about killing applications than | killing tasks. | | > Do they? | | That was the original design (there's even standalone | executables for those commands included in coreutils for | historic reasons). However Bash might have since | optimised out a few forks. | | The shell I've written certainly doesn't fork() every | built in either. However that doesn't change how ^c's | behaviour was intended. | | > Besides, is it relevant to the discussion at hand? | | I'm talking about the behaviour for ^c and how it is | handed in the shell, as a direct response to your comment | about it. So yes. It's exactly relevant to the | discussion. | PaulDavisThe1st wrote: | > So literally every 'if', 'echo' and 'for' (etc) has its | own process ID in Linux/UNIX | | echo: yes | | if, for: no | | Control flow statements do not execute in subshells | (processes) unless explicitly told to do so. | | You may be thinking of test(1) aka [ if | [ a == b ] ; then .... | | which was originally written: if test a | == b ; then | | test(1) is its own executable. But [ is a builtin command | and does not execute in a separate process. | gfodor wrote: | The article's point is basically proven by how many people | here don't even understand he's talking about this, and not | killing the program with Ctrl-C. | zokier wrote: | Rather the opposite; people have hard time understanding | what he is talking about because most applications that | people use already handle ctrl-c as author wants, so its | not a problem many people encounter often. So its | reasonable that people then think its talking about the | problem that many are encountering, programs that just | swallow ctrl-c without doing anything. This is not helped | by author having this bit near the beginning: | | > More often than not I find myself having to kill the | running process from an external app, such as the shell, | after first figuring out what the process ID is. | | See for example these sibling threads: | https://news.ycombinator.com/item?id=32369096 | https://news.ycombinator.com/item?id=32367401 | gfodor wrote: | Well maybe then it only proves people don't read the | articles they comment on :) | dgfitz wrote: | IMHO anyone launching an app via a terminal and Ctrl-C | killing it either is developing the app (in which case they | can manage the signal however they like) or they don't care | and just want the app to die. Any "good" repl won't let you | exit via Ctrl-C so that point is moot. | vermilingua wrote: | > Any "good" repl won't let you exit via Ctrl-C | | And in order to achieve that, it has to take the care | described in TFA. | dgfitz wrote: | Agreed. Not sure what your point is. | codethief wrote: | You don't mention REPLs etc. until the very end of the | article: | | > It definitely applies to interpreters, database-style | terminal interfaces, REPLs, consoles, calculators, command- | lines, and other categories I've unintentionally left out. | | So if your article is supposed to be exclusively about those, | I'd suggest you make this clear right in the beginning. | klez wrote: | I suggest you tell this to the author, not to me :P | codethief wrote: | Oops, sorry, I confused you with kcl. :) | ynik wrote: | > If at the end of your Ctrl+C signal handler you exit() as | expected | | exit() is not signal-safe; signal handlers are expected to call | quick_exit() or _Exit() instead. | CoffeeCollector wrote: | What is a "tight C loop"? | klez wrote: | It's a loop with few instruction that iterates many times, | written in C. | CoffeeCollector wrote: | Can't we have a tight loop in any language? What's special | about C here? | josephcsible wrote: | Tight loops in higher-level languages generally always have | yield points/opportunities to break provided by the | runtime, even if the loop itself doesn't appear to have | any. | g5095 wrote: | "More often than not I find myself having to kill the running | process from an external app, such as the shell, after first | figuring out what the process ID is." | | Short cut here, ctrl-z to background the process, then kill -9 %1 | to kill the first job (type jobs for the numbers) ___________________________________________________________________ (page generated 2022-08-06 23:00 UTC)