Linux编程(三):进程学习笔记

本文围绕Linux进程展开,介绍了进程概念、类型、状态等基础知识,阐述了进程相关命令,如查看信息、设置优先级等。还讲解了进程相关函数,包括创建、结束、执行和回收等操作。最后介绍了守护进程的特点、会话与控制终端概念及创建方法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

进程

进程概念

  • 程序
    • 存放在磁盘上的指令和数据的有序集合(文件)
    • 静态的
  • 进程
    • 执行一个程序所分配的资源的总称
    • 进程是程序的一次执行过程
    • 动态的,包括创建、调度、执行和消亡

进程内容

进程包含正文段、用户数据段、系统数据段。程序包括正文段和用户数据段。

系统数据段主要包括进程控制块、CPU寄存器值、堆栈

  • 进程控制块(pcb)
    • 进程标识PID
    • 进程用户
    • 进程状态、优先级
    • 文件描述符表
  • CPU寄存器
    • 保存程序计数器(PC)的值,值为进程中下一指令的地址

进程类型

  • 交互进程:在shell下启动。以在前台运行,也可以在后台运行。后台运行则加个&字符。前台可以从终端输入输出,后台不能终端输入但可以输出。
  • 批处理进程:和在终端无关,被提交到一个作业队列中以便顺序执行
  • 守护进程:和终端无关,一直在后台运行。

进程状态

  • 运行态:进程正在运行,或者准备运行
  • 等待态:进程在等待一个事件的发生或某种系统资源。事件发生或有资源系统去唤醒
    • 可中断,等待的过程会不会被信号打断
    • 不可中断
  • 停止态:进程被中止,收到信号后可继续运行,例如gdb调式
  • 死亡态:已终止的进程,但pcb没有被释放

在这里插入图片描述

进程相关命令

进程信息的查看
  • ps -ef ps aux 查看系统进程信息 ,加|grep 可以通过关键字查找进程名
  • top 查看进程的动态信息
  • /proc到该目录下去根据进程号查找目录进入查看进程相关 文件的详细信息,例如cat /proc/6/fd/stat可以查看进程的状态信息、进程号等
#include <iostream>
using namespace std;
int main()
{
    while(1);
    return 0;
}
//g++ -o test test.cpp
//./test

ps -ef|more查看系统进程信息
在这里插入图片描述
ps aux可以查看到系统进程的当前状态
在这里插入图片描述

top查看当前系统的动态信息,查看哪些进程最占资源,方便系统优化查看
在这里插入图片描述

