C语言笔记-18-Linux基础-进程
admin
2024-03-19 03:53:13

C语言笔记-18-Linux基础-进程


文章目录

  • C语言笔记-18-Linux基础-进程
  • 前言
  • 一、进程概括
  • 二、进程指令
  • 三、进程函数
    • fork 新建子进程
    • return/exit 关闭子进程
    • atexit 遗言函数
  • 四、wait 进程资源回收
    • wait
    • waitpid
  • 五、exec家族函数 进程映像的更新
  • 六、Linux ELF 可执行文件
    • readelf 查看elf信息指令
  • 总结


前言

自学笔记,没有历史知识铺垫(省略百度部分)C语言笔记-18-Linux基础-进程


一、进程概括

  1. 程序是计算机指令集合,静态数据
  2. 进程是程序在计算机运行的实例
  3. 程序在运行过程中,要使用计算机资源,就要对进程使用的资源进行记录,所有的记录约等于进程

注意:

  1. 进程有独立的4G地址空间(多个进程轮流布局同一份4G地址空间,同一时刻只有一个进程独立占用)
  2. 进程有自己的pid
  3. 进程有自己的PCB

二、进程指令

  1. ps 查看进程
  2. top 实时查看进程

PCB成员
fd 记录进程对文件资源的使用情况(文件描述符)
image 进程镜像(轮流布局独立4G地址空间映射)

三、进程函数

unistd.h

fork 新建子进程

pid_t fork(void); 新建子进程(新建失败时,不会创建子进程)
返回值: 成功时,在父进程和子进程中各返回一次(常说的一次调用,两次返回)

  1. 成功:pid 子进程的pid 返回到父进程中
  2. 成功:0 返回到子进程中
  3. 失败:-1 返回到父进程中,errno被设置

注意:

  1. 子进程的PCB和父进程的PCB读的部分一致,写的部分,子进程会复制父进程PCB该部分后单独存储(2.6版本后增加,写时拷贝技术),即image布局是一样,但是子进程与父进程各运行在独立的内存空间,二者操作本进程时,不会相互影响

可以通过fork返回标识,控制业务在父进程or子进程中处理(异步)

示例代码

#include 
#include 
#include int main(int argc, char *argv[])
{pid_t pid = fork();if (pid == -1){printf("父进程执行失败,该信息永远在父进程中输出\n");}if (pid == 0){printf("子进程pid才会返回0,该信息永远是在子进程中输出\n");}else{printf("父进程才会返回子进程的pid:%d,该信息永远在父进程中输出\n", pid);}printf("由于子进程复制父进程的PCB,该信息会在父进程和子进程都会输出\n");return 0;
}// 输出结果
父进程才会返回子进程的pid:7724,该信息永远在父进程中输出
由于子进程复制父进程的PCB,该信息会在父进程和子进程都会输出
子进程pid才会返回0,该信息永远是在子进程中输出
由于子进程复制父进程的PCB,该信息会在父进程和子进程都会输出

return/exit 关闭子进程

return 标识函数结束,在main函数中,终止进程
exit 终止进程 (stdio操作会被刷新关闭,创建的临时文件也会被移除) 并向父进程返回(status&0377)(0-255)

atexit 遗言函数

进程在正常终止时被调用的函数,如:main函数return后、进程exit后,才会调用的函数

注册遗言函数
atexit() 传入无行参的指针函数

  1. 执行顺序按照注册顺序倒叙执行
  2. 同一个函数可以被注册多次,执行多次
  3. 子进程继承父进程的遗言函数
  4. exec家族中的函数,只要有一个函数调用成功,其他遗言函数会被移除(不执行)
#include 
#include void endCall(){printf("遗言函数触发\n");
}int main(int argc, char *argv[])
{atexit(endCall);atexit(endCall);return 0;
}// 输出结果
遗言函数触发
遗言函数触发

注册遗言函数
on_exit() 传入有行参的指针函数,并传入返回值,功能与atexit()一样

四、wait 进程资源回收

进程终止的时候,需要将进程资源回收,终止的进程无法自己回收,回收操作由父进程进行。
回收函数wait家族函数回收

wait

阻塞并挂起当前进程,等待任意子进程终止。
wait(int *status)
入参:
*status 将子进程状态信息存储到该指针中
返回值:
成功: pid 返回终止了的子进程的pid
失败: -1 errno被设置

wait的宏

  1. WIFEXITED(status) 子进程正常终止,返回真
  2. WEXITSTATUS(status) 返回子进程的退出状态码(exit(status) status&0377)
    这个宏只能在WIFEXITED返回真时使用
  1. WIFSIGNALED(status) 如果子进程是被信号终止的,返回真
  2. WTERMSIG(status) 返回导致子进程终止的信号的编号
    这个宏只能在WIFSIGNALED返回真时使用

wait可以让异步执行的多进程,转变为同步执行
模拟子进程正常终止示例

