7-Process Environment

Please indicate the source: http://blog.csdn.net/gaoxiangnumber1

Welcome to my github: https://github.com/gaoxiangnumber1

7.1 Introduction

7.2 main Function

  • A C program starts execution with main function. The prototype for the main is
    int main(int argc, char *argv[]);
    argc is the number of command-line arguments, argv is an array of pointers to the arguments.
  • When a C program is executed by the kernel, a special start-up routine is called before the main function is called. The executable program file specifies this routine as the starting address for the program; this is set up by the link editor when it is invoked by the C compiler. This start-up routine takes values from the kernel(the command-line arguments and the environment) and sets things up so that the main function is called.

7.3 Process Termination

  • Eight ways for a process to terminate.
    Normal termination occurs in five ways:
    1. Return from main
    2. Calling exit
    3. Calling _exit or _Exit
    4. Return of the last thread from its start routine(Section 11.5)
    5. Calling pthread_exit(Section 11.5) from the last thread
      Abnormal termination occurs in three ways:
    6. Calling abort(Section 10.17)
    7. Receipt of a signal(Section 10.2)
    8. Response of the last thread to a cancellation request(Sections 11.5 and 12.7)
  • The start-up routine is written so that if the main function returns, the exit function is called. The call to main look like
    exit(main(argc, argv));

Exit Functions

  • Three functions terminate a program normally: _exit and _Exit, which return to the kernel immediately, and exit, which performs certain cleanup processing and then returns to the kernel.
#include <stdlib.h>
void exit(int status);
void _Exit(int status);
#include <unistd.h>
void _exit(int status);
  • The exit function has always performed a clean shutdown of the standard I/O library: fclose() is called for all open streams and this causes all buffered output data to be flushed(written to the file).
  • All three exit functions expect a single integer argument, which we call the exit status.
  • If
    (a) any of these functions is called without an exit status, or
    (b) main does a return without a return value, or
    (c) the main function is not declared to return an integer,
    the exit status of the process is undefined.
  • If the return type of main is an integer and main ‘‘falls off the end’’(an implicit return), the exit status of the process is 0.
  • Returning an integer value from the main function is equivalent to calling exit with the same value.

#include <stdio.h>

main()
{
    printf("hello, world\n");
}
  • The exit code is random:
$ gcc hello.c
$ ./a.out
hello, world
$ echo $?  #print the exit status
13
  • Now if we enable c99, the exit code changes.
$ gcc -std=c99 hello.c #enable gcc’s 1999 ISO C extensions
hello.c:4: warning: return type defaults to ‘int’ [enabled by default]
$ ./a.out
hello, world
$ echo $?              #print the exit status
0

atexit Function

  • ISO C: A process can register at least 32 functions that are automatically called by exit. These are called exit handlers and are registered by calling the atexit function.
#include <stdlib.h>
int atexit(void (*func)(void));
Returns: 0 if OK, nonzero on error
  • The exit function calls these functions in reverse order of their registration. Each function is called as many times as it was registered.
  • With ISO C and POSIX.1, exit first calls the exit handlers and then closes(via fclose) all open streams.
  • POSIX.1 specify that any exit handlers installed will be cleared if the program calls any of the exec family of functions.
  • Figure 7.2 summarizes how a C program is started and the various ways it can terminate.

  • The only way a program can be executed by the kernel is if one of the exec functions is called.
  • The only way a process can voluntarily terminate is if _exit or _Exit is called, either explicitly or implicitly(by calling exit). A process can also be involuntarily terminated by a signal(not shown).

#include <stdio.h>
#include <stdlib.h>

void Exit(char *string)
{
    printf("%s\n", string);
}

void exit1()
{
    printf("exit1\n");
}

void exit2()
{
    printf("exit2\n");
}

