IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 系统运维 -> 进程体系与进程管理 -> 正文阅读

[系统运维]进程体系与进程管理

一、进程控制块PCB

task_struct结构体:

  • 进程id:系统中每个进程有唯一的id,在c语言中用pid_t类型表示,就是一个非负整数。

  • 进程的状态:有运行,挂起,停止,僵尸等状态。

  • 进程切换时需要保存和恢复的一些CPU寄存器。

  • 描述虚拟地址空间的信息。

  • 描述控制终端的信息。

  • 当前工作目录。

  • umask掩码。

  • 文件描述符表,包含很多指向file结构体的指针。

  • 和信号相关的信息。

  • 用户id和组id。

  • 控制终端、session和进程组。

  • 进程可以使用的资源上限。

二、进程控制fork

1.fork作用:

根据一个现有的进程复制出一个新进程,原来的进程称为父进程,新进程称为子进程。

系统中运行着很多进程,这些进程都是从最初只有一个进程开始一个一个复制出来的。

2.在shell下输入命令运行一个程序,因为shell进程在读取用户输入的命令之后会调用fork夫指出一个新的shell进程。

fork调用失败返回-1。

fork在子进程中返回0,子进程可以调用getpid函数得到自己的进程id,也可调用getpid函数得到父进程id。

在父进程中用getpid得到自己的进程id,要想得到子进程id,只有将fork返回值记录下来!

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

int main(void) {
    char *msg;
    int n;
    pid_t pid = fork();
    if (pid < 0) {
        perror("fork");
        exit(1);
    }
    if (pid == 0) {
        n=6;
        while (n > 0) {
            printf("child self=%d, parent=%d\n", getpid(), getppid());
            sleep(1);
            n--;
        }
    } else {
        n = 3;
        while (n > 0) {
            printf("father self=%d, parent=%d\n", getpid(), getppid());
            sleep(1);
            n--;
        }
    }
    return 0;
}

3.创建10个子进程,并打印他们的pid和ppid?

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

int main(void) {
    char *msg;
    int n;
    for (int i = 0; i < 10; i++) {
        pid_t pid = fork();
        if (pid < 0) {
            perror("fork");
            exit(1);
        }

        if (!pid) {
            sleep(1);
            printf("child[%d], self=%d, parent=%d\n", i, getpid(), getppid());
            break;
        }
    }
    return 0;
}

4.gdb如何调试多进程?

三、exec函数族

1.当进程调用一种exec函数时,该进程的用户空间代码和数据完全被新程序替换,从新程序的启动例程开始执行。

如果调用成功则加载新的程序从启动代码开始执行,不在返回,如果出错返回-1。

exec函数只有出错的返回值而没有成功的返回值。

exec系统调用执行新程序时会把命令行参数和环境变量表传递给main函数。

和命令行参数argv类似,环境变量表也是一个字符串数组。

name=value

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

int main(void) {
    printf("path value = [%s]\n", getenv("PATH"));
    setenv("PATH", "hello", 1);
    printf("path value = [%s]\n", getenv("PATH"));

    // extern char **environ;
    // for (int i = 0; environ[i]; i++) {
    //     printf("%s\n", environ[i]);
    // }
    return 0;
}

①不带字母p(path)的exec函数第一个参数必须是程序的相对路径或绝对路径,如:“/bin/ls”或“./a.out”

②对于带字母p的函数:如果参数中包含/,将其视为路径名。否则视为不带路径的程序名,在PATH环境变量的目录列表中搜索这个程序。

③对于以e(environment)结尾的exec函数,可以把一份新的环境表量表与其他exec仍使用当前的环境变量表执行新程序。

2.实现exec族函数调用命令

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

int main(void) {
    execlp("ls", "ls", "-a", "-l", NULL);
    perror("exec");
    exit(1);
    return 0;
}
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>

int main(void) {
    char *arr[] = {"hello", "-a", "-l", NULL};
    execvp("ls", arr);
    perror("exec");
    exit(1);
}

3.实现流的重定向

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>
#include <unistd.h>

int main(int argc, char *argv[]) {
    if (argc < 3) {
        printf("usage:cmd + inputfile + outputfile\n");
        return 1;
    }
    int fd = open(argv[1], O_RDONLY);
    if (fd < 0) {
        perror("open read");
        exit(1);
    }
    dup2(fd, 0);
    close(fd);
    fd = open(argv[2], O_WRONLY | O_CREAT, 0644);
    if (fd < 0) {
        perror("open write");
        exit(1);
    }
    dup2(fd, 1);
    close(fd);
    execl("./upper", "./upper", NULL);
    perror("exec");
    return 0;
}

四、wait和waitpid函数

1.一个进程在终止时会关闭所有文件描述符,释放在用户空间分配的内存,但它的PCB还保留着,内核在其中保存了一些信息:

如果正常终止则保存着退出状态,如果异常终止保存着导致该进程终止的信号是哪个。

父进程调用wait或waitpid获取这些信息,然后彻底清除掉这个进程。

一个进程的退出状态可以在shell中用特殊变量$?查看,因为shell是它的父进程,当终止时,shell调用wait或waitpid得到它的退出状态同时彻底清除掉这个进程。