#include 
#include 
#include 
#include int main(int argc, char *argv[])
{int subExitStatus;pid_t pid = fork();if (pid == -1){printf("父进程执行失败,该信息永远在父进程中输出\n");}if (pid == 0){printf("子进程pid才会返回0,该信息永远是在子进程中输出\n");return 99;}else{wait(&subExitStatus);if (WIFEXITED(subExitStatus)){printf("子进程正常终止状态码:%d\n", WEXITSTATUS(subExitStatus));}printf("父进程才会返回子进程的pid:%d,该信息永远在父进程中输出\n", pid);}printf("由于子进程被提前return,该信息永远在父进程中输出\n");return 0;
}// 执行结果
子进程pid才会返回0,该信息永远是在子进程中输出
子进程正常终止状态码:99
父进程才会返回子进程的pid:12483,该信息永远在父进程中输出
由于子进程被提前return,该信息永远在父进程中输出

模拟子进程信号打断示例

#include 
#include 
#include 
#include 
#include int main(int argc, char *argv[])
{int subExitStatus;pid_t pid = fork();if (pid == -1){printf("父进程执行失败,该信息永远在父进程中输出\n");}if (pid == 0){printf("子进程pid才会返回0,该信息永远是在子进程中输出,pid:%d\n", getpid());getchar();return 99;}else{wait(&subExitStatus);if (WIFEXITED(subExitStatus)){printf("子进程正常终止状态码:%d\n", WEXITSTATUS(subExitStatus));}if (WIFSIGNALED(subExitStatus)){printf("子进程信号打断状态码:%d\n", WTERMSIG(subExitStatus));}printf("父进程才会返回子进程的pid:%d,该信息永远在父进程中输出\n", pid);}printf("由于子进程提前被信号终打断,该信息永远在父进程中输出\n");return 0;
}// 执行结果
子进程pid才会返回0,该信息永远是在子进程中输出,pid:13385
子进程信号打断状态码:8
父进程才会返回子进程的pid:13385,该信息永远在父进程中输出
由于子进程提前被信号终打断,该信息永远在父进程中输出

打断信号

 dony15$ kill -8 13385

waitpid

阻塞并挂起当前进程,等待指定子进程终止。
pid_t waitpid(pid_t pid, int *stat_loc, int options);
pid 指定具体的子进程

  1. <-1 等待进程组的pid是|pid|的组中的子进程
  2. -1 等待任意子进程
  3. 0 等待和当前进程同一个进程组的子进程
  4. >0 指定了要等待的子进程的pid

stat_loc 同wait(int *status)的参数
options

  1. WNOHANG 如果没有子进程终止,立即返回
  2. 0 阻塞等待子进程的终止

返回值:
成功 返回状态改变的子进程的pid
如果WNOHANG被指定,没有子进程状态的改变 返回0
错误 -1 errno被设置

// 效果
wait(s)==waitpid(-1,s,0)

五、exec家族函数 进程映像的更新

子进程可以更新image,此时子进程的image与父进程的image将不同(更新子进程image不影响父进程)

execve exec核心函数,所有exec家族函数都是围绕这个函数封装
int execve(const char *path, char *const argv[], char *const envp[]);
功能:执行程序
参数:

  1. filename 要执行的文件名
    这个文件必须是可执行的二进制文件脚本文件
  2. argv 字符串数组 传递给main函数的参数 习惯上第一个是可执行文件的名字
  3. envp name=value 作为环境变量传递给新的可执行程序
    argv和envp以NULL作为结尾
    返回值:
    成功 不返回
    错误 -1 errno被设置

exec家族函数列表

