首页 > 系统相关 >Linux进程函数

Linux进程函数

时间:2023-10-28 20:15:10浏览次数:36  
标签:status 函数 int pid 回收 Linux 进程 include

1.进程相关知识

PCB进程控制块包含的信息

  • 进程id。系统中每个进程有唯一的id,在C语言中用pid_t类型表示,其实就是一个非负整数。
  • 进程的状态,有就绪、运行、挂起、停止等状态。
  • 进程切换时需要保存和恢复的一些CPU寄存器。
  • 描述虚拟地址空间的信息。
  • 描述控制终端的信息。
  • 当前工作目录(Current Working Directory)。
  • umask掩码。
  • 文件描述符表,包含很多指向file结构体的指针。
  • 和信号相关的信息(未决信号集、信号屏蔽字)。
  • 用户id和组id。
  • 会话(Session)和进程组。
  • 进程可以使用的资源上限(Resource Limit)

具体更多操作系统相关的知识可以看这里的随笔  <操作系统 - 随笔分类 - imXuan - 博客园 (cnblogs.com)>

2.进程创建

2.1 fork

  功能:用于从一个已存在的进程中创建一个新进程,新进程称为子进程,原进程称为父进程。

  fork创建子进程,两个进程逻辑上虽然是完全用虚拟内存进行隔离的,但实际上linux引入了读时共享,写时复制的原则,共同读取的数据不需要复制,需要写入的时候再复制,节省空间,具体可以参考操作系统随笔中的内容

#include <sys/types.h>
#include <unistd.h>
​
pid_t fork(void);
/*
返回值:
    成功:子进程中返回 0,父进程中返回子进程 ID。pid_t,为整型。
    失败:返回-1。
        失败的两个主要原因是:
            1)当前的进程数已经达到了系统规定的上限,这时 errno 的值被设置为 EAGAIN。
            2)系统内存不足,这时 errno 的值被设置为 ENOMEM。
*/

2.2 getpid

  功能:获取本进程号(PID)

#include <sys/types.h>
#include <unistd.h>
​
pid_t getpid(void);
// 返回值:本进程号

2.3 getppid

  功能:获取调用此函数的进程的父进程号(PPID)

#include <sys/types.h>
#include <unistd.h>
​
pid_t getppid(void);
// 返回值:调用此函数的进程的父进程号(PPID)

2.4 getpgid

  功能:获取进程组号(PGID)

#include <sys/types.h>
#include <unistd.h>
​
pid_t getpgid(pid_t pid);
/*    
    参数:pid:进程号
    返回值:参数为 0 时返回当前进程组号,否则返回参数指定的进程的进程组号
*/

2.5 exec 函数族

  将当前进程的代码段、数据段等替换成所需要加载程序的代码段、数据段,从新的代码段的第一条指令开始执行,但进程ID不变

  exec函数族函数一旦调用成功,不会返回值,只有失败才返回 -1 或 errno

2.5.1 execlp

int execlp(const char* file, const char* arg, ... /* (char*) NULL */);
/*
参数
   file: 加载程序的名字,需要配合环境变量 PATH 使用
   arg0: 可执行文件名
   arg1: 参数
    ...
   argn: NULL (哨兵)  
*/

  示例

execlp("ls", "ls", "-l", "-F", "-a", NULL); 

  补充

int main( int argc, char* argv[])
{ 
     //函数体内使用了argc或argv
     ……
     return 0;
}
//    argv[0]指向程序运行的全路径名 
//    argv[1]指向在命令行中执行程序名后的第一个字符串 
//    argv[2]指向执行程序名后的第二个字符串 

2.5.2 execl

int execlp(const char* file, const char* arg, ... /* (char*) NULL */);
/*
参数
   file: 加载程序的绝对路径的程序名字
   arg0: 可执行文件名
   arg1: 参数
    ...
   argn: NULL (哨兵)  
*/

  示例

execl("./bin/ls", "ls", "-l", "-F", "-a", NULL); 

3.进程回收

  • 父进程有义务在子进程结束时,回收该子进程,隔备进程无回收关系
  • 进程终止:
    • 关闭所有文件描述符
    • 释放用户空间分配的内存
    • 进程的 pcb 残留在内核。保存进程结束的状态(正常:退出值。异常:终止其运行的信号编号)

3.1 孤儿进程

  父进程先于子进程终止,子进程沦为“孤儿进程”,会被 init 进程领养

  ps ajx 指令可以查看进程信息