int main()
{
    if(atexit(exit2) != 0)
    {
        Exit("Register exit2 error.");
    }

    if(atexit(exit1) != 0)
    {
        Exit("Register exit1 error.");
    }
    if(atexit(exit1) != 0)
    {
        Exit("Register exit1 error.");
    }

    printf("main is done.\n");
    return 0;
}
/*
Output:
main is done.
exit1
exit1
exit2
*/

7.4 Command-Line Arguments

#include <stdio.h>
#include <stdlib.h>

int main(int argc, char *argv[])
{
    for(int index = 0; index < argc /* argv[index] != NULL */; ++index)
    {
        printf("argv[%d]: %s\n", index, argv[index]);
    }

    exit(0);
}
  • ISO C and POSIX.1: argv[argc] is a null pointer. This lets us alternatively code the loop as
    for (i = 0; argv[i] != NULL; i++)

7.5 Environment List

  • Each program is passed an environment list that is an array of character pointers, with each pointer containing the address of a null-terminated C string. The address of the array of pointers is contained in the global variable environ:
    extern char **environ;
    If the environment consisted of five strings, it could look like Figure 7.5.

  • We’ll call environ the environment pointer, the array of pointers the environment list, and the strings they point to the environment strings.
  • The environment consists of name=value strings. Most predefined names are entirely uppercase, but this is only a convention.
  • Historically, UNIX provided a third argument to the main function that is the address of the environment list:
    int main(int argc, char *argv[], char *envp[]);
  • POSIX.1 specifies that the global variable environ should be used instead of the third argument. Access to specific environment variables is through the getenv and putenv functions in Section 7.9, instead of through the environ variable.

7.6 Memory Layout of a C Program

  • A C program has been composed of the following pieces:
    1. Text segment, consisting of the machine instructions that the CPU executes.
      It is sharable so that only a single copy needs to be in memory for frequently executed programs, such as text editors, the C compiler and so on. It is read-only to prevent a program from modifying its instructions.
    2. Initialized data segment, called the data segment, containing variables that are initialized in the program.
      int max_count = 99;
      appearing outside any function causes this variable to be stored in the initialized data segment with its initial value.
    3. Uninitialized data segment, called the bss segment. Data in this segment is initialized by the kernel to arithmetic 0 or null pointers before the program starts executing.
      long sum[1000];
      appearing outside any function causes this variable to be stored in the uninitialized data segment.
    4. Stack, where automatic variables are stored, along with information that is saved each time a function is called. Each time a function is called, the address of where to return to and information about the caller’s environment are saved on the stack. The called function allocates room on the stack for its automatic and temporary variables. This is how recursive functions in C can work. Each time a recursive function calls itself, a new stack frame is used, so one set of variables doesn’t interfere with the variables from another instance of the function.
    5. Heap, where dynamic memory allocation takes place.
  • With Linux on a 32-bit x86 processor, the text segment starts at location 0x08048000, and the bottom of the stack starts below 0xC0000000. The stack grows from higher-numbered addresses to lower-numbered addresses.
  • Several more segment types exist in an a.out, containing the symbol table, debugging information, linkage tables for dynamic shared libraries, and the like. These additional sections don’t get loaded as part of the program’s image executed by a process.
  • The only portions of the program that need to be saved in the program file are the text segment and the initialized data; uninitialized data segment are not stored because the kernel sets the contents to 0 before the program starts running.
  • The size(1) command reports the sizes (in bytes) of the text, data, and bss segments. For example:
$ size /usr/bin/cc /bin/sh
text            data        bss     dec         hex     filename
761967      8160    81056   851183      cfcef   /usr/bin/cc
112154      5184    11240   128578      1f642   /bin/sh

7.7 Shared Libraries

  • Shared libraries remove the common library routines from the executable file, instead maintaining a single copy of the library routine somewhere in memory that all processes reference. This reduces the size of each executable file but may add some runtime overhead, either when the program is first executed or the first time each shared library function is called.
  • Another advantage: Library functions can be replaced with new versions without having to relink edit every program that uses the library(assuming that the number and type of arguments haven’t changed).

7.8 Memory Allocation