#include 
//指向当前进程的环境变量列表
extern char **environ;  
/* (char  *) NULL */);
int execl(const char *path, const char *arg, ...int execlp(const char *file, const char *arg, .../* (char  *) NULL */);
int execle(const char *path, const char *arg, .../*, (char *) NULL, char * const envp[] */);
int execv(const char *path, char *const argv[]);
int execvp(const char *file, char *const argv[]);
int execvpe(const char *file, char *const argv[],char *const envp[]);

exec 公有的
l l代表list,需要将arg元素以可变参数都传入
v v代表vector,传数组即可
p PATH环境变量 需要知道要加载的可执行程序所在的路径.

  1. 有p字母,会到环境变量PATH指定的路径中找可执行程序
  2. 没有p字母,必须指定可执行文件的所在路径(相对 绝对)

e

  1. 如果有字母e,用户指定环境变量传递给新的可执行程序.
  2. 没有字母e.代表从父进程继承环境变量

execvp示例

#include 
#include 
#include int main(int argc, char *argv[])
{pid_t pid = fork();if (pid == -1){printf("父进程执行失败,该信息永远在父进程中输出\n");}if (pid == 0){char *ps_argv[] = {"ps", "-o", "pid,ppid,comm", NULL};execvp("ps", ps_argv);printf("execvp执行失败才会执行\n");}else{printf("父进程才会返回子进程的pid:%d,该信息永远在父进程中输出\n", pid);}printf("由于子进程镜像被更新(execvp执行失败,子程序才会执行),与父进程的PCB不同,该信息会在父进程输出\n");return 0;
}// 执行结果
父进程才会返回子进程的pid:14966,该信息永远在父进程中输出
由于子进程镜像被更新(execvp执行失败,子程序才会执行),与父进程的PCB不同,该信息会在父进程输出PID  PPID COMM999   998 -bash5836  5805 /bin/bash
10801 10800 -bash
10839 10801 vim
13388  5805 /bin/bash

execlp示例

...
execlp("ps", "ps", "-o", "pid,ppid,comm", NULL);
...
// 执行结果
父进程才会返回子进程的pid:14966,该信息永远在父进程中输出
由于子进程镜像被更新(execvp执行失败,子程序才会执行),与父进程的PCB不同,该信息会在父进程输出PID  PPID COMM999   998 -bash5836  5805 /bin/bash
10801 10800 -bash
10839 10801 vim
13388  5805 /bin/bash

查看当前进程环境变量示例

#include 
#include 
#include // 当前进程的环境变量列表
extern char **environ;int main(int argc, char *argv[])
{for (size_t i = 0; environ[i] != NULL; i++){printf("环境变量:%s\n", environ[i]);}return 0;
}// 执行结果
环境变量:SHELL=/bin/bash
环境变量:TMPDIR=/var/folders/3j/nltc60p50b9dl803pp8qkbqc0000gn/T/
环境变量:OLDPWD=/Users/dony15/Desktop/c_code/c_learn_3
环境变量:ORIGINAL_XDG_CURRENT_DESKTOP=undefined
环境变量:MallocNanoZone=0
环境变量:JAVA_11_HOME=/Library/Java/JavaVirtualMachines/jdk-11.0.4.jdk/Contents/Home
环境变量:LC_ALL=en_US.UTF-8
环境变量:NO_PROXY=127.0.0.1
环境变量:COCOS_CONSOLE_ROOT=/Users/dony15/Desktop/cocos2d-x-4.0/tools/cocos2d-console/bin
环境变量:USER=dony15
环境变量:COCOS_TEMPLATES_ROOT=/Users/dony15/Desktop/cocos2d-x-4.0/templates
环境变量:COMMAND_MODE=unix2003
环境变量:JAVA_12_HOME=/Library/Java/JavaVirtualMachines/jdk-12.0.2.jdk/Contents/Home
环境变量:SSH_AUTH_SOCK=/private/tmp/com.apple.launchd.cT3LzZpnc1/Listeners
环境变量:__CF_USER_TEXT_ENCODING=0x1F5:0x19:0x34
...

execle示例
main.c

#include 
#include int main(int argc, char *argv[])
{char *m_env[] = {"name=abc", "age=15",NULL};int a = execle("./t_child.out", "abc", NULL, m_env);perror("err:");return 0;
}

t_child.c

#include 
#include 
#include // 当前进程的环境变量列表
extern char **environ;
int main(int argc, char *argv[])
{printf("start t_child process pid:%d\n", getpid());for (size_t i = 0; argv[i] != NULL; i++){printf("传入变量:%s\n", argv[i]);}for (size_t i = 0; environ[i] != NULL; i++){printf("环境变量:%s\n", environ[i]);}printf("t_child结束\n");return 0;
}
dony15$ gcc -o t_child.out t_child.c 
dony15$ gcc -o main main.c
dony15$ ./main 
start t_child process pid:9380
传入变量:abc
环境变量:name=abc
环境变量:age=15
t_child结束

六、Linux ELF 可执行文件

elf格式为可执行文件,python、bash、自定义写的c.out等都是

readelf 查看elf信息指令

readelf -a main.out

总结

注意:
char *argv[] 中argv是常量,不可以argv++
char **environ; 中environ是变量,可以environ++

向量表:地址空间连续的数组
列表:地址空间可能不连续的数组

  1. 僵尸进程:进程已经终止,进程资源没有回收
  2. 孤儿进程:父进程已终止,子进程没有回收,这种子进程需要过继给另一个进程。
    1.ubuntu 15.04版本之前,过继给init进程(孤儿院)
    2.ubuntu 15.04版本之后,过继给upstart进程(孤儿院)

exec家族函数,无论是环境变量列表、数组,还是入参变量列表、数组,结尾必须要加NULL

本章主要为C语言笔记-18-Linux基础-进程

相关内容

热门资讯

安全承诺书范文 安全承诺书范文...   安全生产承诺书   为了个人的安全健康,为了家庭的幸福美满,为了企业的稳定发展,本人在工作中,自...
个人承诺书 个人承诺书 个人承...   廉洁自律个人承诺书   xxx领导:   为了进一步强化廉政建设,提高廉洁自律意识,从思想源头上...
建筑企业诚信承诺书 建筑企业诚...   建筑企业诚信承诺书 XXXXXXXX公司郑重承诺:   本企业近2年未因发生以下12种行为而受到...
诚信考试承诺书范文大全 诚信考...   公务员诚信考试承诺书   我已仔细阅读xx20xx年考试录用公务员公告、相关政策和违纪违规处理规...
服务承诺书范文 服务承诺书范文...   篇一:工程服务承诺书   尊敬的******:   如果我单位能在本次竞争性谈判中中标,我单位做...