Linux系统编程——进程

文章目录

  • 进程概念
    • 什么是程序,什么是进程,有什么区别?
    • 如何查看系统中有哪些进程?
    • 什么是进程标识符?
  • 进程的创建
    • fork函数
      • 关于内存空间
      • fork创建一个子进程的一般目的
    • vfork函数
  • 进程退出
    • 正常退出
    • 异常退出
  • 进程回收
    • wait
    • waitpid
    • 进程退出与等待子进程
  • 僵尸进程
  • 孤儿进程
  • exec族函数
  • system函数
  • popen函数

进程概念

什么是程序,什么是进程,有什么区别?

  • 程序是静态的概念,gcc xxx.c –o pro,磁盘中生成pro文件,叫做程序

  • 进程是程序的一次运行活动,通俗点意思是程序跑起来了,系统中就多了一个进程

如何查看系统中有哪些进程?

  • 使用ps-aux指令查看,实际工作中,配合grep来查找程序中是否存在某一个进程ps-aux | grep init
  • 使用top指令查看,类似windows任务管理器

什么是进程标识符?

  • 每个进程都有一个非负整数表示的唯一ID,叫做pid
  • Pid=0: 称为交换进程(swapper)。作用—进程调度
  • Pid=1:init进程。作用—系统初始化
  • 获取进程标识符的函数:

获取自身的进程标识符,返回数据类型pid_t

getpid()

获取父进程的进程标识符,返回数据类型pid_t

getppid()

进程的创建

fork函数

  • fork()函数通过系统调用创建一个与原来进程几乎完全相同的进程,也就是两个进程可以做完全相同的事,但如果初始参数或者传入的变量不同,两个进程也可以做不同的事。
#include
pid_t fork(void);
  • fork调用后成功,后面的代码会执行两次,因为有两个进程,由进程调度负责父进程先执行还是子进程先执行
  • 返回值
    • 返回值为0,代表当前进程是子进程
    • 返回值非负数(子进程的pid号),代表当前进程为父进程
    • 返回-1,调用失败

关于内存空间

fork创建一个子进程的一般目的

  1. 一个父进程希望复制自己,使得父、子进程同时执行不同的代码段,这在网络服务进程中是常见的——父进程等待客户端的服务请求,当这种请求到达时,父进程调用fork,使得子进程处理此请求。父进程则继续等待下一个服务请求到达
  2. 一个进程要执行一个不同的程序,这对shell是常见的情况,在这种情况下,子进程从fork返回后立即调用exec

vfork函数

关键区别一:

  • vfork 直接使用父进程存储空间,不拷贝,也就是共享变量。

关键区别二:

  • vfork保证子进程先运行,当子进程调用exit退出后,父进程才执行。

进程退出

正常退出

  • Main函数调用return
  • 进程调用exit(),标准c库
  • 进程调用_exit()或者_Exit(),属于系统调用
  • 补充:
    • 进程最后一个线程返回
    • 最后一个线程调用pthread_exit

异常退出

  • 调用abort
  • 当进程收到某些信号时,如ctrl+C
  • 最后一个线程对取消(cancellation)请求做出响应

注意:

  • 不管进程如何终止,最后都会执行内核中的同一段代码,这段代码为相应的进程关闭所有打开描述符,释放他所使用的存储期等
  • 对上述任一一种终止情形,我们都希望终止进程能够通知其父进程它是如何终止的。对于三个终止函数(exit, _exit和 _ Exit),实现这一点的方法是,将其退出状态(exit status)作为参数传送给函数,在异常终止情况下,内核(不是进程本身)产生一个指示其异常终止原因的终止状态(termination status)。在任意一种情况下,该终止进程的父进程都能用 wait 或 waitpid 函数取得终止状态。

进程回收

  • 父进程等待子进程退出,并收集子进程的退出状态

wait

pid_t wait(int *status);

参数说明:

  • status
    • 非空:子进程退出状态放在它所指向的地址中。
    • 空:不关心退出状态

返回值:

  • ‐1 : 回收失败,已经没有子进程了
  • 大于0 : 回收子进程对应的 pid

注意:
- 如果其所有的子进程都在运行则阻塞
- 如果一个子进程已经终止,正等待父进程获取其终止状态,则取得该子进程的终止状态立即返回
- 如果它没有任何子进程,则立即出错返回
- 调用一次只能回收一个子进程

关于宏:

  • 在父进程中使用宏可以收集终止状态

Linux系统编程——进程_第1张图片

  1. WIFEXITED(status):为非0 ,进程正常结束
  2. WIFSIGNALED(status):为非0,进程异常退出

waitpid

pid_t waitpid(pid_t pid,int *status,int options);

参数说明:

  • status参数:
    • 非空:子进程退出状态放在它所指向的地址中。
    • 空:不关心退出状态
  • pid参数:(指定回收某个子进程)

Linux系统编程——进程_第2张图片

  • options参数:
    Linux系统编程——进程_第3张图片

区别:

  • wait使调用者阻塞,waitpid有一个选项,可以使调用者不阻塞(WNOHANG)