#include <stdlib.h>
void *malloc(size_t size);
void *calloc(size_t nobj, size_t size);
void *realloc(void *ptr, size_t newsize);
All three return: non-null pointer if OK, NULL on error
void free(void *ptr);
  • malloc: allocates a specified number of bytes of memory. The initial value of the memory is indeterminate.
  • calloc: allocates space for a specified number of objects of a specified size. The space is initialized to all 0 bits.
  • realloc: increases or decreases the size of a previously allocated area.
    1. Increases: the initial value of the space between the old contents and the end of the new area is indeterminate.
      -1- If there is room beyond the end of the existing region for the requested space, then allocates this additional area at the end and returns the same pointer(ptr).
      -2- If there isn’t room, allocates another area that is large enough, copies the old contents to the new area, frees the old area, and returns the pointer to the new area.
    2. If ptr is NULL, realloc behaves like malloc and allocates a region of the specified newsize.
  • The pointer returned by the three allocation functions is guaranteed to be suitably aligned so that it can be used for any data object.
  • free causes the space pointed by ptr to be deallocated. This freed space is usually put into a pool of available memory and can be allocated in a later call to one of the three alloc functions.
  • The allocation routines are implemented with the sbrk(2) system call that expands/ contracts the heap of the process. An implementation of malloc and free is in Section 8.7 of K&R[1988].
  • Although sbrk can contract the memory of a process, most versions of malloc and free never decrease their memory size. The space that we free is kept in the malloc pool(not return to the kernel) and is available for a later allocation.
  • Most implementations allocate more space than requested and use the additional space for record keeping(the size of the block, a pointer to the next allocated block, and the like). Writing past the end or before the start of an allocated area could overwrite this record-keeping information in another block.
  • Other fatal errors are freeing a block that was already freed and calling free with a pointer that was not obtained from one of the three alloc functions. If a process calls malloc but forgets to call free, its memory usage will continually increase; this is called leakage. If we do not call free to return unused space, the size of a process’s address space will increase until no free space is left.

Alternate Memory Allocators

  1. libmalloc
  2. vmalloc
  3. quick-fit
    The standard malloc use either best-fit or first-fit memory allocation strategy. Quick-fit is faster than either, but tends to use more memory. It is based on splitting up memory into buffers of various sizes and maintaining unused buffers on different free lists, depending on the buffer sizes. Most modern allocators are based on quick-fit.
  4. jemalloc
  5. TCMalloc
    TCMalloc provides high performance, scalability, and memory efficiency. It uses thread-local caches to avoid locking overhead when allocating buffers from and releasing buffers to the cache. It also has a heap checker and a heap profiler built in to aid in debugging and analyzing dynamic memory usage.
    • The function alloca has the same calling sequence as malloc; instead of allocating memory from the heap, the memory is allocated from the stack frame of the current function.
      1. Advantage: we don’t have to free the space; it goes away automatically when the function returns. The alloca function increases the size of the stack frame.
      2. Disadvantage: some systems can’t support alloca(Linux support), if it’s impossible to increase the size of the stack frame after the function has been called.

7.9 Environment Variables

  • Environment strings are usually of the form name=value. The kernel never looks at these strings; their interpretation is up to applications. For example, shell use numerous environment variables. Some are set automatically at login(such as HOME and USER); others are left for us to set. We normally set environment variables in a shell start-up file to control the shell’s actions.
#include <stdlib.h>
char *getenv(const char *name);
Returns: pointer to value associated with name, NULL if not found
  • getenv returns a pointer to the value of a name=value string. We should use getenv to fetch a specific value from the environment, instead of accessing environ directly.

  • We can affect the environment of only the current process and any child processes that we invoke. We cannot affect the environment of the parent process.
  • clearenv remove all entries from the environment list.
