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 小米 华为 单反 装机 图拉丁
 
   -> 系统运维 -> Linux系统程序设计——进程 -> 正文阅读

[系统运维]Linux系统程序设计——进程

1 进程启动和终止

  • c 程序启动过程
    • 内核启动特色例程
    • 启动例程
      • 在进程的main函数执行之前内核会启动
      • 该例程放置在/lib/libc.so.***中
      • 编译器在编译时会将启动例程编译进可执行文件中
    • 启动例程作用
      • 搜集命令行的参数传递给main函数中的argc和argv
      • 搜集环境信息构建环境表并传给main函数
      • 登记进程的终止函数
  • 进程终止
    • 正常终止
      • 从main函数返回
      • 调用exit(标准c库函数)
      • 调用_exit或_Exit(系统调用)
      • 最后一个线程从其启动例程返回
      • 最后一个线程调用pthread_exit
    • 异常终止
      • 调用abort
      • 接受到一个信号并终止
      • 最后一个线程对取消请求做处理响应
    • 进程返回
      • 通常程序运行成功返回0,否则返回非0
      • 在shell中可以查看进程返回值(echo $?)
  • 进程启动和终止过程 在这里插入图片描述
  • 终止函数登记
    在这里插入图片描述

2 进程状态、进程创建和进程分类

2.1 进程查看和进程状态

  • 查看当前进程
    ps
    
  • 查看所有进程
    ps -ef
    
  • 查看进程状态
    ps -aux
    
    在这里插入图片描述
    在这里插入图片描述
  • 进程状态stat
    在这里插入图片描述

2.2 进程调度和进程状态变化

  • 进程调度
    在这里插入图片描述
  • 进程状态变化关系
    在这里插入图片描述

2.3 进程标识

在这里插入图片描述

2.4 创建进程

在这里插入图片描述

/*******************************************************************
 * Description: fork建立子进程例程
 * 在父进程中fork返回的是子进程的pid
 * 子进程(在子进程中返回的是0
 * 父子进程不一谁先执行,根据系统调度算法决定
 *******************************************************************/
int example2()
{
    printf("pid: %d\n", getpid());

    pid_t pid;
    pid = fork(); //创建子进程

    //在fork之后会运行两个进程(父进程和子进程)
    if (pid < 0) {
        perror("fork error\n");
    } else if (pid > 0) { //父进程(在父进程中fork返回的是子进程的pid)
        printf("I am parent process,pid is %d,ppid is %d, fork return is %d\n", getpid(), getppid(), pid);
    } else { //子进程(在子进程中返回的是0)
        printf("I am child process,pid is %d,ppid is %d, fork return is %d\n", getpid(), getppid(), pid);
    }
    printf("pid=%d\n", getpid());
    sleep(1);
    return 0;
}

在这里插入图片描述

/*******************************************************************
 * Description: 父子进程交替运行
 *******************************************************************/
int example3()
{
    printf("current pid: %d\n", getpid());

    pid_t pid;
    pid = fork(); //创建子进程

    //在fork之后会运行两个进程(父进程和子进程)
    if (pid < 0) {
        perror("fork error\n");
    } else if (pid > 0) { //父进程
        for (int i = 0; i < 10; i++) {
            printf("I am parent process,pid is %d,ppid is %d, fork return is %d\n", getpid(), getppid(), pid);
            sleep(1);
        }
    } else { //子进程
        for (int i = 0; i < 10; i++) {
            printf("I am child process,pid is %d,ppid is %d, fork return is %d\n", getpid(), getppid(), pid);
            sleep(1);
        }
    }

    return 0;
}
  • 父进程fork子进程,子进程会继承父进程的一些信息。数据段、堆、栈是复制过去的,物理内存各自有各自的空间。
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
/*******************************************************************
 * Description: 子进程的内存、堆、栈与父进程的关系
 *******************************************************************/
int g_v = 30; //全局变量存放在数据段
int example4()
{
    int a_v = 30; //局部变量存放在栈中
    static int s_v = 30; //静态变量存放在数据段
    printf("current pid: %d\n", getpid());

    pid_t pid;
    pid = fork(); //创建子进程

    //在fork之后会运行两个进程(父进程和子进程)
    if (pid < 0) {
        perror("fork error\n");
    } else if (pid > 0) { //父进程

        g_v = 40;
        a_v = 40;
        s_v = 40;
        printf("I am parent process,pid is %d,ppid is %d, fork return is %d\n", getpid(), getppid(), pid);

        printf("g_v: %p, a_v: %p, s_v: %p\n", &g_v, &a_v, &s_v);

    } else { //子进程
        g_v = 50;
        a_v = 50;
        s_v = 50;
        printf("I am child process,pid is %d,ppid is %d, fork return is %d\n", getpid(), getppid(), pid);

        printf("g_v: %p, a_v: %p, s_v: %p\n", &g_v, &a_v, &s_v);
    }
    printf("pid: %d\n", getpid());

    return 0;
}