3.2 僵尸进程(zombie)

  子进程终止,父进程未终止,但父进程尚未对子进程进行回收

  结束进程指令:kill -9 进程id。只能结束活跃进程,僵尸进程无效,僵尸进程已经结束,只是父进程没有把他干掉,PCB残留在内核中

3.3 wait 回收

  • 功能:等待任意一个子进程结束,如果任意一个子进程结束了,此函数会回收该子进程的资源。
  • 作用
    • <阻塞>等待子进程退出(终止)
    • 回收子进程残留在内核的pcb
    • 获取子进程的退出状态(正常、异常),传出参数:status
#include <sys/types.h>
#include <sys/wait.h>
​
pid_t wait(int *status);
/*
    参数:
        status : 进程退出时的状态信息。
    返回值:
        成功:已经结束子进程的进程号
        失败: -1
*/

  示例,通过宏可以获取退出码或者信号编号,也可以传入NULL,不需要保存任何信息,只是把子进程回收

int main(int argc, char *argv[])
{
    int status = 9;
    pid_t wpid = 0;
    pid_t pid = fork();
    if(pid == -1)
    {
        perror("fork err"); exit(1);
    }
    else if(pid == 0)
    {
        printf("I'm child pid = %d\n", getpid());
        sleep(3);
        exit(66);
    }
    else
    {
        wpid = wait(&status);    // 保存子进程退出的状态
        if(wpid == -1)
        {
            perror("wait err"); exit(1);
        } 
        if(WIFEXITED(status))    // 宏函数为真,说明子进程正常退出
        {    // 获取退出码
            printf("I'm parent, pid = %d child, exit code = %d\n", wpid, WEXITSTATUS(status));
        }
        else if(WIFSIGNALED(status))    // 宏函数为真, 说明子进程被信号终止
        {    // 获取信号编码
            printf("I'm parent, pid = %d child, killed by %d signal\n", wpid, WTERMSIG(status));
        }
    }
    return 0;
}

3.4 waitpid 回收

pid_t waitpid(pid_t pid, int* status, int options);
/*
     pid > 0  等待进程为 pid 的子进程。
     pid = 0  等待同一个进程组中的任何子进程,如果子进程已经加入了别的进程组,waitpid 不会等待它。
     pid = -1 等待任一子进程,此时 waitpid 和 wait 作用一样。
     pid < -1 等待指定进程组中的任何子进程,这个进程组的 ID 等于 pid 的绝对值。

     status: 传出回收子进程状态
     options: WNOHANG -- 指定回收方式为 “非阻塞”;  0 为阻塞方式
     
     成功返回回收进程的 pid , 失败返回 -1 , 子进程未结束则返回 0 (用非阻塞回收)
*/

  **注意:一次 wait 、 waitpid  调用只能回收一个子进程,想回收 N 个子进程需要将函数放于循环中  

4.进程间通信

  • 进程间通信的原理,多个进程虽然对应了多个虚拟内存映射,但是系统内核是相同的,可以通过内核传递数据
  • 进程间通信的方法
    • 1.管道(最简单)
    • 2.信号(开销小)
    • 3.mmap 映射(速度快,非血缘关系)
    • 4.socket 本地套接字(稳定性好)

4.1 pipe(匿名管道)

  • 实现原理:Linux 内核使用环形队列机制,借助缓冲器(4k)实现
  • 特质
    • 本质:伪文件(实际是内核缓冲区)
    • 用于进程间通信,由两个文件描述符引用,一个读端,一个写端
    • 规定数据从管道写端流入,从读端流出
  • 局限性
    • 只能自己写,不能自己读
    • 管道中的数据,读走就销毁,不能反复读取
    • 半双工通信,数据在同一时刻只能在一个方向上流动
    • 应用于血缘关系进程间
#include <unistd.h>
int pipe(int pipefd[2]);
// pipefd : 传入传出参数,其存放了管道的文件描述符 
// 管道读端: pipefd[0]
// 管道写端: pipefd[1]
// 返回值: 成功0; 失败-1, errno
  • 示例
int main()
{
    int fd_pipe[2] = { 0 };
    pid_t pid;
    if (pipe(fd_pipe) < 0)  // 创建管道
        perror("pipe");
    pid = fork(); // 创建进程
    if (pid == 0)
    { // 子进程
        char buf[] = "I am mike";
        write(fd_pipe[1], buf, strlen(buf));  // 往管道写端写数据
        _exit(0);
    }
    else if (pid > 0)
    {// 父进程
        wait(NULL); // 等待子进程结束,回收其资源
        char str[50] = { 0 };
        read(fd_pipe[0], str, sizeof(str));  // 从管道里读数据
        printf("str=[%s]\n", str); // 打印数据
    }
    return 0;
}

 管道读写行为 

  • 读管道
    • 有数据:read返回实际读到的字节数
    • 无数据:有写端阻塞;无写端返回0(没有相应的read函数)
  • 写管道
    • 无读端:异常终止(没有相应的write函数)(SIGPIPE信号)
    • 有读端:管道满阻塞,管道未满返回实际写入字节数