#include <stdlib.h>
int putenv(char *str);
Returns: 0 if OK, nonzero on error
int setenv(const char *name, const char *value, int rewrite);
int unsetenv(const char *name);
Both return: 0 if OK, −1 on error
  • putenv takes a string of the form name=value and places it in the environment list. If name already exists, its old definition is first removed.
  • setenv sets name to value. If name already exists, then if rewrite is
    (a) nonzero: the existing definition for name is first removed.
    (b) 0: the existing definition remain unchanged and no error occurs.
  • setenv must allocate memory to create the name=value string from its arguments; putenv place the string passed to it directly into the environment. It is an error to pass putenv a string allocated on the stack because the memory would be reused after we return from the current function.
  • unsetenv removes any definition of name. It is not an error if such a definition does not exist.
  • Figure 7.6: The environment list and the environment strings are stored at the top of a process’s memory space, above the stack.
    1. Deleting a string is simple: we find the pointer in the environment list and move all subsequent pointers down one.
    2. Adding a string or modifying an existing string is difficult. The space at the top of the stack cannot be expanded because it is at the top of the address space and so can’t expand upward; it can’t be expanded downward because all the stack frames below it can’t be moved.
    3. Add a string. First, we must call malloc to allocate room for the name=value string and copy the string to this area.
      a. If it’s the first time we add a new name, we have to call malloc to obtain room for a new list of pointers. We copy the old environment list to this new area and store a pointer to the name=value string at the end of this list of pointers. We store a null pointer at the end of this list. Finally, we set environ to point to this new list of pointers. If the original environment list was contained above the top of the stack, then we have moved this list of pointers to the heap. But most of the pointers in this list still point to name=value strings above the top of the stack.
      b. If this isn’t the first time we add new strings, then we’ve already allocated room for the list on the heap, we call realloc to allocate room for one more pointer. The pointer to the new name=value string is stored at the end of the list, followed by a null pointer.
    4. Modify an existing name. If new value’s size is:
      a. <= existing value’s size, we can copy the new string over the old string.
      b. > existing value’s size, we must malloc to obtain room for the new string, copy the new string to this area, and then replace the old pointer in the environment list for name with the pointer to this allocated area.

7.10 setjmp and longjmp Functions

  • Use setjmp and longjmp functions to goto a label that’s in another function.

void cmd_add()
{
    /* Operations */
}

void do_line()
{
    cmd_add();
}

int main()
{
    do_line();
    exit(0);
}

#include <setjmp.h>
int setjmp(jmp_buf env);
Returns: 0 if called directly, nonzero if return from a call to longjmp
void longjmp(jmp_buf env, int val);
  • jmp_buf data type is array that is capable of holding all the information required to restore the status of the stack to the state when we call longjmp.
    env is a global variable because we need to reference it from another function.
  • We call setjmp from the location that we want to return to. In this example, we call setjmp in the main function, so it returns 0 because we called it directly.
  • When we encounter an error, we call longjmp with two arguments. The first is the same env that we used in a call to setjmp; the second, val, is a nonzero value that becomes the return value from setjmp.
  • We can use more than one longjmp for each setjmp with different val and we can test return value of setjmp to determine which function the longjmp was from.

#include <stdio.h>
#include <stdlib.h>
#include <setjmp.h>

jmp_buf jmpbuffer;

void Exit(char *string)
{
    printf("%s\n", string);
    exit(1);
}

void Level2()
{
    printf("Enter Level2, Before longjmp\n");
    longjmp(jmpbuffer, 1);
    printf("After longjmp, Leave Level2\n");
}

void Level1()
{
    printf("Enter Level1, Before Level2\n");
    Level2();
    printf("After Level2, Leave Level1\n");
}

