本篇博客从进程的三个方面做大体介绍,欢迎读者朋友评论与交流。所写内容依然还是以适用为主,方法为辅,若需要更加深层次了解进程的朋友可先阅读操作系统原理之类的书籍。
进程在日常开发中,更多的用于不同程序之间的交互与通信,需要操作系统作为中间媒介,进程通信方式有很多种,稍后会挑两种最常用的通信方式进行讲解。本篇博客关于进程描述的三个方面分别为: 什么是进程? 进程间如何通信? 进程是怎么调度的?
一 进程
进程是程序的一次执行过程,在此过程中,进程会向操作系统申请各种资源(内存、CPU、执行时机),最后进程执行结束后,操作系统回收相关资源,进程也就此消亡。
进程与程序的差别:
1. 进程是一个动态的概念 : 进程是程序的一次执行过程,是动态概念。程序是一组有序的指令集和,是静态概念。
2. 不同的进程可以执行同一个程序, 区分点是所执行的程序和数据集合。
3. 每个进程都有自己的生命周期: 当操作系统要完成某个任务时,它会创建一个进程。当进程完成任务之后,系统就会撤销这个进程,收回它所占用的资源。从创建到撤销的时间段就是进程的生命期。
4. 进程之间存在并发性: 在一个系统中,同时会存在多个进程。他们轮流占用CPU和各种资源。
5. 进程间会相互制约: 进程是系统中资源分配和运行调度的单位,在对资源的共享和竞争中,必然相互制约,影响各自向前推进的速度。
6. 进程可以创建子进程,程序不能创建子程序.
7. 每个进程都由程序、数据和一个进程控制块(Process Control Block, PCB)组成.
进程的重要特征
1. 动态特征:进程对应于程序的运行,动态产生、消亡,在其生命周期中进程也是动态的。
2. 并发特征:任何进程都可以同其他进程一起向前推进。
3. 独立特征:进程是相对完整的调度单位,可以获得CPU,参与并发执行。
4. 交往特征:一个进程在执行过程中可与其他进程产生直接或间接关系。
5. 异步特征:每个进程都以相对独立、不可预知的速度向前推进。
6. 结构特征:每个进程都有一个PCB作为他的数据结构。
进程最基本的特征是并发和共享特征。
进程的状态与转换
1. 进程的三种基本状态
a. 运行状态:获得CPU的进程处于此状态,对应的程序在CPU上运行着。
b. 阻塞状态:为了等待某个外部事件的发生(如等待I/O操作的完成,等待另一个进程发来消息),暂时无法运行。也成为等待状态。
c. 就绪状态:具备了一切运行需要的条件,由于其他进程占用CPU而暂时无法运行。
2. 进程状态转换
a. 运行状态 ===> 阻塞状态:例如正在运行的进程提出I/O请求,由运行状态转化为阻塞状态。
b. 阻塞状态 ===> 就绪状态:例如I/O操作完成之后,由阻塞状态转化为就绪状态。
c. 就绪状态 ===> 运行状态:例如就绪状态的进程被进程调度程序选中,分配到CPU中运行,由就绪状态转化为运行状态。
d. 运行状态 ===> 就绪状态:处于运行状态的进程的时间片用完,不得不让出uCPU,由运行状态转化为就绪状态。
3. 进程的类型
a. 系统进程:操作系统用来管理资源的进程,当系统进程处于运行态时,CPU处于管态,系统之间的关系由操作系统负责。
b. 用户进程:操作系统可以独立执行的的用户程序段,当用户进程处于运行态时,CPU处于目态,用户进程之间的关系由用户负责。
二 进程通信
进程通信一般都需要遵循IPC规范,IPC(Interprocess communication 进程间通信)是一组编程接口,编程开发人员可以协调不同的进程在同一个操作系统里同时或同步运行。
通信的作用主要有:
数据传输:一个进程需要将它的数据发送给另一个进程,发送的数据量在一个字节到几兆字节之间。
共享数据:多个进程想要操作共享数据,一个进程对共享数据的修改,别的进程可以看到。
通知事件:一个进程需要向另一个或一组进程发送消息,告知当前正在发生的事务。
资源共享:多个进程之间共享同样的资源。
进程控制:A进程完全监控B进程在各个状态下的动作与信息;
目前常用的通信方式有以下几种:
管道(pipe):管道是一种半双工的通信方式,数据只能单向流动,而且只能在具有亲缘关系的进程间使用。
有名管道(named pipe):有名管道也是半双工的通信方式,但是它允许无亲缘关系进程间的通信。
信号量(semophore):信号量是一个计数器,可以用来控制多个进程对共享资源的访问。
消息队列(message queue):消息队列就是消息的一个链表,存放在内核中并由消息队列标识符标识。
信号(signal):信号是一类似发送消息的通信机制,用于通知接收进程某个事件已经发生。
共享内存(shared memory):共享内存就是映射一段能被其他进程所访问的内存,这段共享内存由一个进程创建,但多个进程都可以访问。
套接字(socket):套接字也是一种进程间通信机制,一般使用无线或有线网络作为媒,并且套接字还可用于不同机器间的进程通信。
Qt平台常用的进程通信方式
TCP/IP: 跨平台的Qt Network模块提供的类可以让网络编程更加便携和方便。它提供了高级类(例如:QNetworkAccessManager、QFtp)通信,使用特定的应用程序级协议,和较底层的类(例如:QTcpSocket、QTcpServer、QSslSocket)用于实现协议。
Shared Memory
跨平台的QSharedMemory-共享内存类,提供对操作系统的共享内存的实现。它允许多个线程和进程安全访问共享内存段。此外,QSystemSemaphore可以用来控制访问由系统共享的资源,以及进程之间的通信。
D-Bus
Qt的D-Bus模块是一种可用于使用D-Bus协议实现IPC的唯一Unix库。它将Qt的信号和槽机制延伸到IPC级别,允许由一个进程发出的信号被连接到另一个进程的槽。Qt的D-Bus文档已经详细说明如何使用Qt中的D-Bus模块。
QProcess
跨平台类QProcess可以用于启动外部程序作为子进程,并与它们进行通信。
Session Management
在Linux/X11平台上,Qt提供了会话管理的支持。会话允许事件传播到进程。
进程通信实际演示
这里演示三种进程通信方式,并且所写的代码是支持跨平台的,实际代码如下:
新建一个GUI工程程序,名字叫MyIPC_Qt ,
进程通信方式一: QProcess
在上面的工程中,需要实现的功能是当点击按钮时,让当前操作系统下的文本工具打开选定的文本文件。
按钮布局:
接着改一下控件对象名称,便于稍后使用,同时这也是编程的基本要求,及命名规范问题。
接下来开始在mainwindow.cpp中写代码:
mainwindow.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include <QProcess>
QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACE
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
MainWindow(QWidget *parent = nullptr);
~MainWindow();
private slots:
void selsctFilePath();
void openFileText();
void stateChanged(QProcess::ProcessState newState);
void closeExe();
private:
Ui::MainWindow *ui;
QProcess *process;
};
#endif // MAINWINDOW_H
mainwindow.cpp
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include<QFileDialog>
#include <QProcess>
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
{
ui->setupUi(this);
process=new QProcess;
connect(ui->select_bt,SIGNAL(clicked()),this,SLOT(selsctFilePath()));
connect(ui->openText_bt,SIGNAL(clicked()),this,SLOT(openFileText()));
connect(process,SIGNAL(stateChanged(QProcess::ProcessState)),this,SLOT(stateChanged(QProcess::ProcessState)));
connect(ui->close_exe,SIGNAL(clicked(bool)),this,SLOT(closeExe()));
}
MainWindow::~MainWindow()
{
delete ui;
}
void MainWindow::selsctFilePath()
{
QString fileName = QFileDialog::getOpenFileName(this,tr("文件对话框!"), "F:",tr("all文件(* *)"));
ui->filePathLineEdit->setText(fileName);
}
void MainWindow::openFileText()
{
QStringList path;
path<<ui->filePathLineEdit->text().trimmed();
QString exePath="gedit"; //QString exePath="/usr/bin/gedit";
if(! path.isEmpty()){
process->start(exePath,path);
}
}
void MainWindow::stateChanged(QProcess::ProcessState newState)
{
switch(newState)
{
case QProcess::NotRunning:
ui->exe_state->setText("Not Running");
break;
case QProcess::Starting:
ui->exe_state->setText("Starting");
break;
case QProcess::Running:
ui->exe_state->setText("Running");
break;
default:
ui->exe_state->setText("otherState");
break;
}
}
void MainWindow::closeExe()
{
if(process->Running){
process->close();
ui->exe_state->setText("程序关闭成功!");
}else if(process->state()==QProcess::NotRunning){
ui->exe_state->setText("程序未运行!");
}
}
main.cpp
#include "mainwindow.h"
#include <QApplication>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
MainWindow w;
w.show();
return a.exec();
}
运行效果:
点击打开文件按钮后,顺利打开文本编辑软件gedit ,(不过有个前提,需要电脑上面存在这个软件);
点击关闭按钮后:
进程通信方式二: Shared Memoryc
在使用QSharedMemory时,创建的共享内存key与读取共享内存的key名称要一致,否则无法访问已创建的共享内存。
下面使用一个按钮把一张图片放置到共享内存中,并把共享内存的key名称设置为 qt ,另一个按钮实现从共享内存中读取图片并显示出来。示例代码如下:
mainwindow.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include<QSharedMemory>
QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACE
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
MainWindow(QWidget *parent = nullptr);
~MainWindow();
private slots:
void selectImage();
void shareImage();
private:
Ui::MainWindow *ui;
QSharedMemory *shareMemory;
};
#endif // MAINWINDOW_H
mainwindow.cpp
#include "mainwindow.h"
#include "./ui_mainwindow.h"
#include <QBuffer>
#include <QFileDialog>
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
{
ui->setupUi(this);
connect(ui->pushButton,SIGNAL(clicked()),this,SLOT(selectImage()));
connect(ui->bt2,SIGNAL(clicked()),this,SLOT(shareImage()));
//init QShareMemory and set key
shareMemory=new QSharedMemory;
shareMemory->setKey("qt");
}
MainWindow::~MainWindow()
{
delete ui;
}
//create shareMemory
void MainWindow::selectImage()
{
auto openFileName = QFileDialog::getOpenFileName(this,QString(),QString(),"image (*.png *.jpg)");
ui->label->setPixmap(QPixmap(openFileName));
if(shareMemory->isAttached()){
if(shareMemory->detach()) qDebug()<<"detech....";
}
QPixmap map(openFileName);
QBuffer buffer;
buffer.open(QBuffer::ReadWrite);
QDataStream stream(&buffer);
stream<<map;
//create shareMemory
int size=buffer.size();
if(! shareMemory->create(size)){
qDebug()<<shareMemory->errorString();
return;
}
shareMemory->lock();
void *to=shareMemory->data();
void *from=(void*)buffer.data().data();
memcpy(to,from,buffer.size());
shareMemory->unlock();
qDebug()<<"load succ";
}
//read shareMemory
void MainWindow::shareImage()
{
//read shareMemory content
QSharedMemory shareMem;
shareMem.setKey("qt");
if(! shareMem.attach()){
qDebug()<<"attech error:" <<shareMem.errorString();
return;
}
QBuffer buffer;
QDataStream stream(&buffer);
shareMem.lock();
buffer.setData((char*)shareMem.data(),shareMem.size());
shareMem.unlock();
buffer.open(QBuffer::ReadOnly);
QPixmap pix;
stream>>pix;
ui->label_2->setPixmap(pix);
}
main.cpp
#include "mainwindow.h"
#include <QApplication>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
MainWindow w;
w.show();
return a.exec();
}
运行效果:
进程通信方式三: TCP/IP
在演示网络通信时,需要一个服务器端和一个客户端,这里我们将建立两个工程,一个工程作为服务器端,一个工程作为客户端,在两个工程运行时,服务器端需要优先运行。
在Qt中使用TCP/IP协议进行通信时,需要在工程文件中加入网络模块,博主使用的是CMake 。下面是CMake和qmake加入网络模块的写法,各位小伙伴根据自己的实际情况在自己的工程文件中加入网络模块。
CMake:
find_package(Qt6 COMPONENTS Network REQUIRED)
target_link_libraries(mytarget PRIVATE Qt6::Network)
qmake:
QT += network
首先新建一个工程作为服务器端,名称叫test_tcp_server ,代码如下:
mainwindow.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include <QTcpServer>
#include <QTcpSocket>
QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACE
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
MainWindow(QWidget *parent = nullptr);
~MainWindow();
private slots:
void on_send_clicked();
void on_close_clicked();
private:
Ui::MainWindow *ui;
QTcpServer *tcpserver;
QTcpSocket *tcpsocket;
};
#endif // MAINWINDOW_H
mainwindow.cpp
#include "mainwindow.h"
#include "./ui_mainwindow.h"
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
{
ui->setupUi(this);
setWindowTitle("server: port is 8888");
tcpserver=nullptr;
tcpsocket=nullptr;
//创建监听套接字
tcpserver=new QTcpServer(this);//指定父对象 回收空间
//bind+listen
tcpserver->listen(QHostAddress::Any,8888);//绑定当前网卡所有的ip 绑定端口 也就是设置服务器地址和端口号
//服务器建立连接
connect(tcpserver,&QTcpServer::newConnection,[=](){
//取出连接好的套接字
tcpsocket=tcpserver->nextPendingConnection();
//获得通信套接字的控制信息
QString ip=tcpsocket->peerAddress().toString();//获取连接的 ip地址
quint16 port=tcpsocket->peerPort();//获取连接的 端口号
QString temp=QString("[%1:%2] 客服端连接成功").arg(ip).arg(port);
//显示连接成功
ui->textEdit_read->setText(temp);
//接收信息 必须放到连接中的槽函数 不然tcpsocket就是一个野指针
connect(tcpsocket,&QTcpSocket::readyRead,[=](){
//从通信套接字中取出内容
QString str=tcpsocket->readAll();
//在编辑区域显示
ui->textEdit_read->append("客户端:"+str);//不用settext 这样会覆盖之前的消息
});
});
}
MainWindow::~MainWindow()
{
delete ui;
}
void MainWindow::on_send_clicked()
{
if(tcpsocket==nullptr){
return ;
}
//获取编辑区域的内容
QString str=ui->textEdit_write->toPlainText();
//写入通信套接字 协议栈自动发送
tcpsocket->write(str.toUtf8().data());
//在编辑区域显示
ui->textEdit_read->append("服务器端:"+str);//不用settext 这样会覆盖之前的消息
}
void MainWindow::on_close_clicked()
{
//通信套接字主动与服务端断开连接
tcpsocket->disconnectFromHost();//结束聊天
//关闭 通信套接字
tcpsocket->close();
tcpsocket=nullptr;
}
main.cpp
#include "mainwindow.h"
#include <QApplication>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
MainWindow w;
w.show();
return a.exec();
}
先运行服务器端,效果如下:
然后接着建议客户端,工程名称叫test_tcp_client ,代码如下:
mainwindow.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include <QTcpSocket>
QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACE
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
MainWindow(QWidget *parent = nullptr);
~MainWindow();
private slots:
void on_send_clicked();
void on_close_clicked();
void on_connect_clicked();
private:
Ui::MainWindow *ui;
QTcpSocket *tcpsocket; //声明套接字 客户端只有一个通信套接字
};
#endif // MAINWINDOW_H
mainwindow.cpp
#include "mainwindow.h"
#include "./ui_mainwindow.h"
#include<QTcpServer>
#include<QTcpSocket>
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
{
ui->setupUi(this);
tcpsocket=nullptr;
setWindowTitle("客户端");
tcpsocket=new QTcpSocket(this);
connect(tcpsocket,&QTcpSocket::connected,[=](){
ui->textEdit_read->setText("服务器连接成功!");
});
connect(tcpsocket,&QTcpSocket::readyRead,[=](){
//获取通信套接字的内容
QString str=tcpsocket->readAll();
//在显示编辑区域显示
ui->textEdit_read->append("服务器端:"+str);//不用settext 这样会覆盖之前的消息
});
}
MainWindow::~MainWindow()
{
delete ui;
}
void MainWindow::on_send_clicked()
{
if(nullptr==tcpsocket)//连接失败则不发送
return;
//获取发送的信息
QString str=ui->textEdit_write->toPlainText();
//将信息写入到通信套接字
tcpsocket->write(str.toUtf8().data());
//将自己的信息显示在聊天窗口
ui->textEdit_read->append("客服端:"+str);//不用settext 这样会覆盖之前的消息
}
void MainWindow::on_close_clicked()
{
if(nullptr==tcpsocket)
return;
tcpsocket->disconnectFromHost();//断开与服务器的连接
tcpsocket->close();//关闭通信套接字
}
void MainWindow::on_connect_clicked()
{
if(nullptr==ui->server_ip || nullptr==ui->server_port)
return ;
//获取IP地址和端口号
QString IP=ui->server_ip->text();
quint16 Port=ui->server_port->text().toInt();
//与服务器连接
tcpsocket->connectToHost(IP,Port);
}
main.cpp
#include "mainwindow.h"
#include <QApplication>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
MainWindow w;
w.show();
return a.exec();
}
客户端代码写完后,运行效果:
此时,我们在服务器端与客户端之间发送一些信息,验证一下是否具备通信功能,经过验证,功能没有问题。
三 进程调度
进程调度属于操作系统层面的知识,在日常开发中,真正的进程调度均有操作系统完成。
高级语言也仅仅是调用系统的API来完成一些辅助操作。
这里博主推荐一篇博文给大家学习,链接如下:
操作系统——四种进程调度算法模拟实现(C语言)_编写c程序模拟实现单处理机系统中的进程调度算法,实现对多个进程的调度模拟,要求_偏执≈的博客-CSDN博客操作系统——四种进程调度算法模拟实现(C语言)https://blog.csdn.net/mcr1379673758/article/details/124562838?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522167940345716800226560427%2522%252C%2522scm%2522%253A%252220140713.130102334.pc%255Fall.%2522%257D&request_id=167940345716800226560427&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2~all~first_rank_ecpm_v1~pc_rank_34-11-124562838-null-null.142^v75^pc_new_rank,201^v4^add_ask,239^v2^insert_chatgpt&utm_term=QT%20%E8%BF%9B%E7%A8%8B%E8%B0%83%E5%BA%A6&spm=1018.2226.3001.4187
下一篇博文:
Qt6教程之三(9) 多线程、线程间通讯、线程调度_折腾猿王申兵的博客-CSDN博客本篇博客主要讲解Qt中多线程的使用!https://blog.csdn.net/XiaoWang_csdn/article/details/129697936?csdn_share_tail=%7B%22type%22%3A%22blog%22%2C%22rType%22%3A%22article%22%2C%22rId%22%3A%22129697936%22%2C%22source%22%3A%22XiaoWang_csdn%22%7D文章来源:https://www.toymoban.com/news/detail-769364.html
上一篇博文:Qt6教程之三(7) Qt开发桌面软件常用技术_qt桌面开发_折腾猿王申兵的博客-CSDN博客大体介绍Qt开发桌面软件常用工具类及相关技术!https://blog.csdn.net/XiaoWang_csdn/article/details/129347751文章来源地址https://www.toymoban.com/news/detail-769364.html
到了这里,关于Qt6教程之三(8 )多进程、进程间通讯和调度的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!