38

I have a simple question regarding the execution of a simple command. As per my understanding, when we type a command such as ls in an interactive shell,

  1. Shell interprets the command.
  2. Shell creates a child process and executes the command on the child process.
  3. Shell waits for the child process's completion.

If my understanding is correct, a simple command we typed on shell prompt is executed on a child process and the command's result does not affect current shell's environment.

If so, what about built-in commands like cd? If cd is executed on a child process and can not affect current shell's environment, how can the current shell's working directory can be changed?

Jeff Schaller
  • 67,283
  • 35
  • 116
  • 255
MS.Kim
  • 1,687
  • 4
  • 18
  • 18

2 Answers2

36

The shell is just a program, although it plays an important role in the system. Bash, and I would assume most other common shells, are implemented in C. The two most important native C system calls that are used in the creation of subprocesses are fork() and exec(). These functions are usually implemented in higher level languages too, including shell.

  1. fork()

    "Fork" creates a duplicate copy of the calling process as its child. This is how virtually all processes on the system except for the first one (init) begin: as copies of the process which started them. Shell language doesn't actually have a fork function, but it does include syntax to generate subshells, which are the same thing.

  2. exec()

    There isn't actually an exec() call in C, but it colloquially refers to a group of related functions; you can see the list with man 3 exec, which usually begins:

    The exec() family of functions replace the current process image with a new process image...

    And that is exactly what it does: replaces defining parts of the current process's memory stack with new stuff loaded from an executable file (e.g., /usr/bin/ls). This is why fork() is necessary first in the creation of a new process -- otherwise, the calling process ceases to be what it was and becomes something else instead; no new process would actually be created.

That may sound at first like an absurd and inefficient way to do things: why not just have a command that creates a new process from scratch? In fact, that would probably not be as efficient for a few reasons:

  • The "copy" produced by fork() is a bit of an abstraction, since the kernel uses a copy-on-write system; all that really has to be created is a virtual memory map. If the copy then immediately calls exec(), most of the data that would have been copied if it had been modified by the process's activity never actually has to be copied/created because the process doesn't do anything requiring its use.

  • Various significant aspects of the child process (e.g., its environment) do not have to be individually duplicated or set based on a complex analysis of the context, etc. They're just assumed to be the same as that of the calling process, and this is the fairly intuitive system we are familiar with.

For a detailed discussion of exactly what it means to "copy the environment" to the spawned child process, see my answer here.

If so, what about built-in commands like cd?

These are, again, just implemented in C. chdir(), like fork() and exec(), is part of the Unix platform extensions to standard C and what underlies the shell's cd command. From man 2 chdir:

chdir() changes the current working directory of the calling process to the directory specified in path.

This does not require a subprocess -- it affects the caller. The shell is an interactive runtime interpreter, meaning it executes code written in the shell language as you feed it commands. It mostly does not need to execute a new process to do this, it does it as itself.

goldilocks
  • 87,661
  • 30
  • 204
  • 262
8

If so, what about built-in commands like cd? If cd is executed on a child process

Herein lies the flaw in your logic. cd, being a shell builtin, is interpreted specially by the shell and "executed" in-process. You can use the type command to find out whether a command is builtin or not.

strugee
  • 14,951
  • I see. I also found a relevant question. Thank you for pointing out my flaw. – MS.Kim Apr 07 '14 at 03:08
  • 1
    @MS.Kim no problem! if this satisfactorily answered your question, consider marking it as accepted by clicking the check mark to the left of the answer. – strugee Apr 07 '14 at 03:15