int main(int argc, char *argv[])
{
    int cnt = 0;
    printf("Before setjmp\n");
    int val = setjmp(jmpbuffer);
    if(val == 0)
    {
        printf("First time setjmp success.\n");
    }
    else
    {
        printf("Error occur: setjmp() = %d\n", val);
        if(++cnt == 5)
        {
            Exit("Exit");
        }
    }
    printf("Before Level1\n");
    Level1();
    printf("After Level1\n");

    exit(0);
}
/*
Before setjmp
First time setjmp success.
Before Level1
Enter Level1, Before Level2
Enter Level2, Before longjmp
Error occur: setjmp() = 1
Before Level1
Enter Level1, Before Level2
Enter Level2, Before longjmp
Error occur: setjmp() = 1
Before Level1
Enter Level1, Before Level2
Enter Level2, Before longjmp
Error occur: setjmp() = 1
Before Level1
Enter Level1, Before Level2
Enter Level2, Before longjmp
Error occur: setjmp() = 1
Before Level1
Enter Level1, Before Level2
Enter Level2, Before longjmp
Error occur: setjmp() = 1
Exit
*/
  • When main is executed, we call setjmp, which records whatever information it needs in jmpbuffer and returns 0. We then call do_line, which calls cmd_add and assume an error is detected. Before the call to longjmp in cmd_add, the stack looks like that in Figure 7.10. longjmp causes the stack to be unwound back to the main function, throwing away the stack frames for cmd_add and do_line(Figure 7.12). Calling longjmp causes the setjmp in main to return with a value of 1(the second argument for longjmp).

Automatic, Register, and Volatile Variables

  • When we return to main as a result of the longjmp, do automatic variables and register variables have values corresponding to those when the setjmp was previously called(i.e., are their values rolled back), or are their values left alone so that their values are whatever they were when do_line was called?
  • Answer is ‘‘It depends’’, the standards say only that their values are indeterminate. If you have an automatic variable that you don’t want rolled back, define it with the volatile attribute.

#include <stdio.h>
#include <stdlib.h>
#include <setjmp.h>

static jmp_buf jmpbuffer;
static int globval = 1;

static void f2()
{
    longjmp(jmpbuffer, 1);
}

static void f1(int autoval, int regival, int volaval, int statval)
{
    printf("In f1():\n");
    printf("global_val = %d, auto_val = %d, register_val = %d, volatile_val = %d,"
           " static_val = %d\n", globval, autoval, regival, volaval, statval);
    f2();
}

int main(int argc, char *argv[])
{
    int autoval = 2;
    register int regival = 3;
    volatile int volaval = 4;
    static int statval = 5;
    printf("Before 1st setjmp(): "
           "global_val = %d, auto_val = %d, register_val = %d, volatile_val = %d,"
           " static_val = %d\n", globval, autoval, regival, volaval, statval);

    if(setjmp(jmpbuffer) != 0)
    {
        printf("After longjmp:\n");
        printf("global_val = %d, auto_val = %d, register_val = %d, volatile_val = %d,"
               " static_val = %d\n", globval, autoval, regival, volaval, statval);
        exit(0);
    }

    globval = 95;
    autoval = 96;
    regival = 97;
    volaval = 98;
    statval = 99;

    f1(autoval, regival, volaval, statval);
    exit(0);
}
$ gcc 7-13.c               #compile without any optimization
$ ./a.out 
In f1():
global_val = 95, auto_val = 96, register_val = 97, volatile_val = 98, static_val = 99
After longjmp:
global_val = 95, auto_val = 96, register_val = 97, volatile_val = 98, static_val = 99
$ gcc -O 7-13.c            #compile with full optimization
$ ./a.out 
In f1():
global_val = 95, auto_val = 96, register_val = 97, volatile_val = 98, static_val = 99
After longjmp:
global_val = 95, auto_val = 2, register_val = 3, volatile_val = 98, static_val = 99
  • setjmp(3) manual page states that variables stored in memory will have values as of the time of the longjmp, variables in the CPU and floating-point registers are restored to their values when setjmp was called.
  • Without optimization, all five variables are stored in memory(the register hint is ignored for regival). When we enable optimization, both autoval and regival go into registers, and the volatile variable stays in memory. So, you must use the volatile attribute if you’re writing portable code that uses nonlocal jumps. Anything else can change from one system to the next.

