Linux System Programming 学习笔记(五) 进程管理

1. 进程是unix系统中两个最重要的基础抽象之一(另外一个是文件)

A process is a running program
A thread is the unit of activity inside of a process
 
the virtualization of memory is associated with the process, the threads all share the same memory address space
 

2. pid

The idle process has the pid 0
The first process that the kernel executes after booting the system, called the init process, has the pid 1
 
默认状况下,内核规定一个最大的进程ID值:32768(16位有符号数)
内核使用 严格的线性方式为进程分配PID,若是当前全部分配的PID值最大为17,那么下一次分配的PID值为18
进程层级:每个进程(init进程除外)都是从另外一个进程引生的
Children normally belong to the same process groups as their parents
 

3. 运行新进程

In Unix, the act of loading into memory and executing a program image is separate from the act of creating a new process
exec system call:loads a binary program into memory, replacing the previous contents of the address space, and begins execution of  the new program
fork system call:create a new process
to execute a new program in a new process :first a fork to create a new process, and then an exec to load a new binary into that process
 
exec系列函数中, 只有execve是系统调用,其他函数都是根据execve进行封装的C语言库函数
 
fork系统调用以后,子进程与父进程几乎彻底相同,除了如下:
a.进程ID不一样
b.挂起的信号被清除,并不被子进程继承
c.父进程的文件锁不被子进程继承
 
This “fork plus exec” combination is frequent and simple:
 
pid_t pid = fork();
if (pid == -1) {
    fprintf(stderr, "fork error\n");
}

/* the child */
if (pid == 0) {
    const char* args[] = {"grep", NULL};
    int ret = execv("bin/grep", args);
    if (ret == -1) {
        fprintf(stderr, "execv error\n");
        exit(EXIT_FAILURE);
    }
}

 

4. copy-on-write

早期Unix中,实现fork很是简单,the kernel created copies of all internal data structures, duplicated the process's page table entries, and then performed a
page-by-page copy of the parent's address space into the child's new address space, 这样拷贝实现无疑是费时的
 
copy-on-write是一种减少资源复制开销的延迟策略。
若是多个用户请求对它所拥有的资源进行读操做,资源副本的拷贝是不须要进行的,每一个用户只须要操做一个指向共同资源的指针便可。只要没有用户试图修改这个资源,那么副本的拷贝开销就能够避免。若是有一个用户确实须要修改这个资源,那么直到这个时候,资源才复制一份,复制的一份资源被交给须要修改的用户,其他全部用户继续共享原初的资源
 
在虚拟内存的具体示例中,copy-on-write是之内存页为单位进行的。
进程的内存页首先被标记为 read-only and copy-on-write, 若是有进程须要修改这个内存页,就产生页错误,就在这时,内核复制该内存页,而且内存页的copy-on-write标记被清除。
现代机器体系结构都硬件层面上支持copy-on-write
 

5. exit

当一个进程终止时,内核清除它包含的全部资源:分配的内存、打开的文件、信号量等
结束一个程序最经典的方式不是显式调用exit,而是 "falling off the end" of the program
 
当一个进程终止时, 内核向其父进程发送 SIGCHLD 信号。默认状况下,该信号被忽略,父进程也能够调用 sigaction 捕捉该信号
 

6. wait

#include <sys/types.h>
#include <sys/wait.h>

/* wait() returns the pid of a terminated child or −1 on error. */
pid_t wait (int *status);


pid_t waitpid (pid_t pid, int *status, int options);
当pid==-1时,waitpid等待任一子进程,等同于 wait,即 wait (&status) 等同于 waitpid (−1, &status, 0)
 

7. system

#define _XOPEN_SOURCE    /* if we want WEXITSTATUS, etc. */
#include <stdlib.h>
int system (const char *command);
It is common to use system() to run a simple utility or shell script, often with the explicit goal of simply obtaining its return value
If command is NULL, system() returns a nonzero value if the shell /bin/sh is available, and 0 otherwise
 
here is a sample implementation for system():
 
/* my_system - sync spwans and waits for the command
 * /bin/sh -c <cmd> .
 *
 * Return -1 on error of any sort, or the exit code from
 * the launched process. Does not block or ignore any signals.
 */

int my_system(const char* cmd)
{
    int status;
    pid_t pid = fork();
    if (pid == -1) {
        fprintf(stderr, "fork error\n");
        return -1;
    } else if (pid == 0) {
        const char* argv[4];
        argv[0] = "sh";
        argv[1] = "-c";
        argv[2] = "cmd";
        argv[3] = NULL;
        execv("bin/sh", argv);
       exit(-1);
    }
    if (waitpid(pid, &status, 0) == -1) {
        return -1;
    } else if (WIFEXITED(status)) {
        return WEXITSTATUS(status);
    }
    return -1;
}

 

8. 僵尸进程

a process that has terminated but has not yet been waited upon by its parent is called a “zombie.”
Zombie processes continue to consume system resources, although only a small percentage
若是一个父进程fork产生了子进程,那么该父进程就必须负责wait其子进程(回收僵尸子进程的所占资源)
若是一个父进程在终止以前没有wait其子进程呢? 内核会视察其全部子进程,并将这些子进程挂在init进程之下(确保任何进程在任什么时候候都有一个父进程),init进程会周期性wait其全部子进程,这些子进程最终仍是会被回收资源,而不会长期以僵尸进程形式存在
 

9. Users and Groups

#include <sys/types.h>
#include <unistd.h>

int setuid (uid_t uid);
int setgid (gid_t gid);

uid_t getuid (void);
gid_t getgid (void);
Each process is a member of a process group,Process groups that contain more than one process are generally implementing job control
 
A command on the shell such as this one:
/*  one process group containing three processes */
$ cat ship-inventory.txt | grep booty | sort
 

10. 守护进程

A daemon is a process that runs in the background, not connecting to any controlling terminal
一般在系统启动时开始运行,而且以root权限运行,处理系统级任务,名字后缀通常为d
 
一个守护进程必须知足:a. 是init进程的子进程   b. 不容许与终端链接
相关文章
相关标签/搜索