进程退出与等待子进程

使用wait函数获取退出的状态,打印退出状态的宏

僵尸进程

  • 一个比较特殊的状态,当子进程退出时,父进程(使用wait()系统调用)没有读取到子进程退出的返回代码时就会产生僵尸进程。僵尸进程会在以终止状态保持在进程表中,并且会一直等待父进程读取退出状态代码。

孤儿进程

  • 父进程如果不等待子进程退出,在子进程之前就结束了自己的“生命”,此时子进程叫做孤儿进程。Linux避免系统存在过多孤儿进程,init进程(pid=1)收留孤儿进程,变成孤儿进程的父进程。

exec族函数

linux进程—exec族函数(execl, execlp, execle, execv, execvp, execvpe)

exec 族函数的作用

  • 我们用 fork 函数创建新进程后,经常会在新进程中调用 exec 函数去执行另外一个程序。当进程调用 exec 函数时,该进程被完全替换为新程序。因为调用 exec 函数并不创建新进程,所以前后进程的ID并没有改变。子进程fork返回后立即调用exec

函数族原型:

Linux系统编程——进程_第4张图片

  • 带 p 的一类 exec 函数,包括 execlp、execvp、execvpe,如果参数 file 中包含 /,则就将其视为路径名,否则就按 PATH 环境变量,在它所指定的各目录中搜寻可执行文件。
  • 带 l 的一类 exec 函数(l表示list,包括 execl、execlp、execle,要求将新程序的每个命令行参数都说明为一个单独的参数。这种参数表以空指针结尾。
  • 带v的一类exec函数,应该先构造一个指向各个参数的指针数组,然后将该数组的地址作为这些函数的参数
  • 带e,多了envp[]数组,使用新的环境变量代替调用进程的环境变量

返回值:

  • exec 函数族的函数执行成功后不会返回,调用成功后后面的程序不会执行。调用失败时,会设置 errno 并返回-1,然后从原程序的调用点接着往下执行

参数说明:

  • path:可执行文件的路径名字
  • arg: 可执行程序所带的参数,第一个参数为可执行文件名字,没有带路径且 arg 必须以 NULL 结束
  • file: 如果参数 file 中包含 /,则就将其视为路径名,否则就按 PATH 环境变量,在它所指定的各目录中搜寻可执行文件。

注意:

  • 环境变量:环境变量里面都是路径,通过路径可以找到可执行程序,
  • 获取环境变量:echo $PATH
  • 手动添加环境变量:export PATH=$PATH:xxx可执行文件的路径

实例:

  1. 调用./bin/echoarg路径下的echoarg执行文件
    Linux系统编程——进程_第5张图片

  2. 调用系统可执行文件ls,通过whereis查看系统可执行程序的地址。若要调用ls-l,则第三个参数为”-l”
    Linux系统编程——进程_第6张图片

  3. 调用系统时间:调用date,获取date的地址,参数为null
    Linux系统编程——进程_第7张图片

  4. execl配合fork使用,实现功能,当父进程检测到输入为1的时候,创建子进程把配置文件的字段值修改掉。
    Linux系统编程——进程_第8张图片

system函数

linux system函数详解

函数原型:

#include
int system(const char *command)

返回值:

  • 成功,则返回进程的状态值;
  • 如果system()在调用/bin/sh时失败则返回127
  • 其他失败原因返回-1

关于system()函数源码:

  • system是封装exce函数的,需要传递一个字符串,该字符串是cmdstring,该字符串包含了执行文件和参数。
  • 实际上是执行文件用sh -c

在这里插入图片描述

替代excel函数

  • 直接写调用函数与参数,不用写可执行文件的路径:

在这里插入图片描述

popen函数

linux下popen的使用心得

函数原型

#include “stdio.h”
FILE *popen(const char *command,const char *mode)

参数说明:

  • command: 是一个指向以 NULL 结束的 shell 命令字符串的指针。这行命令将被传到 bin/sh 并使用 -c 标志,shell 将执行这个命令。

  • mode: 只能是读或者写中的一种,得到的返回值(标准 I/O 流)也具有和 type 相应的只读或只写类型。如果 type 是 “r” 则文件指针连接到 command 的标准输出;如果 type 是 “w” 则文件指针连接到 command 的标准输入。

返回值:

  • 如果调用成功,则返回一个读或者打开文件的指针,如果失败,返回NULL,具体错误要根据errno判断

函数原型:

int pclose (FILE* stream)

参数说明:

  • stream:popen返回的文件指针

返回值:

  • 如果调用失败,返回 -1

比system在应用中的好处:

  • 可以获取运行的输出结果,存放在字符串或文件当中,因为excel成功不会返回结果
  • 返回的是文件流,而通过fread读取文件流可以写入到指定缓冲区
  • 第一个参数是指令,第二个参数一般选字符r,输出
  • 需要使用pclose关闭文件

例子:
Linux系统编程——进程_第9张图片

你可能感兴趣的:(Linux,linux,服务器,windows)