Potential Problem with Automatic Variables

  • An automatic variable can never be referenced after the function that declared it returns.

  • Problem is that when open_data returns, the space it used on the stack will be used by the stack frame for the next function that is called. But the standard I/O library will still be using that portion of memory for its stream buffer. Chaos is sure to result.
  • Correct: The array databuf needs to be allocated from global memory, either statically(static or extern) or dynamically(one of the alloc functions).

7.11 getrlimit and setrlimit Functions

  • Every process has a set of resource limits, some of which can be queried and changed by the getrlimit and setrlimit functions.
#include <sys/resource.h>
int getrlimit(int resource, struct rlimit *rlptr);
int setrlimit(int resource, const struct rlimit *rlptr);
Both return: 0 if OK, −1 on error
  • The resource limits for a process are normally established by process 0 when the system is initialized and then inherited by each successive process. Each implementation has its own way of tuning the various limits.
  • Each call to two functions specifies a single resource and a pointer to the following structure.
struct rlimit
{
    rlim_t  rlim_cur;   // soft limit: current limit
    rlim_t  rlim_max;   // hard limit: maximum value for rlim_cur
};
  • Three rules govern the changing of the resource limits.
    1. A process can change its soft limit to a value less than or equal to its hard limit.
    2. A process can lower its hard limit to a value greater than or equal to its soft limit. This lowering of the hard limit is irreversible for normal users.
    3. Only a superuser process can raise a hard limit.
  • An infinite limit is specified by the constant RLIM_INFINITY. The resource argument takes on one of the following values.
RLIM_INFINITYMeaning
RLIMIT_ASThe maximum size in bytes of a process’s total available memory. This affects the sbrk function (Section 1.11) and the mmap function (Section 14.8).
RLIMIT_COREThe maximum size in bytes of a core file. A limit of 0 prevents the creation of a core file.
RLIMIT_CPUThe maximum amount of CPU time in seconds. When the soft limit is exceeded, the SIGXCPU signal is sent to the process.
RLIMIT_DATAThe maximum size in bytes of the data segment: the sum of the initialized data, uninitialized data, and heap from Figure 7.6.
RLIMIT_FSIZEThe maximum size in bytes of a file that may be created. When the soft limit is exceeded, the process is sent the SIGXFSZ signal.
RLIMIT_MEMLOCKThe maximum amount of memory in bytes that a process can lock into memory using mlock(2).
RLIMIT_MSGQUEUEThe maximum amount of memory in bytes that a process can allocate for POSIX message queues.
RLIMIT_NICEThe limit to which a process’s nice value (Section 8.16) can be raised to affect its scheduling priority.
RLIMIT_NOFILEThe maximum number of open files per process. Changing this limit affects the value returned by the sysconf function for its _SC_OPEN_MAX argument (Section 2.5.4). See Figure 2.17 also.
RLIMIT_NPROCThe maximum number of child processes per real user ID. Changing this limit affects the value returned for _SC_CHILD_MAX by the sysconf function (Section 2.5.4).
RLIMIT_NPTSThe maximum number of pseudo terminals (Chapter 19) that a user can have open at one time.
RLIMIT_RSSMaximum resident set size (RSS) in bytes. If available physical memory is low, the kernel takes memory from processes that exceed their RSS.
RLIMIT_SBSIZEThe maximum size in bytes of socket buffers that a user can consume at any given time.
RLIMIT_SIGPENDINGThe maximum number of signals that can be queued for a process. This limit is enforced by the sigqueue function (Section 10.20).
RLIMIT_STACKThe maximum size in bytes of the stack. See Figure 7.6.
RLIMIT_SWAPThe maximum amount of swap space in bytes that a user can consume.
RLIMIT_VMEMThis is a synonym for RLIMIT_AS.

  • The resource limits affect the calling process and are inherited by any of its children. This means that the setting of resource limits needs to be built into the shells to affect all our future processes.

#include <stdio.h>
#include <stdlib.h>
#include <sys/resource.h>

