• 深入理解Linux进程管理与优化:原理、调度和资源控制详解


    理解进程

    1. 理解进程

    进程是操作系统中最基本的概念之一,理解进程对于学习操作系统非常重要。

    1.1 进程的定义

    进程是指正在运行的程序实例,它有自己的地址空间、数据栈、程序计数器等资源。

    1.2 进程的状态

    进程在不同的运行阶段会处于不同的状态,包括运行态、就绪态和阻塞态。

    1.3 进程标识符(PID)

    进程标识符(PID)是系统中唯一标识一个进程的数字,通过PID可以对进程进行管理和控制。

    进程创建与终止

    2. 进程创建与终止

    进程的创建和终止是操作系统中的重要功能之一。

    2.1 fork()系统调用

    fork()系统调用可以创建一个新的进程,新进程与原进程几乎完全相同,包括代码、数据和资源等。

    #include 
    #include 
    
    int main() {
        pid_t pid;
        
        pid = fork();
        if (pid < 0) {
            perror("Fork failed");
            return -1;
        } else if (pid == 0) {
            // 子进程
            printf("This is the child process.\n");
        } else {
            // 父进程
            printf("This is the parent process.\n");
        }
        
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    2.2 exec()系列系统调用

    exec()系列系统调用可以在一个进程中执行新的程序,替换原有的代码和数据。

    #include 
    #include 
    
    int main() {
        char *args[] = {"ls", "-l", NULL};
        
        execvp("ls", args);
        
        // 如果execvp执行成功,下面的代码不会被执行
        perror("Exec failed");
        
        return -1;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    2.3 exit()系统调用

    exit()系统调用用于终止当前进程的执行。

    #include 
    #include 
    
    int main() {
        printf("Before exit.\n");
        
        exit(0);
        
        // 下面的代码不会被执行
        printf("After exit.\n");
        
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    2.4 僵尸进程与孤儿进程

    当一个进程终止后,其父进程可以通过wait()系统调用来获取子进程的终止状态。如果父进程没有及时调用wait(),那么子进程就会变成僵尸进程。相反,如果一个子进程的父进程先于子进程终止,则子进程成为孤儿进程。

    进程调度

    3. 进程调度

    进程调度是操作系统中的核心功能之一,它决定了进程的运行顺序和分配时间。

    3.1 进程调度的基本原理

    操作系统通过进程调度算法来决定下一个要执行的进程,以提高系统性能和资源利用率。

    3.2 进程调度算法

    常见的进程调度算法包括先来先服务(FCFS)、最短作业优先(SJF)、轮转调度(RR)、优先级调度等。

    3.3 静态优先级与动态优先级

    进程可以有静态优先级和动态优先级,静态优先级是在创建进程时指定的,而动态优先级可以根据运行情况进行调整。

    进程控制

    4. 进程控制

    进程控制涉及到进程之间的通信和管理。

    4.1 进程信号

    4.1.1 信号的概念

    信号是一种进程之间通信的机制,用于通知某个特定事件发生。

    4.1.2 常见的信号

    常见的信号包括SIGINT(中断信号)、SIGTERM(终止信号)和SIGKILL(强制终止信号)等。

    4.2 进程间通信(IPC)

    进程间通信(IPC)是指进程之间交换信息和共享资源的机制。

    4.2.1 管道(pipe)
    #include 
    #include 
    
    int main() {
        int fd[2];
        char buf[256];
        
        pipe(fd);
        
        if (fork() == 0) {
            // 子进程
            close(fd[0]);
            
            write(fd[1], "Hello, pipe!", 13);
            
            return 0;
        } else {
            // 父进程
            close(fd[1]);
            
            read(fd[0], buf, sizeof(buf));
            printf("%s\n", buf);
            
            wait(NULL);
            
            return 0;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    4.2.2 命名管道(FIFO)
    $ mkfifo myfifo
    $ echo "Hello, FIFO!" > myfifo
    $ cat myfifo
    Hello, FIFO!
    
    • 1
    • 2
    • 3
    • 4
    4.2.3 共享内存(shared memory)
    #include 
    #include 
    #include 
    
    #define SHM_SIZE 1024
    
    int main() {
        int shmid;
        char *shmaddr;
        
        shmid = shmget(IPC_PRIVATE, SHM_SIZE, IPC_CREAT | 0666);
        if (shmid < 0) {
            perror("Shmget failed");
            return -1;
        }
        
        shmaddr = shmat(shmid, NULL, 0);
        if (shmaddr == (void*)-1) {
            perror("Shmat failed");
            return -1;
        }
        
        sprintf(shmaddr, "Hello, shared memory!");
        
        printf("%s\n", shmaddr);
        
        shmdt(shmaddr);
        
        shmctl(shmid, IPC_RMID, NULL);
        
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    4.2.4 信号量(semaphore)
    #include 
    #include 
    #include 
    
    union semun {
        int val;
        struct semid_ds *buf;
        unsigned short *array;
    };
    
    int main() {
        int semid;
        union semun arg;
        struct sembuf sops;
        
        semid = semget(IPC_PRIVATE, 1, IPC_CREAT | 0666);
        if (semid < 0) {
            perror("Semget failed");
            return -1;
        }
        
        arg.val = 1;
        semctl(semid, 0, SETVAL, arg);
        
        if (fork() == 0) {
            // 子进程
            sops.sem_num = 0;
            sops.sem_op = -1;
            sops.sem_flg = SEM_UNDO;
            semop(semid, &sops, 1);
            
            printf("Enter critical section.\n");
            
            sleep(5);
            
            printf("Leave critical section.\n");
            
            sops.sem_num = 0;
            sops.sem_op = 1;
            sops.sem_flg = SEM_UNDO;
            semop(semid, &sops, 1);
            
            return 0;
        } else {
            // 父进程
            sops.sem_num = 0;
            sops.sem_op = -1;
            sops.sem_flg = SEM_UNDO;
            semop(semid, &sops, 1);
            
            printf("Enter critical section.\n");
            
            sleep(3);
            
            printf("Leave critical section.\n");
            
            sops.sem_num = 0;
            sops.sem_op = 1;
            sops.sem_flg = SEM_UNDO;
            semop(semid, &sops, 1);
            
            wait(NULL);
            
            return 0;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    4.2.5 消息队列(message queue)
    #include 
    #include 
    #include 
    
    struct msgbuf {
        long mtype;
        char mtext[256];
    };
    
    int main() {
        int msqid;
        struct msgbuf buf;
        
        msqid = msgget(IPC_PRIVATE, IPC_CREAT | 0666);
        if (msqid < 0) {
            perror("Msgget failed");
            return -1;
        }
        
        buf.mtype = 1;
        sprintf(buf.mtext, "Hello, message queue!");
        
        msgsnd(msqid, &buf, sizeof(buf.mtext), 0);
        
        msgrcv(msqid, &buf, sizeof(buf.mtext), 0, 0);
        
        printf("%s\n", buf.mtext);
        
        msgctl(msqid, IPC_RMID, NULL);
        
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32

    进程监控和管理工具

    5. 进程监控和管理工具

    为了更好地管理进程,我们可以使用一些工具来监控和管理进程的执行。

    5.1 top命令

    top命令是一个动态显示系统中运行进程状态的工具,通过top命令可以查看进程的CPU占用率、内存占用率等信息。

    $ top
    
    • 1

    5.2 ps命令

    ps命令用于查看系统中的进程状态,包括进程标识符(PID)、运行状态等信息。

    $ ps -ef
    
    • 1

    5.3 kill命令

    kill命令用于向指定的进程发送信号,以实现对进程的控制和管理。

    $ kill -9 PID
    
    • 1

    5.4 nice和renice命令

    nice和renice命令用于调整进程的优先级,以改变进程的调度顺序。

    $ nice -n 10 command
    
    • 1

    5.5 nohup命令

    nohup命令用于在后台运行程序,并忽略所有挂断信号。

    $ nohup command &
    
    • 1

    进程资源限制与管理

    6. 进程资源限制与管理

    为了保证系统的正常运行,操作系统对进程的资源使用进行了限制和管理。

    6.1 进程资源限制的概念

    进程资源限制是指对进程使用的资源进行限制,包括CPU时间、内存大小等。

    6.2 ulimit命令

    ulimit命令用于设置和显示进程资源限制的值。

    $ ulimit -a
    
    • 1

    6.3 cgroup控制组

    cgroup控制组是一种可以对进程及其子进程进行资源限制和管理的机制。

    守护进程

    7. 守护进程

    守护进程是在后台运行的进程,它独立于终端并且没有控制终端。

    7.1 守护进程的定义和特点

    守护进程是一种长期运行的后台进程,通常用于提供某种服务或定期执行某些任务。

    7.2 编写守护进程的步骤

    编写守护进程的步骤包括fork()、setsid()、更改工作目录、重定向标准输入输出等。

    #include 
    #include 
    #include 
    
    int main() {
        pid_t pid;
        
        pid = fork();
        
        if (pid < 0) {
            perror("Fork failed");
            return -1;
        } else if (pid > 0) {
            // 父进程退出
            return 0;
        }
        
        setsid();
        
        chdir("/");
        
        umask(0);
        
        close(STDIN_FILENO);
        close(STDOUT_FILENO);
        close(STDERR_FILENO);
        
        open("/dev/null", O_RDONLY);
        open("/dev/null", O_WRONLY);
        open("/dev/null", O_RDWR);
        
        while (1) {
            // 守护进程的工作代码
            sleep(1);
        }
        
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38

    7.3 守护进程的启动和停止方法

    守护进程可以使用脚本来启动和停止,例如使用systemd、init.d等方式。

    实例分析与最佳实践

    8. 实例分析与最佳实践

    在实际的开发中,我们可以结合实例分析和最佳实践来更好地理解进程的使用和管理。

    8.1 多进程编程实例

    #include 
    #include 
    
    #define NUM_CHILDREN 10
    
    int main() {
        int i;
        
        for (i = 0; i < NUM_CHILDREN; i++) {
            if (fork() == 0) {
                // 子进程
                printf("Child process: %d\n", getpid());
                
                return 0;
            }
        }
        
        // 等待所有子进程终止
        for (i = 0; i < NUM_CHILDREN; i++) {
            wait(NULL);
        }
        
        // 父进程
        printf("Parent process: %d\n", getpid());
        
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27

    8.2 进程监控与自动重启

    编写一个守护进程,监控某个程序的执行状态,如果程序异常退出,则自动重启程序。

    #include 
    #include 
    #include 
    
    int main() {
        pid_t pid;
        
        while (1) {
            // 启动程序
            pid = fork();
            
            if (pid < 0) {
                perror("Fork failed");
                exit(-1);
            } else if (pid == 0) {
                execl("/path/to/program", NULL);
                perror("Execl failed");
                exit(-1);
            } else {
                wait(NULL);
                
                // 程序异常退出,等待一段时间后自动重启
                sleep(5);
            }
        }
        
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28

    8.3 避免进程泄露和资源耗尽

    编写一个守护进程,定期检查并清理僵尸进程和孤儿进程,避免进程泄露和资源耗尽。

    #include 
    #include 
    #include 
    #include 
    
    void reap_zombies() {
        pid_t pid;
        int status;
        
        while ((pid = waitpid(-1, &status, WNOHANG)) > 0) {
            // 处理僵尸进程
            if (WIFEXITED(status)) {
                printf("Child process %d exited normally with exit code: %d\n", pid, WEXITSTATUS(status));
            } else if (WIFSIGNALED(status)) {
                printf("Child process %d terminated by signal: %d\n", pid, WTERMSIG(status));
            }
        }
    }
    
    int main() {
        pid_t pid;
        
        while (1) {
            pid = fork();
            
            if (pid < 0) {
                perror("Fork failed");
                return -1;
            } else if (pid == 0) {
                // 子进程
                printf("Child process: %d\n", getpid());
                
                return 0;
            } else {
                // 父进程
                wait(NULL);
                
                // 清理僵尸进程
                reap_zombies();
            }
        }
        
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44

    8.4 进程优化技巧

    在编写程序时,可以使用一些进程优化技巧来提高程序的性能和效率。

    例如,避免频繁创建和销毁进程、合理使用进程间通信机制、优化进程资源的使用等。

    总结

    本文介绍了进程的相关知识和管理方法。首先,文章解释了进程的定义、状态和标识符(PID)。接着,详细说明了进程的创建与终止方式,包括fork()系统调用、exec()系列系统调用和exit()系统调用,以及僵尸进程和孤儿进程的问题。

    进程调度是文章的下一个重点内容,包括进程调度的基本原理、调度算法,以及静态优先级和动态优先级的概念。

    文章还介绍了进程控制方面的知识,包括进程信号的概念和常见的信号类型,以及进程间通信的方式,如管道、命名管道、共享内存、信号量和消息队列。

    此外,文章列举了几个进程监控和管理工具,包括top命令、ps命令、kill命令、nice和renice命令以及nohup命令。

    进程资源限制与管理也是关注的领域,文章介绍了进程资源限制的概念,并提到了ulimit命令和cgroup控制组。

    在守护进程部分,文章解释了守护进程的定义、特点,以及编写守护进程的步骤和启动停止方法。

    最后,文章给出了一些实例分析和最佳实践,包括多进程编程实例、进程监控与自动重启、避免进程泄露和资源耗尽,以及进程优化技巧。

    通过本文的学习,读者可以全面了解进程的概念、创建与终止、调度、控制、监控和管理等方面的知识,并且能够应用到实际的开发和系统管理中。

  • 相关阅读:
    python免杀初探
    使用python中的pandas对csv文件进行拆分
    【FFH】啃论文俱乐部---世界上最快的C语言JSON库
    代码随想录训练营Day1:二分查找与移除元素
    TouchGFX之文本和字体
    Java String类(超级详细!)
    HoloLens第三视角开发【保姆级教程】【踩坑记录】
    【小航的算法日记】哈希
    观察者模式的运用——消息队列
    156 - Ananagrams (UVA)
  • 原文地址:https://blog.csdn.net/qq_41308872/article/details/133158126