在这里插入图片描述
可以观察到,父子进程的数据段、堆、栈中的内容被修改,而内存地址是相同的,可以知道fork出的子进程会复制父进程的虚拟内存,但不会复制物理内存。如下图,可直观描述父子进程的内存关系。
在这里插入图片描述

/****************************************************************
 * Description: 父进程打开文件,设置偏移量,子进程追加内容
 * ***************************************************************/
int example5(int argc, char* argv[])
{
    if (argc < 2) {
        fprintf(stderr, "usage: %s file [exit|_exit|return]\n", argv[0]);
        exit(1);
    }
    int fd = open(argv[1], O_WRONLY);
    if (fd < 2) {
        perror("open error!\n");
        exit(1);
    }
    pid_t pid;
    pid = fork(); //创建子进程

    if (pid < 0) {
        perror("fork error\n");
    } else if (pid > 0) { //父进程
        //父进程将文件偏移量调整到文件尾部
        if (lseek(fd, 0L, SEEK_END) < 0) {
            perror("lseek error!");
            exit(1);
        }
    } else { //子进程
        //子进程从文件尾部追加内容
        char* str = "hello world\n";
        ssize_t size = strlen(str) * sizeof(char);
        sleep(3); //等待父进程设置偏移量完成

        //此处的fd是从父进程中复制过来的
        //但是和父进程中的fd都是指向同一个文件
        if (write(fd, str, size) != size) {
            perror("write error!\n");
            exit(1);
        }
    }
    printf("pid: %d finish \n", getpid());
    //父子进程都要关闭各自的文件描述符
    close(fd);
    return 0;
}

  • 如果连续fork() n次,会产生2的n次方个子进程
    在这里插入图片描述

2.6 进程链和进程扇

在这里插入图片描述

  • 创建进程链
/****************************************************************
 * Description: 进程链例程
 * ***************************************************************/
int example6(int argc, char* argv[])
{
    int counter = 0;
    if (argc < 2) { //没有指定创建多少个进程,默认为2
        counter = 2;
    } else {
        counter = atoi(argv[1]); //字符转换成整形
    }
    pid_t pid;
    for (int i = 1; i < counter; i++) {
        pid = fork();
        if (pid < 0) {
            perror("fork error!\n");
        } else if (pid > 0) {
            //父进程退出循环,由子进程创建子进程
            break;
        }
    }
    printf("pid: %d ,ppid: %d\n", getpid(), getppid());
    while (1) {
        sleep(1);
    }
    return 0;
}
  • 创建进程扇
/****************************************************************
 * Description: 进程链例扇
 * ***************************************************************/
int example7(int argc, char* argv[])
{
    int counter = 0;
    if (argc < 2) { //没有指定创建多少个进程,默认为2
        counter = 2;
    } else {
        counter = atoi(argv[1]); //字符转换成整形
    }
    pid_t pid;
    for (int i = 1; i < counter; i++) {
        pid = fork();
        if (pid < 0) {
            perror("fork error!\n");
        } else if (pid == 0) {
            //子进程退出循环,由父进程创建子进程
            break;
        }
    }
    printf("pid: %d ,ppid: %d\n", getpid(), getppid());
    while (1) {
        sleep(1);
    }
    return 0;
}
ps -ef | grep 进程名   #查看特定的进程
pstree     #进程树,查看父子进程关系

2.7 守护进程、孤儿进程、僵尸进程

  • 守护进程
    在这里插入图片描述
  • 孤儿进程:
    父进程结束,子进程就变成了孤儿进程,会由1号进程(init进程)领养。
  • 僵尸进程:
    子进程退出了,但是父进程没有用wait或waitpid去获取子进程的状态信息,那么子进程的进程描述符仍然保存在系统中,这种进程称为僵尸进程。
    在这里插入图片描述

3 进程的相关系统调用

3.1 wait函数

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

