# System Programing Concept ## 3.1 System Calls A system call is a controlled entry point into the kernel, allowing a process to request that the kernel perform some action on the process’s behalf. Before going into the details of how a system call works, we note some general points: * A system call changes the processor state from user mode to kernel mode, so that the CPU can access protected kernel memory. * The set of system calls is fixed. Each system call is identified by a unique number. (This numbering scheme is not normally visible to programs, which identify system calls by name.) * Each system call may have a set of arguments that specify information to be transferred from user space (i.e., the process’s virtual address space) to kernel space and vice versa. Figure 3-1 illustrates the above sequence using the example of the execve() system call. ![](https://i.imgur.com/A2POJnQ.png) The information given in the preceding paragraphs is more than we’ll usually need to know for the remainder of this book. However, it illustrates the important point that, even for a simple system call, quite a bit of work must be done, and thus system calls have a small but appreciable overhead. ## 3.2 Library Functions Many library functions don’t make any use of system calls (e.g., the stringmanipulation functions). On the other hand, some library functions are layered on top of system calls. For example, the fopen() library function uses the open() system call to actually open a file. Often library functions are designed to provide a more caller-friendly interface than the underlying system call. For example, the printf() function provides output formatting and data buffering, whereas the write() system call just outputs a block of bytes. Similarly, the malloc() and free() functions perform various bookkeeping tasks that make them a much easier way to allocate and free memory than the underlying brk() system call. ## The Standard C Library; The GNU C Library (glibc) N/A, reference by yourself if interests in. ## Handling Errors from System Calls and Library Functions Almost every system call and library function returns some type of status value indicating whether the call succeeded or failed. This status value should always be checked to see whether the call succeeded. If it did not, then appropriate action should be taken—at the very least, the program should display an error message warning that something unexpected occurred. Although it is tempting to save typing time by excluding these checks (especially after seeing examples of UNIX and Linux programs where status values are not checked), it is a false economy. Many hours of debugging time can be wasted because a check was not made on the status return of a system call or library function that “couldn’t possibly fail.” ### Handling system call errors When a system call fails, it sets the global integer variable errno to a positive value that identifies the specific error. Successful system calls and library functions never reset errno to 0, so this variable may have a nonzero value as a consequence of an error from a previous call. Furthermore, SUSv3 permits a successful function call to set errno to a nonzero value (although few functions do this). Therefore, when checking for an error, we should always first check if the function return value indicates an error, and only then examine errno to determine the cause of the error. Exception: getpriority() > void perror(const char *msg); > char *strerror(int errnum); ### Handling errors from library functions For our purposes, library functions can be divided into the following categories: * Some library functions return error information in exactly the same way as system calls: a –1 return value, with errno indicating the specific error. An example of such a function is remove(), which removes a file (using the unlink() system call) or a directory (using the rmdir() system call). Errors from these functions can be diagnosed in the same way as errors from system calls. * Some library functions return a value other than –1 on error, but nevertheless set errno to indicate the specific error condition. For example, fopen() returns a NULL pointer on error, and the setting of errno depends on which underlying system call failed. The perror() and strerror() functions can be used to diagnose these errors. * Other library functions don’t use errno at all. The method for determining the existence and cause of errors depends on the particular function and is documented in the function’s manual page. For these functions, it is a mistake to use errno, perror(), or strerror() to diagnose errors. ## Notes on the Example Programs in This Book Some header and source sample code which diagnose and print the error. ## Portability Issues ### Feature Test Macros N/A ### System Data Types Various implementation data types are represented using standard C types, for example, process IDs, user IDs, and file offsets. Although it would be possible to use the C fundamental types such as int and long to declare variables storing such information, this reduces portability across UNIX systems, for the following reasons: * The sizes of these fundamental types vary across UNIX implementations (e.g.,a long may be 4 bytes on one system and 8 bytes on another), or sometimes even in different compilation environments on the same implementation. Furthermore, different implementations may use different types to represent the same information. For example, a process ID might be an int on one system but a long on another. * Even on a single UNIX implementation, the types used to represent information may differ between releases of the implementation. Notable examples on Linux are user and group IDs. On Linux 2.2 and earlier, these values were represented in 16 bits. On Linux 2.4 and later, they are 32-bit values. To avoid such portability problems, SUSv3 specifies various standard system data types, and requires an implementation to define and use these types appropriately. Each of these types is defined using the C typedef feature. For example, the pid_t data type is intended for representing process IDs, and on Linux/x86-32 this type is defined as follows: typedef int pid_t; Most of the standard system data types have names ending in _t. Many of them are declared in the header file <sys/types.h>, although a few are defined in other header files. An application should employ these type definitions to portably declare the variables it uses. For example, the following declaration would allow an application to correctly represent process IDs on any SUSv3-conformant system: pid_t mypid; ### Miscellaneous Portability Issues #### Initializing and using structures Each UNIX implementation specifies a range of standard structures that are used in various system calls and library functions. As an example, consider the sembuf structure, which is used to represent a semaphore operation to be performed by the semop() system call: ``` struct sembuf { unsigned short sem_num; /* Semaphore number */ short sem_op; /* Operation to be performed */ short sem_flg; /* Operation flags */ }; ``` Although SUSv3 specifies structures such as sembuf, it is important to realize the following: * In general, the order of field definitions within such structures is not specified. * In some cases, extra implementation-specific fields may be included in such structures. Consequently, it is not portable to use a structure initializer such as the following: ``` struct sembuf s = { 3, -1, SEM_UNDO }; ``` Although this initializer will work on Linux, it won’t work on another implementation where the fields in the sembuf structure are defined in a different order. To portably initialize such structures, we must use explicit assignment statements, as in the following: ``` struct sembuf s; s.sem_num = 3; s.sem_op = -1; s.sem_flg = SEM_UNDO; ``` If we are using C99, then we can employ that language’s new syntax for structure initializers to write an equivalent initialization: ``` struct sembuf s = { .sem_num = 3, .sem_op = -1, .sem_flg = SEM_UNDO }; ``` ## Summary System calls allow processes to request services from the kernel. Even the simplest system calls have a significant overhead by comparison with a user-space function call, since the system must temporarily switch to kernel mode to execute the system call, and the kernel must verify system call arguments and transfer data between user memory and kernel memory. The standard C library provides a multitude of library functions that perform a wide range of tasks. Some library functions employ system calls to do their work; others perform tasks entirely within user space. On Linux, the usual standard C library implementation that is used is glibc. Most system calls and library functions return a status indicating whether a call has succeeded or failed. Such status returns should always be checked. We can improve the portability of system programs by using the system data types defined in various standards, rather than native C types. SUSv3 specifies a wide range of system data types that an implementation should support and that an application should employ. # Q&A errno who set 今天有個問題很有趣 "kernel到底算不算是一個process" 回家谷哥了一下 > In user-space we can create a process by executing program or calling fork inside a program, Kernel will create process descriptor(task_struct) for each user-space process. > In kernel space is there any any concept called process, if so how they will be created? > As per my understanding kernel threads will be created in kernel space using kernel_thread() etc, kernel_thread() is internally calling do_fork(), so kernel threads also represented using task_struct? > If both user-space process and kernel space threads are represented using task_struct, then how scheduler will schedule the user-space process and kernel space thread? > 這個回答不錯 > Kernel is a comprehensive/complex process, that handles all other tasks(threads) in both kernel and user spaceKernel's PID is 0, and that triggers all other processes(Kernel/User), directly(children) and indirectly(child handling other subsequent children).Kernel runs the scheduler, which is the core part of task/process management. > > To see the kernel Processes that are spawned by kernel `ps -caefL | grep -v grep | grep "\[\|PPID" | grep root` ![](https://i.imgur.com/aTxc6Iv.png) > You will not be able to see the line of 0th PID, which is the kernel(the driving force) > > Good to say that kernel is, "device driver for the CPU" USER是root的process都是kernel space的process or(thread) 在Linux process跟thread的界線很模糊 兩者都是用task_struct在maitain的,只是shared的東西不一樣,但可以看到kernel thread也都有自己的一份pid, 所以kernel底下那些到底是process還是thread? > **Processes and threads in user-space diffen in what they share. Threads share FDs and memory (and a few other things), while processes do not (unless explicitly shared). There is only one kernel memory space, so there is no sense in talking about kernel processes, only kernel threads. – rodrigo Jan 25 '15 at 16:30** > 就以記憶體空間來看,還是稱為kernel thread比較有sense 所以在linux上process跟thread到底怎麼分 可以看一下Jserv大大的見解 https://hackmd.io/@sysprog/linux-process 節錄一段 ![](https://i.imgur.com/HJQxv9d.png) 只能說有夠混淆的 # 結論: * linux kernel is more than a process. more like a manager * User 為root的那些kernel process,就是kernel thread 有理解錯誤請更正