Linux多任务编程(并发)

并发

指用户可以在同一时间运行多个应用程序(任务)
一个任务可以包含一个或者多个独立的子任务
这些独立的子任务称之为进程或线程。

进程

Linux系统中用task_struct来描述和记录进程的一切,这个结构体称为进程控制块(PCB)
该结构体定义于 /usr/src/linux-headers-5.3.0-40/include/linux/sched.h
其中包括了进程的编号(PID)进程的状态 进程使用的文件

进程ID

Linux中任意一个进程(除了init进程)都有一个创建或者启动它的父母
启动或者创建他的进程称之为新进程的父进程
当新进程被创建时,内核会给之分配一个唯一的编号,称为进程ID,简称PID
操作系统通过进程的PID来对进程进行管理

可以通过使用ps指令来查看进程的ID 状态等信息
ps -ef
ps -aux
除了指令外,系统还提供了相应的接口可以从代码中获取进程的ID
getpid() //获取当前进程的ID
getppid() //获取父进程的ID

进程的地址空间

从逻辑上而言,指令和数据是不同的,即使是数据也分为 全局变量 局部变量 常量 …
所以为了方便管理进程的使用空间,系统把进程的内存地址空就划分为不同的段。

.text 代码段/文本段 存放指令 只读,共享
.rodata 只读的常量区 字符串
.data 数据段 初始化的静态变量和全局变量
.bss 未初始化的数据段 未始化的静态变量和全局变量,一般在程序执行前,全部置零
.heap 堆 也称为动态内存空间 如malloc
.stack 栈 存放局部变量

进程的状态

进程从创建到消亡的过程
创建:进程正在创建
就绪:进程具备一切执行条件,正在等待CPU的调度
运行:进程正在运行占用CPU
阻塞:因等待某个事件而发生休眠,如果资源分配到位,就会被唤醒进入就绪状态
僵死:进程已经结束,但资源未回收
消亡:进程结束

进程的特点

1.进程是程序的一次执行活动从产生到消亡是一次动态的过程而程序是静态的,是指令的有序集合。
2.正在运行的进程由程序的代码、数据等组成,进程是系统分配资源的基本单位
3.进程有自己的地址空间,也仅能使用自己的空间
4.一个程序可以对应多个进程,但一个进程只能对应一个程序

相关的API

进程标识

#include <unistd.h>
pid_t getpid(void);
pid_t getppid(void);

进程的创建

#include <unistd.h>
pid_t fork(void);
说明:
该函数创建一个新的进程,新进程为调用进程的子进程,调用进程称为新进程的父进程
如果fork成功,则父子进程同时存在,而子进程几乎是完全对父进程的复制.
如果fork成功,父子进程分别返回,父进程返回的是子进程的PID,子进程返回的是0
如果失败,则返回-1.同时设置errno

执行一个新程序
exec族 主要作用是让一个程序 去执行一个指定的程序文件

指定程序的参数有两种:
l:
把程序运行的参数,一个一个的列举出来
如:
"sum" "3" "4" "NULL"
v:
把程序运行的参数 弄成一个char * 的数组
如:
char * buf {
    
    "sum","3","4",NULL};

(相关函数)

#include <unistd.h>
extern char **environ;

int execl(const char *path, const char *arg, ... /* (char *) NULL */);
参数:
@path:要执行外部程序的绝对路径
@arg:要执行程序的名字
... 如果程序要其他的参数 写在后面即可 以NULL结尾


int execv(const char *path, char *const argv[]);
参数:
@path:要执行外部程序的绝对路径
@argv:要执行程序的名字,及其参数的字符串数组形式
系统中有一个环境变量 PATH
环境变量是整个系统环境所以进程共享的变量
PATH:=dir1:dir2:dir3....
pATH的作业是指定命令或者程序的搜索路径
当只指定了一个命令或者文件名是,如果没有指定路径
那么系统会首先从dir1这个目录里面找,......
意思就是说,如果你的程序文件在PATH这个搜索路径中那么你指定的文件就可以不带路
径
p:指定的文件在标准的搜索路径下面


int execlp(const char *file, const char *arg, ... /* (char *) NULL */);
参数
@:file:要执行的程序的名字,可以不带路径,会自动去环境变量里面查找
4.执行shell指令
5.进程结束
6.等待子进程结束
@arg要执行程序的名字
... 如果程序要其他的参数 写在后面即可 以NULL结尾


int execvp(const char *file, char *const argv[]);
返回:
失败返回-1,同时设置errno
成功,原有的进程则会被新的程序所替换掉.

执行shell指令

#include <stdlib.h>
int system(const char *command);
功能:执行指定的指令
参数:指令字符串 如 "ls -al 1.c"
system("ls -al 1.c");
or
char cmd[256];
sprintf(cmd,"ls -al %s",name);
system(cmd);

进程结束

#include <stdlib.h> 库函数
void exit(int status);//结束当前进程 刷新stdio的缓存区

#include <unistd.h> //系统接口
void _exit(int status);//结束当前进程

等待子进程结束

#include <sys/types.h>
#include <sys/wait.h>
pid_t wait(int *wstatus);
功能:阻塞等待任意子进程结束 ,并释放子进程占用的资源
参数:@wstatus 退出状态码 一般为NULL
返回值:
失败返回-1,同时设置errno
成功返回结束进程的pid

pid_t waitpid(pid_t pid, int *wstatus, int options);
功能:等待任意子进程结束 ,并释放子进程的资源
参数:
@pid:
可以由有如下写法:
== -1 表达等待任意的子进程结束
== 0 表示等于与调用进程同组的任意子进程
”进程组“
就是一组进程,每一给进程都必须属于某一个进程组
并且每个进程组都会有一个组长进程,一般来说组长就是
创建这个进程组的进程,进程组的组id就是组长进程的id
< -1 表示等到组id等于pid的绝对值的那个组的任意进程
如: pid == -2398
等待进程组2398的那个组的任意子进程
@wstatus:
退出状态码, 一般填NULL
@options:功能选项 为0则表示阻塞,如果不想阻塞,则可以设置为WNOHANG
返回值:
成功返回结束进程的pid
如果调用该接口没有进程结束则返回0
失败返回-1.同时设置errno
一般来说,子进程结束后,父进程调用wait/waitpid去释放子进程资源
但是,又可以子进程还在运行,他的父进程先结束了,系统会让init进程来成为这些孤儿进程的新
的父进程

猜你喜欢

转载自blog.csdn.net/weixin_46836491/article/details/126502499