#define doit(name) pr_limits(#name, name)

void Exit(char *string)
{
    printf("%s\n", string);
    exit(1);
}

static void pr_limits(char *name, int resource)
{
    struct rlimit limit;
    unsigned long long lim;

    if (getrlimit(resource, &limit) < 0)
    {
        printf("getrlimit error for %s", name);
        exit(1);
    }
    printf("%-14s ", name);
    if (limit.rlim_cur == RLIM_INFINITY)
    {
        printf("(infinite) ");
    }
    else
    {
        lim = limit.rlim_cur;
        printf("%10lld ", lim);
    }
    if (limit.rlim_max == RLIM_INFINITY)
    {
        printf("(infinite)");
    }
    else
    {
        lim = limit.rlim_max;
        printf("%10lld", lim);
    }
    putchar((int)'\n');
}

int main()
{
    doit(RLIMIT_AS);
    doit(RLIMIT_CORE);
    doit(RLIMIT_CPU);
    doit(RLIMIT_DATA);
    doit(RLIMIT_FSIZE);
    doit(RLIMIT_MEMLOCK);
    doit(RLIMIT_MSGQUEUE);
    doit(RLIMIT_NICE);
    doit(RLIMIT_NOFILE);
    doit(RLIMIT_NPROC);
    doit(RLIMIT_RSS);
    doit(RLIMIT_SIGPENDING);
    doit(RLIMIT_STACK);

    exit(0);
}
/*
Output:
RLIMIT_AS      (infinite) (infinite)
RLIMIT_CORE             0 (infinite)
RLIMIT_CPU     (infinite) (infinite)
RLIMIT_DATA    (infinite) (infinite)
RLIMIT_FSIZE   (infinite) (infinite)
RLIMIT_MEMLOCK      65536      65536
RLIMIT_MSGQUEUE     819200     819200
RLIMIT_NICE             0          0
RLIMIT_NOFILE        1024       4096
RLIMIT_NPROC        31348      31348
RLIMIT_RSS     (infinite) (infinite)
RLIMIT_SIGPENDING      31348      31348
RLIMIT_STACK      8388608 (infinite)
*/
  • This program prints out the current soft limit and hard limit for all the resource limits supported on the system.

7.12 Summary

Exercise 1.

On an Intel x86 system under Linux, if we execute the program that prints ‘‘hello, world’’ and do not call exit or return, the termination status of the program — which we can examine with the shell—is 13. Why?

  • The return value from printf(the number of characters output) becomes the return value of main. Change the length of the string printed and see if the new length matches the return value. Not all systems exhibit this property. If you enable the ISO C extensions in gcc, then the return value is always 0, as required by the standard.

Exercise 2.

When is the output from the printf in Figure 7.3 actually output?

  • If the program is run interactively, standard output is usually line buffered, so the actual output occurs when each newline is output.
  • If standard output were directed to a file, it would be fully buffered, and the actual output wouldn’t occur until the standard I/O cleanup is performed.

Exercise 3.

Is there any way for a function that is called by main to examine the command-line arguments without
(a) passing argc and argv as arguments from main to the function or
(b) having main copy argc and argv into global variables?

  • On Linux, there is no way to do this. Copies of argc and argv are not kept in global variables like environ is.

Exercise 4.

Some UNIX system implementations purposely arrange that, when a program is executed, location 0 in the data segment is not accessible. Why?

  • This provides a way to terminate the process when it tries to dereference a null pointer.

Exercise 5.

Use the typedef facility of C to define a new data type Exitfunc for an exit handler. Redo the prototype for atexit using this data type.

typedef void Exitfunc(void);
int atexit(Exitfunc *func);

Exercise 6.

If we allocate an array of longs using calloc, is the array initialized to 0? If we allocate an array of pointers using calloc, is the array initialized to null pointers?

  • calloc initializes the memory that it allocates to all zero bits. ISO C does not guarantee that this is the same as either a floating-point 0 or a null pointer.