①如果一个进程已经终止,但它的父进程尚未调用wait或waitpid对它进行清理,这时的进程状态称为僵尸进程。可以用ps?u查看进程状态。

②如果用kill -9去杀掉一个僵尸进程:僵尸进程还在杀不了。但杀父进程是可以的。

?

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>

int main(void) {
    pid_t pid = fork();
    if (pid < 0) {
        perror("fork");
        exit(1);
    }
    if (pid == 0) {
        int n = 5;
        while (n > 0) {
            printf("Is child process\n");
            sleep(1);
            n--;
        }
    } else {
        int stat_val;
        waitpid(pid, &stat_val, 0);
        if (WIFEXITED(stat_val)) {
            printf("child exited with code: %d\n", WEXITSTATUS(stat_val));
        } else if (WIFSIGNALED(stat_val)) {
            printf("child terminated abnormally, signal is %d\n %d\n", WTERMSIG(stat_val));
        }
    }
    return 0;
}

2.父进程调用wait或waitpid可能会:

①阻塞(所有子进程还在运行)

②带子进程的终止信息立即返回(如果一个子进程已终止,正等待父进程读取其终止信息)

③出错立即返回(没有任何子进程)

3.wait VS?waitpid

如果父进程的所有子进程都在运行,调用wait将使父进程阻塞,而调用waitpid如果在options参数中指定WNOHANG可使父进程不阻塞而立即返回0。

wait等待任一停止的子进程,而waitpid通过pid参数指定等待哪一个子进程。

五、进程间通信

1.IPC:?interProcess?communication

每个进程各自有不同的用户地址空间,任何一个进程的全局变量在另一个进程中都看不到,所以进程之间要交换数据必须通过内核。

在内核开辟一块缓冲区,进程1把数据从用户空间拷贝到内核缓冲区,进程2从内核缓冲区把数据读走,内核提供的这种机制称为进程间通信。

2.管道

①管道是一种最基本的IPC机制,由pipe函数创建。

调用pipe函数时在内核开辟一块缓冲区(管道)用于通信,有一个读端一个写端,然后通过filedes参数传出给用户程序两个文件描述符。

filedes[0]指向管道读端,filedes[1]指向管道写端

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>

int main(void) {
    pid_t pid;
    int fd[2], n;
    char buf[20];
    if (pipe(fd) < 0) {
        perror("pipe");
        exit(1);
    }
    pid = fork();
    if (pid < 0) {
        perror("fork");
        exit(1);
    }
    if (pid > 0) {
        close(fd[0]);
        write(fd[1], "hello pipe\n", 11);
        wait(NULL);
    } else {
        close(fd[1]);
        sleep(1);
        n = read(fd[0], buf, 20);
        write(1, buf, n);
    }
    return 0;
}

②使用管道注意以下4中特殊情况(假设都是阻塞I/O操作,没有设置O_NONBLOCK标志):

  • 如果所有指向管道写端的文件描述符关闭了,而仍然有进程从管道的读端读数据,那么管道中神域数据都被读取后,再次read会返回0,像读到文件末尾一样。

  • 如果有指向管道写端的文件描述符没有关闭,而持有管道写端的进程也没有向管道写数据,这时有进程从管道读端读数据,那么管道中剩余数据被读取后,再次read会阻塞,直到管道中有数据可读才读取数据并返回。

  • 如果所有指向管道读端的文件描述符关闭了,这时有进程向管道的写端write,那么该进程会收到信号SIGPIPE,通常会导致进程异常终止。

  • 如果有指向管道读端的文件描述符没关闭,而持有管道读端的进程也没从管道读数据,这时有进程向管道写端写数据,那么在管道被写满时再次write会阻塞,知道管道中有空位置才写入数据并返回。

    #include <stdio.h>
    #include <unistd.h>
    #include <stdlib.h>
    #include <sys/types.h>
    #include <sys/wait.h>
    
    int main(void) {
        pid_t pid;
        int fd[2], n;
        char buf[20];
        char test[1024];
        if (pipe(fd) < 0) {
            perror("pipe");
            exit(1);
        }
        pid = fork();
        if (pid < 0) {
            perror("fork");
            exit(1);
        }
        if (pid > 0) {
            close(fd[0]);
            for (int i = 0; i< 100; i++) {
                write(fd[1], test, 1024);
                printf("i=%d\n", i);
            }
            wait(NULL);
        } else {
            close(fd[1]);
            sleep(10);
        }
        return 0;
    }

  系统运维 最新文章
配置小型公司网络WLAN基本业务(AC通过三层
如何在交付运维过程中建立风险底线意识,提
快速传输大文件,怎么通过网络传大文件给对
从游戏服务端角度分析移动同步(状态同步)
MySQL使用MyCat实现分库分表
如何用DWDM射频光纤技术实现200公里外的站点
国内顺畅下载k8s.gcr.io的镜像
自动化测试appium
ctfshow ssrf
Linux操作系统学习之实用指令(Centos7/8均
上一篇文章      下一篇文章      查看所有文章
加:2022-02-06 14:09:05  更:2022-02-06 14:10:45 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/16 5:42:20-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码