void out_status(const int status)
{
    if (WIFEXITED(status)) {
        printf("normal exit: %d\n", WEXITSTATUS(status));
    } else if (WIFSIGNALED(status)) {
        printf("abnormal exit: %d\n", WTERMSIG(status));
    } else if (WIFSTOPPED(status)) {
        printf("stop exit: %d\n", WSTOPSIG(status));
    } else {
        printf("unknow exit\n");
    }
}
/******************************************************************************************************************
 * Description: wait()回收线程资源,通过宏判断进程终止类型
 * WIFEXITED(status)、WIFSIGNALED(status)、WIFSTOPPED(status)判断是否发生相应类型的终止
    返回值为true/false
 * WEXITSTATUS(status)、WTERMSIG(status)、WSTOPSIG(status)能根据status解析处具体的终止信息
    如果是正常终止,则返回exit传入的数字
 * ****************************************************************************************************************/
int example8()
{
    int status;
    pid_t pid;
    if ((pid = fork()) < 0) {
        perror("fork error\n");
        exit(1);
    } else if (pid == 0) {
        printf("pid: %d, ppid: %d\n", getpid(), getppid());
        exit(3); //子进程终止运行(正常终止)
    }
    //父进程阻塞,等待子进程结束并回收
    wait(&status);
    out_status(status);
    printf("-------------------------------------------------------------------------\n");

    if ((pid = fork()) < 0) {
        perror("fork error\n");
        exit(1);
    } else if (pid == 0) {
        printf("pid: %d, ppid: %d\n", getpid(), getppid());
        int i = 3, j = 0;
        int k = i / j; //除零运算,非正常终止
    }
    //父进程阻塞,等待子进程结束并回收
    wait(&status);
    out_status(status);
    printf("-------------------------------------------------------------------------\n");

    if ((pid = fork()) < 0) {
        perror("fork error\n");
        exit(1);
    } else if (pid == 0) {
        printf("pid: %d, ppid: %d\n", getpid(), getppid());
        pause(); //暂停终止
    }
    //此处为父进程执行,pid为子进程ID号
    do {
        pid = waitpid(pid, &status, WNOHANG | WUNTRACED);
        if (pid == 0) //pid==0证明子进程尚未结束
            sleep(1);
    } while (pid == 0);

    out_status(status);
    printf("-------------------------------------------------------------------------\n");
    return 0;
}

在这里插入图片描述

3.2 exec函数

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

/****************************************************************
 * Description: exec函数例程
 * ***************************************************************/
int example9()
{

    //const char* const 定义常量字符串
    const char* const cmd1 = "cat"; //相对路径
    const char* const cmd2 = "/bin/cat"; //绝对路径
    const char* const argv1 = "/etc/passwd";
    const char* const argv2 = "/etc/group";
    pid_t pid;
    if ((pid = fork()) < 0) {
        perror("fork error\n");
        exit(1);
    } else if (pid == 0) {
        //子进程调用exec函数,执行新的程序
        if (execl(cmd2, cmd1, argv1, argv2, nullptr) < 0) {
            perror("execl error!\n");
            exit(1);
        }
        printf("after execl...\n"); //如果execl调用成功,此句不会执行
    }
    sleep(1);
    printf("----------------------------------------------------------------------\n");
    char* argv[4] = { (char*)cmd1, (char*)argv1, (char*)argv2, NULL };
    if ((pid = fork()) < 0) {
        perror("fork error\n");
        exit(1);
    } else if (pid == 0) {
        //子进程调用exec函数,执行新的程序
        if (execvp(cmd2, argv) < 0) {
            perror("execl error!\n");
            exit(1);
        }
        printf("after execl...\n"); //如果execl调用成功,此句不会执行
    }
    sleep(1);
    printf("----------------------------------------------------------------------\n");
    wait(nullptr);
    return 0;
}

3.3 system函数

在这里插入图片描述

  系统运维 最新文章
配置小型公司网络WLAN基本业务(AC通过三层
如何在交付运维过程中建立风险底线意识,提
快速传输大文件,怎么通过网络传大文件给对
从游戏服务端角度分析移动同步(状态同步)
MySQL使用MyCat实现分库分表
如何用DWDM射频光纤技术实现200公里外的站点
国内顺畅下载k8s.gcr.io的镜像
自动化测试appium
ctfshow ssrf
Linux操作系统学习之实用指令(Centos7/8均
上一篇文章      下一篇文章      查看所有文章
加:2022-05-08 08:30:52  更:2022-05-08 08:34:21 
 
开发: 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/15 18:06:42-

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