进程的优先级
  • 普通用户优先级只能大于或等于0,只能降低已有进程的优先级 ,即优先级只能是一个正数
  • 管理员用户没有限制,既可以升高优先级也可以降低优先级
  • 优先级(-20,19) ,-20优先级最高,默认优先级为0
  • nice -n 进程等级 ./文件名 以指定的优先级运行进程
  • renice -n 进程等级 进程ID` 改变正在执行进程的优先级
进程的前后台执行
  • 1、后台执行 ./文件名 & 执行完毕后显示 后台进程作业号 进程ID
  • 2、 使用jobs查看后台所有运行的进程
  • 3、后台进程转换前台运行 fg 作业号
  • 4、Ctrl+z 前台进程变成后台进程挂起,进程状态为变成停止态
  • 5、使用bg 作业号停止态变成运行态 即将挂起的进程放在后台运行

进程相关函数

进程创建fork
#include <unistd.h>
pid_t fork(void);
  • 创建新的进程,失败时返回-1
  • 成功时父进程返回子进程的进程号,是一个大于0的值,子进程返回0
  • 通过fork的返回值区分父进程和子进程

for example

#include <unistd.h>
#include <stdio.h>
int main()
{
    pid_t pid;
    if((pid=fork())<0)
    {
        perror("fork");
        return -1;
    }
    else if(pid==0)
    {
        printf("child process:my pid is %d\n",getpid());
    }
    else
    {
        printf("parent process:my pid is %d\n",getpid());
    }
}
父子进程
  • 子进程继承了父进程的几乎所有的内容,两个不同的进程,pid和ppid不一样
  • 父子进程有独立的地址空间,互不影响 ,独立的地址空间、代码和数据,各自的代码和变量
  • 若父进程先结束
    • 子进程成为孤儿进程,被init进程收养
    • 子进程变成后台进程
  • 若子进程先结束
    • 父进程如果没有及时回收子进程的返回值和退出状态,子进程的pcb没有释放,子进程变成僵尸进程
  • 子进程从何处开始执行?
    • 父进程从main函数开始,子进程从fork的下一个语句开始执行
      
    • 子进程继承了父进程的程序计数器,程序计数器存放下一条指令的地址
      
  • 父子进程谁先执行?
    • 指的是fork后谁先执行,CPU调度决定
  • 父进程能否多次调用fork?子进程呢?
    • 父进程可以多次创建进程,子进程需要回收,子进程也可以调用fork创建孙进程
进程结束exit/_exit
#include <stdlib.h>
#include <unistd.h>
void exit(int status);
void _exit(int status);
  • 结束当前的进程并将status低八位返回 父进程只接受低八位
  • exit结束进程时会刷新流缓冲区,_exit不会刷新流缓冲区,注意数据丢失的可能

for example1

#include <stdio.h>
#include <stdlib.h>
int main()
{
    printf("this process will exit");//输入到缓冲区了
    exit(0);//刷新了缓冲区会输出到终端了
    printf("never be displayed");
}

for example2

#include <stdio.h>
#include <stdlib.h>
int main()
{
    printf("using exit...\n");//输出到终端上显示
    printf("This is the end");//输入到缓冲区里
    exit(0);//刷新了缓冲区会输出到终端了
    //_exit(0);//缓冲区未刷新不会显示到终端上
}
exec函数族
  • 进程调用exec函数执行某个程序
  • 进程当前内容被指定的程序替换
  • 实现让父子进程执行不同的程序
    • 父进程创建子进程
    • 子进程调用exec函数族
    • 父进程不受影响
execl/execlp
#include <unistd.h>
int execl(const char *path,const char *arg,...);
int execlp(const char *file,const char *arg,...);
  • 成功时执行指定的程序;失败时返回EOF
  • path 执行的程序名称,包含路径
  • arg…传递给执行的程序的参数列表,最后一定要NULL结尾
  • file 执行的程序的名称,在PATH中查找

for example

执行ls命令,显示/etc目录下所有文件的详细信息

#include <unistd.h>
#include <stdio.h>
int main()
{
    pid_t pid;
    if((pid=fork())<0)
    {
        perror("fork");
        return -1;
    }
    else if(pid==0)
    {
        printf("child process:my pid is %d\n",getpid());
        if(execl("/bin/ls","ls -a","-l","/etc",NULL)<0)
        {
            perror("excel");
        }
//        if(execlp("ls","ls","-a","-l","/etc",NULL)<0)
//        {
//            perror("excelp");
//        }
    }
    else
    {
        printf("parent process:my pid is %d\n",getpid());
    }
    return 0;
}
execv/execvp
#include <unistd.h>
int execl(const char *path,const char *const argv[]);
int execlp(const char *file,const char *const argv[]);
  • 成功时执行指定的程序;失败时返回EOF
  • arg…封装成指针数组的形式

for example

执行ls命令,显示/etc目录下所有文件的详细信息

#include <unistd.h>
#include <stdio.h>
int main()
{
    pid_t pid;

    if((pid=fork())<0)
    {
        perror("fork");
        return -1;
    }
    else if(pid==0)
    {
        printf("child process:my pid is %d\n",getpid());
        char *arg[]={"ls","-a","-l","/etc",NULL};
        if(execvp("ls",arg)<0)
        {
            perror("execv");
        }
    }
    else
    {
        printf("parent process:my pid is %d\n",getpid());
    }
    return 0;
}
system
#include <stdlib.h>
int system("const char *command");
  • 成功时返回命令command的返回值;失败时返回EOF
  • 在当前进程中自动创建一个子进程,子进程去执行command所包含的程序,父进程等待command命令执行结束后才继续执行
进程回收
  • 子进程结束时由父进程回收
  • 孤儿进程由init进程回收
  • 若没有及时回收会出现僵尸进程,父进程没有回收子进程的PCB,当父进程结束时,子进程成为孤儿进程由init进程回收
wait
#include <unistd.h>
pid_t wait(int *status);
  • 成功时返回回收的子进程的进程号;失败时返回EOF
  • 若子进程没有结束,父进程一直阻塞
  • 若有多个子进程,哪个先结束就先回收
  • status指定保存子进程返回值和结束方式的地址
  • status为NULL表示直接释放子进程PCB,不接收返回值

for example

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

int main()
{
    pid_t pid;
    int status;
    if((pid=fork())<0)
    {
        perror("fork");
        exit(-1);
    }
    else if(pid==0)
    {
        sleep(1);
        exit(2);
    }
    else
    {
        wait(&status);
        printf("%x\n",status);
    }
    return 0;
}
进程返回值和结束方式
  • 子进程通过exit/_exit/return 返回某个值(0-255)
  • 父进程调用wait(&status)回收
    • WIFEXITED(status) 判断子进程是否正常结束
    • WEXISTSTATUS(status) 获取子进程返回值
    • WIFSIGNALED(status) 判断子进程是否被信号结束
    • WTERMSIG(status) 获取结束子进程的信号类型
waitpid
#include <unistd.h>
pid_t waitpid(pid_t pid,int *status,int option);
  • 成功时返回回收的子进程的pid或0;失败时返回EOF

  • pid可用于指定回收哪个子进程或任意子进程

  • status指定用于保存子进程返回值和结束方式的地址

  • option指定回收方式,0表示阻塞,WNOHANG表示非阻塞,返回值为0,表示子进程未结束

  • waitpid(pid,&status,0);阻塞回收进程号为pid的子进程

  • waitpid(pid,&status,WNOHANG);非阻塞回收进程号pid的子进程,返回值为0,表示子进程还未结束

  • waitpid(-1,&status,0);阻塞回收任意子进程,等价于wait

  • waitpid(-1,&status,WNOHANG);非阻塞回收任意子进程

守护进程

  • 守护进程(Daemon)是Linux三种进程类型之一
  • 通常在系统启动时运行,系统关闭时结束
  • Linux系统中大量使用,很多服务程序以守护进程形式运行
守护进程特点
  • 始终在后台运行
  • 独立于任何终端
  • 周期性的执行某种任务或等待处理特定事件
  • 使用ps -ef时,进程名称最后一个字母是d,表示是个守护进程
会话、控制终端
  • Linux以会话(session)、进程组的方式管理进程

  • 每个进程属于一个进程组,运行程序时会创建一个进程去执行这个程序,进程创建时同时也创建了一个进程组,程序如果创建了子进程,子进程和父进程也属于这个进程组

  • 会话是一个或多个进程组的集合。通常用户打开一个终端时,系统会创建一个会话,所有通过该终端运行的进程都属于这个会话,一个会话最多打开一个控制终端

  • 控制终端关闭时,所有相关进程会被结束

守护进程创建
  • 创建子进程,父进程退出

    • if(fork()>0)
      {
          exit(0);
      }
      
    • 子进程变成孤儿进程,被init进程收养

    • 子进程在后台运行

  • 子进程创建新会话

    • if(setsid()<0)
      {
          exit(-1);
      }
      
    • 子进程成为新的会话组长,不属于原先的终端会话了

    • 子进程脱离原先的终端

  • 更改进程当前工作目录

    • chdir("/tmp")权限0777可读可写可执行
    • 守护进程一直在后台运行,其工作目录不能被卸载,守护进程创建时它的当前工作目录有可能指向任意一个目录,需要把守护进程的当前工作目录指向一个不能卸载的地方
    • 重新设定当前工作目录cwd
  • 重设文件权限掩码

    • if(umask(0)<0)
      {
          exit(-1);
      }
      
    • 文件权限掩码设置为0

    • 只影响当前进程

  • 关闭打开的文件描述符,子进程继承了父进程打开的文件描述符

    • int i;
      for(i=0;i<getdtablesize();i++)
      {
          close(i);
      }
      
    • 关闭所有从父进程继承的打开文件

    • 已脱离终端,stdin/stdout/stderr无法再使用

for example

创建守护进程,每隔1秒钟将系统时间写入文件time.log

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

int main()
{
    pid_t pid;
    FILE *fp;
    time_t t;
    int i;
    if((pid=fork())<0)
    {
        perror("fork");
        exit(-1);
    }
    else if(pid>0)
    {
        exit(0);
    }
    else
    {
        setsid();
        umask(0);
        chdir("/tmp");
        for(i=0;i<getdtablesize();i++)
        {
            close(i);
        }
        if((fp=fopen("time.log","a"))==NULL)
        {
            perror("fopen");
            exit(-1);
        }
        while(1)
        {
            time(&t);
            fprintf(fp,"%s",ctime(&t));
            fflush(fp);
            sleep(1);
        }
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

H_Mike

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值