Learning Unix for OS X: Multitasking
This practical guide shows you how to get much more from your system by tapping into Unix, the robust operating system concealed beneath OS X’s interface.
Multitasking
OS X can do many jobs at once, dividing the processor’s time between running applications and system processes so quickly that it looks as if everything is running at the same time. This is called multitasking. As new applications are launched, processes are started, and others go idle or shut down entirely, the system monitors each of these tasks and doles out memory and CPU resources on the fly to make sure everything runs smoothly.
Most users think of multitasking in terms of the way OS X handles applications like Adobe Photoshop, Microsoft Word, Mail, Messages, Safari, and so on—allowing you to have multiple applications open, each with its own windows. But on the Unix side, OS X allows you to run multiple Unix programs and/or processes at the same time as well. These processes can all be run and managed through a single Terminal window, with a little help from something called job control. Even if you’re using a window system, you may want to use job control to do several things inside the same Terminal window. For instance, you may prefer to do most of your work from one Terminal window, instead of having multiple Terminal windows open when you really don’t need to.
Why else would you want job control? Suppose you’re launching a Unix program that takes a long time to run. On an old-school, single-task operating system, you would enter the command and wait for the job to finish, returning you to the command prompt (which is your indication that you’re free to enter a new command). In OS X and other modern operating systems, however, you can enter new commands in the “foreground” while one or more programs are running in the “background.”
When you enter a command as a background process, the shell prompt reappears immediately so that you can enter a new command. The original program runs in the background, but you can use the same Terminal window to do other things during that time. Depending on your system and your shell, you may be able to close the Terminal window or even completely log off from OS X while the background process completes.
Running a Command in the Background
Running a program as a background process is most often done to free a Terminal when you know the program is going to take a long time to complete. It’s also done whenever you want to launch a new application from an existing Terminal window, so you can keep working in the existing Terminal window, as well as within the new application.
To run a program in the background, all you need to do is add the &
character at the end of the command line before pressing the Return key. The shell then assigns and displays a process ID (PID) number for the program:
$ sort bigfile > bigfile.sort & [1] 372
Sorting is a good example, because it can take a while to sort huge files.
The PID for this program is 372. The PID is useful when you want to check the status of a background process or if you need to cancel it. To check on the status of the process, use the ps command with the following two options: -f to have expanded output, and -p because you’re specifying a process ID. The full command for this example is:
$ ps -fp 372 UID PID PPID C STIME TTY TIME CMD 501 372 16901 0 10:12AM ttys001 0:00.00 sort
To cancel a process, use the kill command, followed by the PID of the process you want to cancel. In this instance, the command would look like:
$ kill 372
Fortunately, you don’t need to remember the PID every time, because there are Unix commands (explained in the next section) to check on the processes you have running. Also, bash writes a status line to your screen when a background process finishes.
In bash, you can put an entire sequence of commands separated by semicolons (;) into the background by putting an ampersand (&) at the end of the command line. In other shells, enclose the command sequence in parentheses before adding the ampersand:
(command1; command2) &
OS X’s Unix shells also have a feature (mentioned earlier) called job control that allows you to use the suspend character (usually Control-Z) to suspend a program running in the foreground. The program pauses, and you get a new shell prompt. You can then do anything else you like, including putting the suspended program into the background using the bg command. The fg command brings a suspended or background process to the foreground.
For example, you might start sort running on a big file, then decide you want to edit another file. You can stop sort with Control-Z, and then put it in the background with the bg command. The shell then gives you another shell prompt, at which you can start using vi while sort runs merrily in the background:
$ sort hugefile1 hugefile2 > sorted ...time goes by... ^Z Stopped $ bg [1] sort hugefile1 hugefile2 > sorted & $ vi test.txt
Checking on a Process
If a background process seems to be taking forever to run, or if you change your mind and want to stop a process, you can check the status of the process and even cancel it.
ps
When you enter the ps (process status) command, you get a variety of useful information about the processes that are running, including how long a process has been active and the Terminal from which it was launched. Not sure you’re the person who launched a process? The tty program shows the name of the Terminal where you’re logged in. This is especially helpful when you’re logged in to multiple machines, as the following code shows:
$ ps PID TTY TIME CMD 409 ttys000 0:00.04 -bash 813 ttys001 0:00.02 -bash $ tty /dev/ttys000
In the preceding output, s000
corresponds to the Terminal window for ttys000
(which is the current window, as the tty command shows), and s001
denotes a second Terminal window. In its basic form, ps lists the following:
- Process ID (
PID
) -
A unique number assigned by Unix to the process
- Terminal name (
TTY
) -
The Unix name for the terminal from which the process was started
- Runtime (
TIME
) -
The amount of CPU time (in minutes and seconds) that the process has used
- Command
CMD
-
The name of the process
In Unix, each Terminal window has its own name. The previous example shows processes running in two windows: s000
and s001
. If you want to see the processes that a certain user is running, use the following construct:
ps -U username
where username
is the username of someone logged in to the system.
To see all processes running on the system, use ps -ax. The -a option shows processes from all users, and the -x option shows processes that are not connected with a Terminal session. Many of these processes are a core part of OS X, while others may be graphical programs you are running, such as Safari. The head -20 in the following command line limits the output to the first 20 lines:
$ ps -ax | head -20 PID TTY TIME CMD 1 ?? 9:41.93 /sbin/launchd 42 ?? 2:05.18 /usr/libexec/UserEventAgent (System) 43 ?? 3:46.72 /usr/sbin/syslogd 45 ?? 0:07.54 /usr/libexec/kextd 46 ?? 1:55.25 /System/Library/Frameworks/CoreServices.framework/ Versions/A/Frameworks/FSEvents.framework/ Versions/A/Support/fseventsd 50 ?? 0:02.28 /System/Library/CoreServices/appleeventsd --server 51 ?? 2:18.30 /usr/libexec/configd 52 ?? 0:19.18 /System/Library/CoreServices/powerd.bundle/powerd 57 ?? 2:04.30 /usr/libexec/airportd 59 ?? 0:01.35 /usr/libexec/warmd 60 ?? 8:53.56 /System/Library/Frameworks/CoreServices.framework/ Frameworks/Metadata.framework/Support/mds 64 ?? 0:00.72 /System/Library/CoreServices/iconservicesd 65 ?? 0:00.05 /System/Library/CoreServices/iconservicesagent 66 ?? 0:40.39 /usr/libexec/diskarbitrationd 68 ?? 1:34.73 /usr/libexec/coreduetd 69 ?? 0:00.04 /usr/libexec/wdhelper 71 ?? 13:52.00 /System/Library/CoreServices/backupd.bundle/Contents/ Resources/mtmfs --tcp --resvport --listen localhost --oneshot --noportmap --nobrowse 72 ?? 1:53.06 /usr/libexec/opendirectoryd 73 ?? 0:00.28 /usr/sbin/wirelessproxd
The output of ps -ax can be baffling, since almost all the processes listed are the low-level system tasks required for OS X to run happily on your system.
Tip
In the preceding list, notice the OS kernel extensions module kextd, the configuration management daemon configd, the audio utility coreaudiod, the low-level disk management program diskarbitrationd, the CoreServices, etc. These are processes that a regular user shouldn’t have to worry about. Just beware before you try killing one of these processes; doing so could cause your system to crash.
You can find out what processes are being run as root by using -U root:
$ ps -ax -U root | head PID TTY TIME CMD 1 ?? 9:42.22 /sbin/launchd 42 ?? 2:05.19 /usr/libexec/UserEventAgent (System) 43 ?? 3:46.73 /usr/sbin/syslogd 45 ?? 0:07.54 /usr/libexec/kextd 46 ?? 1:55.26 /System/Library/Frameworks/CoreServices.framework/ Versions/A/Frameworks/FSEvents.framework/Versions/A/Support/fseventsd 50 ?? 0:02.28 /System/Library/CoreServices/appleeventsd --server 51 ?? 2:18.30 /usr/libexec/configd 52 ?? 0:19.18 /System/Library/CoreServices/powerd.bundle/powerd 57 ?? 2:04.30 /usr/libexec/airportd
You can also change the output, of course, by specifying yourself as the account with the $LOGNAME
environment variable (ps -ax -U $LOGNAME | head), though the results will probably look identical until you get to the last few processes on the list. And if you want to see more information about the process names, try using the -w flag. You can even use it more than once to get even more, like -ww or -www.
When you’re just learning how to interpret the oft-confusing output of the ps command, you might find it quite helpful to simultaneously run the Activity Monitor (/Applications/Utilities), shown in Figure 1-1.
It’s useful to change the filter at the top of the Activity Monitor from the default of “My Processes” to “All Processes” using the View menu in the program. This gives you a much better sense of what’s happening on your computer, and if you do have a runaway application or one that’s locked, it often doesn’t show up in the My Processes view anyway.
top
A better way to see what applications are running and which are taking up the most resources is to use the helpful top command. Figure 1-2 shows top in action.
If you’re curious what commands consume the most system resources, leave top running in a Terminal window while you work. However, do be aware that top itself consumes some system resources, so if you’re not paying attention to its output, you can quit top by typing q
. You can always start it up again if things seem to be oddly slow on your computer.
top packs a lot of information into its display—considerably more than we have space to explain here. However, look at the first few lines and you’ll get some quick insight into how well your system configuration matches the needs of the processes you’re running. You can grab a snapshot of the first seven lines of output with this command (the flag used is a lowercase “L” followed by the digit one):
$ top -l 1 | head -8 Processes: 316 total, 3 running, 12 stuck, 301 sleeping, 1615 threads 2015/10/06 13:16:09 Load Avg: 0.44, 0.59, 0.71 CPU usage: 4.31% user, 12.94% sys, 82.73% idle SharedLibs: 204M resident, 21M data, 28M linkedit. MemRegions: 82102 total, 5634M resident, 125M private, 2340M shared. PhysMem: 15G used (2442M wired), 570M unused. VM: 889G vsize, 467M framework vsize, 1408(0) swapins, 7488(0) swapouts.
What you should look for here is high CPU usage (anything over about 25 percent is usually considered high, unless you’re running something like Photoshop or some other CPU-intensive task) or too little free memory (I have 570M free—as shown at the end of the second-to-last line—out of a 16 GB RAM configuration, not a ton of space).
Tip
Swapping is based on the idea that the memory needed for an application can be broken into pages, as many as needed for the app at that particular moment. As multiple processes compete for the system memory, memory pages that haven’t been accessed for a while are temporarily copied to a special place on the hard disk, and those pages are given to applications that need them now. The process of swapping an older page for a newer one is called a pageout or swapout.
To display processes sorted by CPU usage (rather than process ID), use:
$ top -o cpu
If you find this view to be more useful than top’s traditional view, you can add this as an alias to your .profile file:
alias top='/usr/bin/top -s 5 -o cpu'
This updates top’s display every five seconds rather than the default of every second, and sorts the results by highest CPU usage to lowest. For more information on top, visit its manpage (man top).
If you see a process in top that seems to be a resource hog, you can give its PID value to ps to find out more about that specific job. If you know that Apple Mail is running as process 317, for example, ps -p 317 will give you more process-related information.
Canceling a Process
You may decide that you shouldn’t have put a process in the background, or that the process is taking too long to execute. You can cancel a background process if you know its PID.
kill
The kill command terminates a process. This has the same basic result as using the Finder’s Force Quit option, though it can be more graceful, as you’ll see. To kill a process, use the following format:
kill PID(s)
Tip
OS X includes a very helpful utility called Force Quit, accessible from the Apple menu (Apple Menu→Force Quit, or Option-⌘-Esc), which can be quite useful when applications are stuck or nonresponsive. However, commands entered into the Terminal window can only be canceled from the command line—they don’t show up in the Force Quit window. Additionally, Force Quit doesn’t show you administrative processes. To stop Unix programs and administrative processes, you must use either the command line or the Activity Monitor (/Applications/Utilities).
kill terminates the designated PIDs (shown under the PID heading in the ps listing). If you do not know the PID of the process you want to kill, you should first run ps to display the status of your processes.
The following example illustrates how to enter two commands—sleep and who—on the same line, and designate those to run as a background process. The sleep n
command simply causes a process to “go to sleep” for n
seconds:
$ (sleep 60;who) & [1] 981 $ ps PID TTY TIME CMD 409 ttys000 0:00.09 -bash 981 ttys000 0:00.00 -bash 982 ttys000 0:00.00 sleep 60 813 ttys001 0:00.02 -bash 912 ttys001 0:00.58 vi ch07.asc $ kill 981 [1]+ Terminated: 15 ( sleep 60; who )
Here, I decided that 60 seconds was too long to wait for the output of who. The ps listing showed that sleep had the PID number 982, so I used this PID to kill the sleep process. You should see a message like “Terminated” or “Killed”; if you don’t, use another ps command to make sure the process has been killed (or that you killed the right process).
Now who executes immediately—as it’s no longer waiting on sleep—and displays a list of users logged in to the system.
killall
If you’d rather not worry about finding the PID for a particular process, you can always use the killall command, which lets you kill processes by name instead. Since it’s possible to inadvertently kill a different process with the same name (like your Terminal application or your shell), I strongly recommend that you always start by using the -s option so killall shows you what it’ll do without actually killing anything:
$ (sleep 60;who) & [2] 990 $ killall -s make No matching processes belonging to you were found $ killall -s who No matching processes belonging to you were found $ killall -s sleep kill -TERM 991 kill -TERM 986
Did it surprise you that there’s no match to killall -s who even though sleep;who is running in the background? The reason it didn’t match is because the who command itself isn’t yet running, but the sleep command is; you can see that it’s matched by the third instance of killall.
If you have eagle eyes, you’ll notice that the sleep command’s PID isn’t the same as the PID given by the shell when the sleep;who command was dropped into the background. That’s because when a job is put into the background, the shell copies itself, and then the copy shell (Unix folk call that the subshell) manages the commands. It’s the subshell that has PID 990, and sleep is a subprocess of that shell, so it gets a different PID: 991. When sleep finishes and the who command runs, that’ll have yet another PID (most likely 992).
To kill the sleep process, simply remove the -s flag from the killall command, or, if you’re curious, replace it with -v so you can see what the program does:
$ killall sleep -bash: line 52: 995 Terminated: 15 sleep 60 taylor console May 14 10:50 taylor ttys000 May 14 11:39 taylor ttys001 May 14 11:56 $ killall -v sleep No matching processes belonging to you were found [1]+ Done ( sleep 60; who )
Notice that the first killall killed the sleep process, which immediately caused who to be run. When I tried to use killall again with the -v flag, it was too late and there was no longer a sleep command running.
Launching GUI Applications
One great feature of OS X’s Unix command line is that you can interact with the graphical applications in Aqua. For example:
-
Drag a file or folder from the Finder onto a Terminal window and watch as its full pathname gets dropped in after the command prompt.
-
Want to use vi to edit a text file that’s on your Desktop? Just type vi on the command line, followed by a space, and then drag the file onto the Terminal window.
-
When viewing a file in the Finder, you’ll see what’s known as a proxy icon in the Finder’s title bar that shows you what directory you’re in. Type cd followed by a space, then drag the proxy icon into the Terminal window and press Return; you’ll be taken to that same exact location, just in the Terminal.
If you can have the Finder interact with the Terminal, it should be no surprise to you that you can also have the Terminal interact with other graphical applications on the Mac. For this, OS X offers the open command.
open
By default, the open command works identically to double-clicking an icon in the Finder. To open up a picture file called peanut.jpg in your default picture editor, use:
$ open peanut.jpg
If you don’t have a graphics-editing application like Photoshop installed, the image opens in Preview (/Applications). If Preview is already running, the peanut.jpg image file opens in a new window.
The open command also lets you work at the command line with file matching, since it accepts more than one filename at a time. For example, if you need to open up a bunch of Microsoft Word files in a directory, just use:
$ open *.doc
You can, however, get things a bit confused, because sometimes the system doesn’t know what to do with certain files. For example, try issuing the following command:
$ open .profile
The default application that’s used when there’s no specific binding is TextEdit, which works in this instance, but look what happens when you try to open something it can’t recognize:
$ open .sample.swp No application knows how to open /Users/taylor/Desktop/.sample.swp.
In this case, open just couldn’t figure out what to do with this temporary scratch file from the vi editor. That’s because open uses a file’s creator and/or type code to determine which application should be used to open a particular file. Since vi’s scratch files don’t have a creator or type code, the command gets confused and ends up doing nothing.
Useful Starting Options for Use with open
The open command has a lot of power accessible through command options. For example, if you want to stream a bunch of input into a text file then open it in an Aqua file, you can do so by using the -f option:
$ mdfind NIKON | open -f
This quick call to Spotlight generates a list of all filenames that reference or include NIKON
. It would be easy to generate a printout with TextEdit, too.
The most useful option for use with open is -a, which is used to specify an application to open. For example, you can launch Messages with the generic open command, but you need to know where it’s located on your system:
$ open messages The file /Users/taylor/Desktop/messages does not exist.
Add the -a option, though, and open knows that you’re talking about an application, so it’ll search in the /Applications directory to find and launch it:
$ open -a messages
Notice that open is smart enough to ignore case: the actual application is called Messages. You can also use the open -a command to open applications that are in a subdirectory of /Applications. Want to launch the Console (located in /Applications/Utilities)? Use open -a console. Ready to compare the output of Activity Monitor to the ps command, as discussed earlier in this chapter? Launch Activity Monitor with open -a “activity monitor”.
If you want to open a file with TextEdit, there’s another option to open that’s worth knowing: use open -e, and whatever you specify will be opened with the TextEdit program, regardless of its type. For example, if you wanted to open an HTML file in TextEdit instead of with BBEdit, you could use the following:
$ open -e ~/Sites/someFile.html
The open command will then look in your Sites folder for the file someFile.html and open it in TextEdit.
Making open More Useful
open makes it a breeze to launch your favorite applications, but because it requires that you type in the full application name, a few aliases are in order:
alias word="open -a Microsoft\ Word" alias excel="open -a Microsoft\ Excel" alias gc="open -a GraphicConverter\ 9"
With these added to your .profile file, you can easily launch Graphic Converter by just entering gc, and launch Microsoft Excel with excel and Microsoft Word with word.
A more sophisticated approach would be to use a shell script wrapper that would give its arguments to open and, if they failed, try to figure out what application you were talking about. It’s an advanced topic, but here’s how that script might look:
#!/bin/sh # open2 - a smart wrapper for the cool OS X 'open' command # to make it even more useful. By default, open launches the # appropriate application for a specified file or directory # based on the Aqua bindings, and has a limited ability to # launch applications if they're in the /Applications dir. # first off, whatever argument we're given, try it directly: open=/usr/bin/open if ! $open "$@" >/dev/null 2>&1 ; then if ! $open -a "$@" >/dev/null 2>&1 ; then # More than one arg? Don't know how to deal with it: quit if [ $# -gt 1 ] ; then echo "open: Can't figure out how to open or launch $@" >&2 exit 1 else case $(echo $1 | tr '[:upper:]' '[:lower:]') in acrobat ) app="Acrobat Reader" ;; address* ) app="Contacts" ;; chat ) app="Messages" ;; cpu ) app="Activity Monitor" ;; dvd ) app="DVD Player" ;; word ) app="Microsoft Word" ;; excel ) app="Microsoft Excel" ;; prefs ) app="System Preferences" ;; qt|quicktime ) app="QuickTime Player" ;; * ) echo "open: Don't know what to do with $1" >&2 exit 1 esac echo "You asked for $1 but I think you mean $app." >&2 $open -a "$app" fi fi fi exit 0
This script has a simple table of nicknames for common applications, allowing you to use open2 qt to launch QuickTime Player, for example.
Tip
This script is based on one in my book, Wicked Cool Shell Scripts (No Starch Press), which explains 101 powerful and interesting shell scripts. You can learn about the book, and download this script for yourself, at http://intuitive.com/wicked/.