Exercise 7.

In the output from the size command at the end of Section 7.6, why aren’t any sizes given for the heap and the stack?

  • The heap and the stack aren’t allocated until a program is executed by one of the exec functions(Section 8.10).

Exercise 8.

In Section 7.7, the two file sizes(879443 and 8378) don’t equal the sums of their respective text and data sizes. Why?

  • The executable file(a.out) contains symbol table information that can be helpful in debugging a core file. To remove this information, use the strip(1) command. Stripping the two a.out files reduces their size to 798,760 and 6,200 bytes.

Exercise 9.

In Section 7.7, why does the size of the executable file differ so dramatically when we use shared libraries for such a trivial program?

  • When shared libraries are not used, a large portion of the executable file is occupied by the standard I/O library.

Exercise 10.

At the end of Section 7.10, we showed how a function can’t return a pointer to an automatic variable. Is the following code correct?

int f1(int val)
{
    int num = 0;
    int *ptr = &num;

    if (val == 0)
    {
        int val;
        val = 5;
        ptr = &val;
    }
    return(*ptr + 1);
}
  • The code is incorrect, since it references the automatic integer val through a pointer after the automatic variable is no longer in existence. Automatic variables declared after the left brace that starts a compound statement disappear after the matching right brace.

Please indicate the source: http://blog.csdn.net/gaoxiangnumber1

Welcome to my github: https://github.com/gaoxiangnumber1

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
在idea中用tomcat插件运行时打不开浏览器而出现这些代码"C:\Program Files (x86)\Java\jdk1.8.0_121\bin\java.exe" -Dmaven.multiModuleProjectDirectory=C:\Users\王成远\IdeaProjects\untitled -Djansi.passthrough=true -DarchetypeCatalog=internal -Dmaven.home=C:\Environment\apache-maven-3.6.1 -Dclassworlds.conf=C:\Environment\apache-maven-3.6.1\bin\m2.conf "-Dmaven.ext.class.path=C:\Program Files\JetBrains\IntelliJ IDEA 2023.1.2\plugins\maven\lib\maven-event-listener.jar" "-javaagent:C:\Program Files\JetBrains\IntelliJ IDEA 2023.1.2\lib\idea_rt.jar=51770:C:\Program Files\JetBrains\IntelliJ IDEA 2023.1.2\bin" -Dfile.encoding=UTF-8 -classpath C:\Environment\apache-maven-3.6.1\boot\plexus-classworlds-2.6.0.jar org.codehaus.classworlds.Launcher -Didea.version=2023.1.2 tomcat7:run -P !jdk-1.8 [INFO] Scanning for projects... [INFO] [INFO] ------------------------< org.example:untitled >------------------------ [INFO] Building untitled 1.0-SNAPSHOT [INFO] --------------------------------[ jar ]--------------------------------- [INFO] [INFO] >>> tomcat7-maven-plugin:2.2:run (default-cli) > process-classes @ untitled >>> [INFO] [INFO] --- maven-resources-plugin:2.6:resources (default-resources) @ untitled --- [INFO] Using 'UTF-8' encoding to copy filtered resources. [INFO] Copying 0 resource [INFO] [INFO] --- maven-compiler-plugin:3.1:compile (default-compile) @ untitled --- [INFO] Nothing to compile - all classes are up to date [INFO] [INFO] <<< tomcat7-maven-plugin:2.2:run (default-cli) < process-classes @ untitled <<< [INFO] [INFO] [INFO] --- tomcat7-maven-plugin:2.2:run (default-cli) @ untitled --- [INFO] Skipping non-war project [INFO] ------------------------------------------------------------------------ [INFO] BUILD SUCCESS [INFO] ------------------------------------------------------------------------ [INFO] Total time: 0.982 s [INFO] Finished at: 2023-06-11T09:39:45+08:00 [INFO] ------------------------------------------------------------------------ Process finished with exit code 0是什么原因
最新发布
06-12
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值