标签:status,函数,int,pid,回收,Linux,进程,include
From: https://www.cnblogs.com/stux/p/17794117.html

相关文章

  • 第八周Linux教材第四章学习笔记——并发编程
     第四章 并发编程4.1并行计算导论在早期,大多数计算机只有一个处理组件,称为处理器或中央处理器(CPU)。受这种硬件条件的限制,计算机程序通常是为串行计算编写的。要求解某个问题,先要设计一种算法,描述如何一步步地解决问题,然后用计算机程序以串行指令流的形式实现该算法。在只有......
  • 无涯教程-Clojure - struct-map函数
    通过显式定义将哪些值分配给结构中的哪些键,此函数用于将值专门分配给键值。struct-map-语法(struct-mapstructnamekeynvaluen….)参数   - "structname"是要赋予结构的名称,"keyn和valuen"是需要分配给该结构的键值。返回值 - 返回一个结构对象,其值映射......
  • 无涯教程-Clojure - defstruct函数
    该函数用于定义所需的结构。defstruct-语法(defstructstructnamekeys)参数   - "structname"是要赋予结构的名称,"keys"是需要作为结构一部分的键。返回值 - 返回结构对象。defstruct-示例以下程序显示了有关如何使用它的示例。(nsclojure.examples.......
  • 无涯教程-Clojure - vary-meta函数
    返回与原始对象具有相同类型和值的对象,但具有组合的元数据。vary-meta-语法(vary-metaobjnew-meta)参数   - 'obj'是要检查是否有任何元数据与之关联的对象,"new-meta"是需要与对象关联的元数据值。返回值 -返回与原始对象具有相同类型和值的对象,但具有组合......
  • 无涯教程-Clojure - meta-with函数
    此函数用于定义任何对象的元数据映射。meta-with-语法(with-metaobjmapentry)参数   - 'obj'是需要与元数据关联的对象,"mapentry"是需要与对象相关联的元数据。返回值 - 返回与obj具有相同类型和值的对象,并以mapentry作为其元数据。meta-with-示例(nscl......
  • 无涯教程-Clojure - meta函数
    此函数用于查看是否有任何元数据与对象相关联。meta-语法(metaobj)参数   - 'obj'是要检查是否有任何元数据与之关联的对象。返回值 - 返回obj的元数据,如果没有元数据,则返回nil。meta-示例(nsclojure.examples.example(:gen-class))(defnExample[......
  • Python reversed函数及用法
    reserved()是Pyton内置函数之一,其功能是对于给定的序列(包括列表、元组、字符串以及range(n)区间),该函数可以返回一个逆序序列的迭代器(用于遍历该逆序序列)。reserved()函数的语法格式如下:reversed(seq)其中,seq可以是列表,元素,字符串以及range()生成的区间列表。下面程......
  • Linux 下使用 Docker 安装 Redis
    1、下载redisdockerpullredis:6.2.62、提前创建挂载目录mkdir-p/mydata/redis/confmkdir-p/mydata/redis/datamkdir-p/mydata/redis/logtouch/mydata/redis/conf/redis.conftouch/mydata/redis/log/redis.logchmod777/mydata/redis/log/redis.log3、启......
  • 二次函数在区间上的最大(小)值问题
    前言本篇博文适合高一学生和高三一轮学习使用。对于高一学生而言,对初中学习的二次函数\(f(x)\)\(=\)\(ax^2\)\(+\)\(bx\)\(+\)\(c\)\(\quad\)\((a\neq0)\)已经形成了思维定势,总认为其最大值或者最小值是\(f(x)\)\(=\)\(f(-\cfrac{b}{2a})\)\(=\)\(\cfrac{4ac-b^2}{4a}\),很少......
  • Linux中设置NTP时间同步服务器的方法
    概括:在Linux中设置NTP时间同步服务器是确保多台主机之间时间同步的重要步骤。本文将从四个方面详细阐述Linux中设置NTP时间同步服务器的方法,包括安装NTP、配置NTP客户端、配置NTP服务器以及常见问题及其解决方法。1、安装NTP安装NTP是为了确保Linux主机能够正常运行时间同......