Linux 学习笔记(十三)—— 进程控制

一、进程创建

在linux中“fork()”函数非常重要,它的作用是从已存在的进程中创建一个新的进程,新进程为子进程,而原进程为父进程;

#include <unistd.h>

pid_t fork(void);

子进程返回0,父进程返回子进程的PID,出错返回-1

fork()调用失败原因:系统中有太多进程,实际用户的进程数超过了限制;

写时拷贝:父子进程可写的部分,页表权限也设置为只读,这样之后父/子进程修改相关内容的时候就会出现异常,操作系统识别该部分可写,然后给要修改内容的进程重新分配物理内存空间和更新页表,最后把权限设置为正确的读写权限;

二、进程终止

#include <stdlib.h>

void exit(int status); ——  终止进程,在任意地方调用都是(主函数 + 自定义函数等)

#include <unistd.h>

void _exit(int status); ——  系统调用,终止进程

注意:1)exit()和_exit()的区别在于,_exit()是纯正的系统调用函数,不做其它动作,而exit()函数在终止进程之前还会做,执行用户定义的清理函数,冲刷缓冲关闭流等动作;2)缓冲区不在系统内核中;

Status:

  • If this is 0 or EXIT_SUCCESS, it indicates success.
  • If it is EXIT_FAILURE, it indicates failure.
  • 这也是之前写代码的时候总是“return 0”的原因,0是进程的退出码,表征进程的运行结果是否正确(0 —>  success,不同的数字 —> 各种失败的原因);
  • “echo $?”显示命令行当中最近一个程序运行的退出码;

#include <string.h>

char* strerror(int errnum);  ——  标准错误

#include <errno.h>

errno  ——  全局变量,保存最近的故障码;

进程异常退出:异常一般都是引起了硬件的异常,程序主要是收到了异常对应的信号;

kill -num PID:可以向对应进程发送num对应的异常信号,终止进程并报出相应的故障信息;

三、进程的等待

  • 僵尸进程无法被常规方法终止,只能通过进程等待来终止它,解决内存泄漏的问题;——  必须解决
  • 通过进程等待,获得子进程的退出情况;——  按需设置

 进程等待:是通过系统调用“pid_t wait(int* status)”或“pid_t waitpid(pid, int* status,int options)”,来进行对子进程状态检测和回收的功能;

//该代码创建N=10个子进程并关闭后,使用系统调用wait()回收了子进程资源
//子进程的返回值未予考虑
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/wait.h>

#define N 10

void showchild(id)
{
    prrintf("I am child : %d", id);
    sleep(5);
}

main()
{
//创建10个子进程
int i = 0;
for(i = 0, i < N, i++)
{
    pid_t id = fork();
    if (id > 0)
    {
        showchild(id);
        exit(0);
    }
    
}

sleep(10);
// 此代码让父进程等待10s这样子进程都结束了,其实没有也无所谓
// 因为如果子进程没结束,父进程在wait的时候也会阻塞在那里等待子进程结束后回收子进程资源

//等待回收创建的进程
for(i = 0, i < N, i++)
{
    pid_t id = wait(NULL);
     prrintf("success kill child : %d", id);
    
}
return 0;
}

int* status:

  • 该参数是输出型参数,即虽然是函数的参数,但是这个参数是为了在函数里面改变它,把相关信息带出来,起到输出作用的参数;
  •  虽然是int指针,但其内部按区域划分成了好几块,分别表示不同的信息,并不是一整个int;
  • 【0-6】这7个比特位,表示进程是否出异常,如未出异常全为“0”;【7】core dump标志位之后通信部分说明;【8-15】这8个比特位,表示退出状态(退出码),如果是异常退出,该处全为“0”;
  • 查看status中的部分值,可以通过“status & 0X7F”或“(status>>8) & 0XFF”这样的位运算提取,也可以通过操作系统提供的宏“WIFEXITED(status)【如果进程走完返回值为真,进程异常退出返回0】”或“WEXITSTATUS(status)【退出码】”

扩展:1)子进程运行完以后会释放代码和数据等信息,但PCB会保留,子进程退出信息会保存在PCB中;2)进程等待失败“wait()”/"waitpid()"会返回“-1”,经典返回失败场景,如等待的不是该父进程的子进程;

int options:

  • 该参数可以选择等待方式,默认值为“0”,表示阻塞等待方式,即如果子进程没有走完,父进程就一直等待;
  • 非阻塞等待(宏:WNOHANG):此时pid_t的返回值为“0”;该参数设为该值,当子进程没有走完的时候,父进程继续走,不理子进程了;
  • 如果想达成“非阻塞轮询”的效果,这个轮询需要自定义代码实现,多次循环调用"waitpid()";

四、进程的替换

之前用"fork()"创建进程的时候,子进程执行的是父进程的代码,最多就是用“if”等判断语句,执行和父进程不一样的分支,但总归还是在父进程的代码中写出来的。如果想要子进程执行和父进程完全不一样的代码,这就需要进程的替换了;

<unistd.h>

int execl (const char *path, const char *arg, ..., (char *)NULL);

其中:1)path是可执行程序的路径;2)arg是程序的名字;3)"..."可变参数列表;4)参数列表的结束标志,必须为 NULL;

功能:"execl()"函数会加载并运行一个新的程序,而不创建新的进程,原理是替换了原来进程的所有代码和数据;

说明:"execl()"函数执行成功后,原父进程"execl()"后面的代码将不被子进程执行,除非”execl()“函数执行失败(提供路径有误等原因);因此,该函数只有失败返回值(-1),成功将不会返回;

小知识:Linux中形成的可执行程序,是有格式的(ELF,executable and Linkable Format)配套有可执行程序的表头。表头不仅将可执行程序分区,还说明了可执行程序的入口地址;

进程替换库函数系列:

  • execl("/user/bin/ls", "ls", "-a", "-l", NULL);  【l:list】
  • execlp("ls", "ls", "-a", "-l", NULL);   【l:list;p:path】——   不需要写路径,函数会去环境变量PATH下查找;
  • execle(("/user/bin/ls", "ls", "-a", "-l", NULL,环境变量列表【以NULL结尾,如果采用了自定义环境变量列表,父进程的环境变量将会被覆盖,不再采用】)【e:environment】
  • 【stdlib.h】int putenv(char* string  <"PRIVATE_ENV=666666">);  ——  新增/修改环境变量
  • execv("/user/bin/ls",argv); argv[] = {"ls", "-a", "-l", NULL};    【v:vector】
  • execvp("ls", argv) ; 【vector + PATH】

进程替换系统调用函数:

  • 【unistd.h】execve(const char * filename, char* const argv[],  char* const envp[])

小知识:

  1. C++的源文件后缀可以是".cpp"、".cc"、".cxx";
  2. “.sh”后缀的文件是shell脚本;
  3. make-makefile实现一次编译多个可执行文件的方法(虚拟依赖关系):【.PHONY: all】|  【all: mycode  mycommand】;

shell脚本test.sh

#! /user/bin/bash        脚本语言并不是脚本在跑,而是使用“/user/bin/bash”这样的解释器在跑

< 内容为正常命令行指令 >

调用:bash test.sh

调用:execl(" /user/bin/bash", "bash", "test.sh", NULL)

python代码test.py

#! /user/bin/python3

< 正常python代码 >

调用:python3 test.py

调用:execl(" /user/bin/python3", "python3", "test.py", NULL)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值