系统编程之多进程
系列文章目录
目录
总结
前言
系统调用是由操作系统实现提供的所有系统调用所构成的集合即程序接口或应用编程接口 (Application Programming Interface,API)。是应用程序同系统之间的接口。系统功能调用是操作系统提供给程序设计人员的一种服务。
一、多进程
1. 相关的理论概念
- 进程:一个正在运行的程序,系统中就会产生一个对应的进程,动态的概念,在内存中运行。
- 程序:编译器编译得到的二进制程序,静态的概念,存储在电脑的磁盘上。
- 父进程:你调用fork这个函数,创建出来的就是子进程,调用fork的进程就是父进程。
- 子进程:fork产生的都是子进程。
- 进程的ID号(pid):类似于进程的"身份证号"。linux中可用 ps aux查看pid和进程名COMMAND
- 僵尸进程:僵尸进程是当子进程比父进程先结束,而父进程又没有回收子进程,释放子进程占用的资源,此时子进程将成为一个僵尸进程。如果父进程先退出 ,子进程被init接管,子进程退出后init会回收其占用的相关资源.。
- 进程的组成: 栈,堆,数据段,代码段,进程控制块(PCB--》process contrl block)
- 进程控制块: 指的是系统中定义的一个结构体,该结构体用于存储进程运行时候的状态信息。
- 进程组:多个进程组成的一个集合。
// 进程控制块结构体
struct task_struct{
pid_t pid; //当前进程的ID
struct files_struct *files; //指向另外一个结构体数组的,这个数组专门用来存放你的程序中已经打开的文件信息
};
2. linux提供的跟进程有关的接口函数
2.1 进程的创建
pid_t fork(void);
返回值: >0 父进程,此时返回值中保存的就是创建成功的那个子进程的ID号
==0 子进程
<0 失败特点:一次调用两次返回,跟我们以前学习的函数是不一样,我们以前的函数都只能一次调用,返回一次。父子进程究竟谁先执行,谁后执行,是不确定的
2.2 进程的退出
void exit(int status);
void _exit(int status);
参数:表示进程的退出值性质:
- 跟return的区别:return是关键字,exit和_exit都是函数
- exit和_exit的区别:exit结束进程的时候,会刷新缓冲区,但是_exit不会
- 刷新缓冲区:遇到回车,或者return或者exit()都能刷新缓冲区
2.3 进程的回收
2.3.1 wait()
#include <sys/wait.h>
pid_t wait(int *wstatus); // 阻塞等待当前父进程,直到子进程退出为止
返回值:成功 返回回收的那个子进程的ID号
失败 -1
参数:wstatus 用于保存进程退出时候的状态信息(包括退出值)
注意:exit的退出值仅仅只是退出状态信息的一部分
2.3.2 waitpid()
2.4 获取当前进程的ID号,获取父进程的ID号
pid_t getpid(void); // 返回值:当前进程的ID号
pid_t getppid(void); // 返回值:当前进程的父进程的ID号
pid_t getpgrp(void); // 返回值:当前进程所属进程组的ID号
/*
进程的退出与回收
*/
#include "test1.h"
#include <stdio.h>
#include <string.h>
#include <unistd.h>
int main()
{
int status;
pid_t id=fork();
if(id>0) //父进程
{
printf("父进程在运行!\n");
printf("父进程的ID号是:%lu\n",getpid());
}
else if(id==0) //子进程
{
printf("子进程在运行!\n");
printf("子进程的ID号是:%lu\n",getpid());
printf("我爹的ID号是:%ld\n",getppid());
//while(1); //让子进程无法正常退出
//结束子进程
exit(1);
}
else
{
perror("生娃失败!\n");
return -1;
}
//回收子进程
printf("等待子进程,等得好辛苦!\n");
pid_t otherid=wait(&status);
printf("我回收的子进程ID号是:%lu 它的退出状态信息是:%d\n",otherid,status);
return 0;
}
3.在一个进程中调用执行另外一个进程
可以通过在一个进程中调用执行shell命令实现,具体有两种实现方法
3.1 system()函数
在windows系统中,system函数直接在控制台调用一个command命令。
在Linux/Unix系统中,system函数会调用fork函数产生子进程,由子进程来执行command命令,命令执行完后随即返回原调用的进程
#include <stdlib.h>
int system(const char * command)
命令执行成功返回0,执行失败返回-1。
3.2 exec函数族
底层原理:以下六个函数通过创建子进程,然后在子进程里面执行你的命令或者程序
int execl(const char *path, const char *arg, ...);
返回值:失败 -1
参数:path --》你要执行的程序或者命令的路径名
arg --》你要执行的程序或者命令的参数int execlp(const char *file, const char *arg, ...);
参数:file --》你要执行的程序或命令的名字(不需要写路径名)
arg --》你要执行的程序或者命令的参数(以列表的形式逐一列举)
int execle(const char *path, const char *arg, ... (char *) NULL, char * const envp[]);
参数:path --》你要执行的程序或者命令的路径名
arg --》你要执行的程序或者命令的参数
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[]);
补充:
1 进程的状态改变
- 就绪态:进程已经运行了,但是还没有获取cpu的执行权
- 执行态:cpu的执行权已经切换到当前进程,当前进程执行
- 睡眠态、挂起态:sleep() wait() pause(),让你的进程暂时处于睡眠态
- 暂停态:进程收到STOP信号
- 僵尸态:进程退出,但是没有回收,就处于僵尸态
- 死亡态:进程退出,回收了,就正常结束死亡了
2 线程跟进程的关系
- 一个进程可以有多个线程,但至少有一个线程;而一个线程只能在一个进程的地址空间内活动。
- 资源分配给进程,同一个进程的所有线程共享该进程所有资源。
- CPU分配给线程,即真正在处理器运行的是线程。
- 线程在执行过程中需要协作同步,不同进程的线程间要利用消息通信的办法实现同步。
注:进程是最基本的资源拥有单位和调度单位。
总结
以上就是今天要讲的内容,本文介绍了多进程相关的理论概念和接口函数。