【Linux】七、进程间通信

简介: 目录一、进程间通信介绍1.1 进程间通信概念1.2 为什么要有进程间通信1.3 进程间通信目的1.4 进程间通信分类1.5 进程间通信的本质二、管道2.1 什么是管道2.2 匿名管道2.2.1 pipe函数2.2.2 匿名管道的原理2.2.3 匿名管道的使用2.2.4 以文件描述符的角度看待2.2.5 匿名管道测试代码2.2.6 匿名管道读写规则2.2.7 匿名管道的特征2.2.8 基于匿名管道的进程池2.3 命名管道2.3.1 使用命令创建命名管道 2.3.2 命名管道的原理2.3.3 在程序中创建命名管道

目录

一、进程间通信介绍

1.1 进程间通信概念

1.2  为什么要有进程间通信

1.3 进程间通信目的

1.4 进程间通信分类

1.5 进程间通信的本质

二、管道

2.1 什么是管道

2.2 匿名管道

2.2.1 pipe函数

2.2.2 匿名管道的原理

2.2.3 匿名管道的使用

2.2.4 以文件描述符的角度看待

2.2.5 匿名管道测试代码

2.2.6 匿名管道读写规则

2.2.7 匿名管道的特征

2.2.8 基于匿名管道的进程池

2.3 命名管道

2.3.1 使用命令创建命名管道

2.3.2 命名管道的原理

2.3.3 在程序中创建命名管道

2.3.4 unlink函数

2.3.5 使用命名管道实现serve&client通信

2.3.6 匿名管道与命名管道的区别


一、进程间通信介绍

1.1 进程间通信概念

       进程间通信就是在不同进程之间传播或交换信息,进程间通信简称IPC(Interprocess communication)

1.2  为什么要有进程间通信

为什么要有进程间通信??

有时候我们是需要多进程协同的,去完成某种业务

1.3 进程间通信目的

  1. 数据传输:一个进程需要将它的数据发送给另一个进程
  2. 资源共享:多个进程之间共享同样的资源
  3. 通知事件:一个进程需要向另一个或一组进程发送消息,通知它(它们)发生了某种事件(如进程终止时要通知父进程)
  4. 进程控制:有些进程希望完全控制另一个进程的执行(如Debug进程),此时控制进程希望能够拦截另一个进程的所有陷入和异常,并能够及时知道它的状态改变

1.4 进程间通信分类

(1)管道

  • 匿名管道
  • 命名管道

(2)System V IPC

  • System V 消息队列
  • System V 共享内存
  • System V 信号量

(3)POSIX IPC

  • 消息队列
  • 共享内存
  • 信号量
  • 互斥量
  • 条件变量
  • 读写锁

管道:管道是基于文件系统的,System V IPC:聚焦在本地通信,POSIX IPC:让通信可以跨主机

1.5 进程间通信的本质

进程间通信的本质就是:让不同的进程看到同一份资源

两个进程间想要通信,就必须提供某一个资源,这个资源用于给两个进程之间进行通信。这个资源不能是进程的双方提供的,因为进程是具有独立性的,一个进程提供了资源,进行通信另一个进程必定会访问这个资源,这时就破坏了进程的独立性

因此,这个资源只能由第三方提供,这个第三方就是OS,OS需要直接或间接给通信双方的进程提供 “内存空间”

image.png

这个资源可以是OS中不同的模块提供,不同的模块提供的不同资源,造就了不同的通信种类(消息队列,共享内存,信号量...),因此出现了不同的通信方式

所以,进程间想要通信,首先要看到同一份资源,看到同一份资源才会有通信

二、管道

2.1 什么是管道

管道是Unix中最古老的进程间通信的形式,我们把从一个进程连接到另一个进程的一个数据流称为一个 “管道”

比如,我们执行的这条 cat file | grep hello 命令,其中 “|” 就是管道

image.png

其中,cat 命令和 grep 命令都是两个程序,当它们运行起来后就变成了两个进程,cat进程的数据传输到 “管道” 当中,grep进程再通过 “管道” 当中读取数据,至此便完成了数据的传输,两个进程就完成了通信

image.png

管道又分匿名管道和命名管道

2.2 匿名管道

匿名管道用于进程间通信,且仅限于本地父子进程之间通信

2.2.1 pipe函数

pipe函数用于创建匿名管道,man查看pipe,pipe函数是一个系统调用

man 2 pipe

image.png

pipe
头文件:#include <unistd.h>
函数原型
int pipe(int pipefd[2]);
返回值
成功时返回0,调用失败时返回-1且错误码被设置

pipe函数的参数 pipefd[2] 是一个输出型参数,数组pipefd 用于返回两个指向管道读端和写端的文件描述符

pipe函数的参数 pipefd[2] 是一个输出型参数,数组pipefd 用于返回两个指向管道读端和写端的文件描述符

  • pipefd[0]是管道读端的文件描述符
  • pipefd[1]是管道写端的文件描述符

帮助记忆:0可以想象成嘴(读),1可以想象成笔(写)

image.png

因为匿名管道仅用于父子进程间通信,所以要使用匿名管道就要使用 fork函数

2.2.2 匿名管道的原理

匿名管道用于进程间通信,且仅限于本地父子进程之间通信

进程间通信的本质就是,让不同的进程看到同一份资源,使用匿名管道实现父子进程间通信的原理就是,让两个父子进程先看到同一份被打开的文件资源,然后父子进程就可以对该文件进行写入或是读取操作,进而实现父子进程间通信

该文件资源是文件系统提供的,该文件资源就是匿名管道,该文件资源的操作方法与文件一致,也有自己的文件缓冲区

注意:父子进程对该文件进行写入操作时,该文件缓冲区当中的数据不会发生写时拷贝,该文件资源由文件系统维护

2.2.3 匿名管道的使用

管道只能单向通信,不能双向通信。比如,一端是写入了,另一端就必须是读取,反过来也是,一端进行读取,另一端必须进行写入

(1)父进程调用pipe函数创建管道

image.png

(2)父进程进行创建子进程

image.png

(3)父进程需要读取,父进程就需要关闭写端,子进程进行写入,子进程需要关闭读端

image.png

(4)父进程需要写入,父进程就需要关闭读端,子进程进行读取,子进程需要关写读端

image.png

注意:管道是单向通信的

2.2.4 以文件描述符的角度看待

站在文件描述符的角度看待匿名管道:

(1)父进程调用pipe函数创建管道

image.png

(2)父进程进行创建子进程

image.png

(3)父进程需要读取,父进程就需要关闭写端,子进程进行写入,子进程需要关闭读端

image.png

(4)父进程需要写入,父进程就需要关闭读端,子进程进行读取,子进程需要关写读端

image.png

2.2.5 匿名管道测试代码

以子进程写入,父进程读取为例

#include <iostream>#include <unistd.h>#include <cstdio>#include <cassert>#include <cstring> #include <sys/types.h>#include <sys/wait.h>usingnamespacestd;
//子进程写入,父进程读取intmain()
{
// 第一步:创建管道文件,打开读写端intfds[2];
intn=pipe(fds);
assert(n==0);//否则创建管道失败,直接断言//创建子进程pid_tid=fork();
assert(id>=0);//否则创建子进程失败//子进程通信代码--子进程写入if(id==0)
    {
//关闭读端,写端打开close(fds[0]);
constchar*s="我是子进程,我正在给你发消息";
intcnt=0;
while(true)
        {
++cnt;
charbuffer[1024];//只能在子进程看到snprintf(buffer, sizeofbuffer, "child -> parent say: %s[%d][子进程pid:%d]", s, cnt, getpid());
write(fds[1], buffer, strlen(buffer));
sleep(3);
if(cnt>=10)
break;
        }
close(fds[1]);
cout<<"子进程关闭自己的写端"<<endl;
exit(0);
    }
//父进程通信代码--父进程读取close(fds[1]);
while(true)
    {
sleep(1);
charbuffer[1024];
ssize_ts=read(fds[0], buffer, sizeof(buffer)-1);
if(s>0)//读取到数据        {
buffer[s] ='\0';//防止越界cout<<"Get Message# "<<buffer<<" | 父进程pid: "<<getpid() <<endl;
        }
elseif(s==0) //读到文件结尾        {
cout<<"父进程读取完成"<<endl;
break;
        }
    }
close(fds[0]);
cout<<"父进程的读端关闭"<<endl;
//等待子进程intstatus=0;
n=waitpid(id, &status, 0);
cout<<"等待子进程pid->"<<n<<" : 退出信号:"<< (status&0x7F) <<endl;
return0;
}

运行结果

image.png

2.2.6 匿名管道读写规则

  1. 读快,写慢。如果管道中没有数据,读端进程再进行读取,会阻塞当前正在读取的进程;如果写端不进行写入,读端进程会一直阻塞;
  2. 读慢,写快。如果写端把管道写满了,再写就会对该进程进行阻塞,需要等待对方对管道内数据进行读取;如果读端不读取数据,写端进程会一直阻塞;
  3. 写关闭,读取到0。如果写入进程关闭了写入fd,读取端将管道内的数据读完后,程序结束
  1. 读关闭,写?如果读关闭,操作系统会给写端发送13号信号SIGPIPE,终止写端。

(1)读快,写慢

上面代码是读快,写慢这种情况

b668387a3d7f46a18ad67158fd7cee09.gif

(2)读慢,写快

修改代码,修改sleep时间即可

image.png

运行结果

459f1666994c4f4a95f76b13402b7293.gif

(3)写关闭,读取到0

写入一条消息,直接关闭写端

image.png

运行结果

c39fd0017f534a24bd9c0fca2b20a617.gif

(4)读关闭,写?

读一次,直接把读端关闭

image.png

运行结果

74f2d5367cc049c5b33e46d6424e7861.gif

2.2.7 匿名管道的特征

  1. 只能用于具有共同祖先的进程(具有亲缘关系的进程)之间进行通信;通常,一个管道由一个进程创建,然后该进程调用fork,此后父、子进程之间就可应用该管道
  2. 管道提供流式服务(网络)
  3. 一般而言,进程退出,管道释放,所以管道的生命周期随进程
  4. 一般而言,内核会对管道操作进行同步与互斥(多线程)
  5. 管道是半双工的,数据只能向一个方向流动;需要双方通信时,需要建立起两个管道

2.2.8 基于匿名管道的进程池

实现思路:父进程控制写端进行写入,子进程进行读取,读取命令码后执行相应的任务,父进程创建多个子进程

image.png

代码:

#include <iostream>#include <string>#include <vector>#include <ctime>#include <cassert>#include <unistd.h>#include <sys/types.h>#include <sys/wait.h>usingnamespacestd;
#define makeSeed() srand((unsigned long)time(nullptr) ^ getpid() ^ 0x363 ^ rand() % 1234)#define PROCESS_SUM 10typedefvoid (*func_t)();//函数指针类型//--------------------------------  模拟一下子进程要完成的某种任务  --------------------- voiddownloadTask()
{
cout<<getpid() <<"执行下载任务\n"<<endl;
sleep(1);
}
voidioTask()
{
cout<<getpid() <<"执行io任务\n"<<endl;
sleep(1);
}
voidflushTask()
{
cout<<getpid() <<"执行刷新任务\n"<<endl;
sleep(1);
}
voidloadTaskFunc(vector<func_t>*out)
{
assert(out);
out->push_back(downloadTask);
out->push_back(ioTask);
out->push_back(flushTask);
}
//--------------------------------  以下代码是多进程代码  --------------------- classsubEP//sub end point{
public:
subEP(pid_tsubId, intwriteFd)
        :_subId(subId)
        ,_writeFd(writeFd)
    {
charnameBuffer[1024];
snprintf(nameBuffer, sizeof(nameBuffer), "preocess - %d [pid(%d) - fd(%d)]", _num++, _subId, _writeFd);
_name=nameBuffer;
    }
public:
staticint_num;
string_name;
pid_t_subId;
int_writeFd;
};
intsubEP::_num=0;
intrecvTask(intreadFd)
{
intcode=0;
ssize_ts=read(readFd, &code, sizeofcode);
if(s==sizeof(code))//读取正常    {
returncode;
    }
elseif(s<=0)//读取出错    {
return-1;
    }
else    {
return0;
    }
}
voidcreateSubProcess(vector<subEP>*subs, vector<func_t>&funcMap)
{
//vector<int> deleteFd;//第一种方法:解决下一个子进程拷贝父进程读写端的问题for(inti=0; i<PROCESS_SUM; i++)
    {
intfds[2];
intn=pipe(fds);
assert(n==0);
        (void)n;
pid_tid=fork();
//子进程if(id==0)
        {
// for(int i = 0; i < deleteFd.size(); i++)//     close(deleteFd[i]);close(fds[1]);
while(true)
            {
//1.获取父进程发送的命令码,没有收到命令码,进行阻塞等待intcommandCode=recvTask(fds[0]);
//2.执行任务if(commandCode>=0&&commandCode<funcMap.size())
                {
funcMap[commandCode]();
                }
elseif(commandCode==-1)//读取失败返回-1                {
break;
                }
            }
//子进程退出exit(0);
        }
//父进程close(fds[0]);
subEPsub(id, fds[1]);
subs->push_back(sub);////deleteFd.push_back(fds[1]);    }
}
voidsendTask(constsubEP&process, inttaskNum)
{
cout<<"send tak num: "<<taskNum<<" send to -> "<<process._name<<endl;
intn=write(process._writeFd, &taskNum, sizeof(taskNum));
assert(n==sizeof(int));
    (void)n;
}
voidloadBlanceContrl(vector<subEP>&subs,  vector<func_t>&funcMap, intcount)
{
intprocessSum=subs.size();
inttaskSum=funcMap.size();
boolforever= (count==0?true : false);
while(true)
    {
// 1. 随机选择一个子进程intsubIdx=rand() %processSum;
// 2. 随机选择一个任务inttaskIdx=rand() %taskSum;
// 3. 任务发送给选择的进程sendTask(subs[subIdx], taskIdx);
sleep(1);
if(!forever)
        {
count--;
if(count==0)
break;
        }
    }
//第二种方法:解决下一个子进程拷贝父进程读写端的问题//写端退出,关闭读for(inti=0; i<processSum; i++)
    {
close(subs[i]._writeFd);
    }
}
voidwaitProcess(vector<subEP>process)
{
intprocessSum=process.size();
for(inti=0; i<processSum; i++)
    {
waitpid(process[i]._subId, nullptr, 0);
cout<<"wait sub process success ..."<<process[i]._subId<<endl;
    }
}
intmain()
{
//创建随机数makeSeed();
// 1.建立子进程并建立和子进程通信的信道// 1.1 加载方法任务表vector<func_t>funcMap;
loadTaskFunc(&funcMap);
// 1.2 创建子进程,并且维护父子通信信道vector<subEP>subs;
createSubProcess(&subs, funcMap);
// 2.父进程,控制子进程,负载均衡的向子进程发送命令码inttaskCnt=5;//执行任务次数,为0时永远执行任务loadBlanceContrl(subs, funcMap, taskCnt);
// 3.回收子进程waitProcess(subs);
return0;
}

运行结果

image.png

小提示:以 .cpp .cxx .cc 结尾的都是C++的源文件

2.3 命名管道

       匿名管道只能用于具有共同祖先的进程(具有亲缘关系的进程)之间的通信,通常,一个管道由一个进程创建,然后该进程调用fork创建子进程,父子进程通过匿名管道进行通信。如果要实现两个毫不相关进程之间的通信,可以使用命名管道来做到

2.3.1 使用命令创建命名管道

使用 mkfifo 命令创建一个命名管道

image.png


mkfifo文件名ps: mkfifonamed_pipe


可以看到,创建出来的文件的类型是 p ,代表该文件是命名管道文件

image.png

命名管道也有自己的 inode,说明命名管道就是一个独立的文件

image.png

使用这个命名管道文件,就能实现两个进程之间的通信了。我们在一个进程(进程A)中用 shell脚本每秒向命名管道写入一个字符串,在另一个进程(进程B)当中用 cat命令从命名管道当中进行读取

现象:当进程A启动后,进程B会每秒从命名管道中读取一个字符串打印到显示器上

这就证明了这两个毫不相关的进程可以通过命名管道进行数据传输,即通信

先测试往显示器上打印(shell脚本语言)

cnt=0; while :; do echo "hello world -> $cnt"; let cnt++; sleep 2; done

运行结果

b83e6bc38f2041e48c830208205cfe83.gif

输出重定向到管道里

cnt=0; while :; doecho"hello world -> $cnt"; letcnt++; sleep2; done>named_pipe

注:脚本语言是一个进程,cat也是一个进程,两个进程毫无关系

cat 进行输入重定向 ,向管道 named_pipe 读取数据

cat < named_pipe

运行结果

9c319a4c0d5242f5b4cd19354d35f38d.gif

之前我们说过,当管道的读端进程退出后,写端进程再向管道写入数据就没有意义了,此时写端进程会被操作系统杀掉,在这里就可以很好的得到验证:当我们终止掉读端进程后,因为写端执行的循环脚本是由命令行解释器bash执行的,所以此时 bash 就会被操作系统杀掉,我们的云服务器也就退出了

image.png

注意:命名管道的大小是不会改变的,都为0,因为数据都是在文件缓冲区

2.3.2 命名管道的原理

命令管道用于实现两个毫不相关进程之间的通信

进程间通信的本质就是,让不同的进程看到同一份资源,使用命令管道实现父子进程间通信的原理是:也是让两个父子进程先看到同一份被打开的文件资源,这个文件资源就是我们创建的命名管道

两个毫不相关进程打开了同一个命名管道,此时这两个进程也就看到了同一份资源,进而就可以进行通信了,通信的数据依旧是在文件缓冲区里面,并且不会刷新到磁盘

命名管道可以通过路径+名字标定唯一性,匿名管道是通过地址来标定唯一性的,这个地址没有名字,所以叫匿名管道

2.3.3 在程序中创建命名管道

在程序中创建命名管道使用也是使用 mkfifo,mkfifo 是命令,也是一个函数

man 3 mkfifo 查看一下

image.png

mkfifo函数的函数原型如下:

int mkfifo(const char *pathname, mode_t mode);

解释:

头文件:#include <sys/types.h>#include <sys/stat.h>声明:intmkfifo(constchar*pathname, mode_tmode);
参数:(1)pathnamemkfifo函数的第一个参数是pathname,表示要创建的命名管道文件注意:若pathname以路径的方式给出,则将命名管道文件创建在pathname路径下若pathname以文件名的方式给出,则将命名管道文件默认创建在当前路径下(2)modemkfifo函数的第二个参数是mode,表示创建命名管道文件的默认权限返回值:命名管道创建成功,返回0命名管道创建失败,返回-1,错误码被设置

注意:若想创建出来命名管道文件的权限值不受影响,则需要在创建文件前使用 umask 函数将文件默认掩码设置为0

代码示例:

#include <stdio.h>#include <sys/types.h>#include <sys/stat.h>#define FILE_NAME "named_pipe"intmain()
{
umask(0); //将文件默认掩码设置为0//使用mkfifo创建命名管道文件intn=mkfifo(FILE_NAME, 0666);
if (n<0)
    { 
perror("mkfifo");
return-1;
    }
return0;
}

运行结果

image.png

2.3.4 unlink函数

上面的程序再次运行就会报错

image.png

这是因为 mkfifo 函数创建管道是,如果管道已经存在,就不会创建,直接报错:文件已经存在

如果我们想让程序运行结束,创建的管道也被删除,就要使用 unlink函数

man 3 unlink 查看一下

image.png

unlink头文件:#include <unistd.h>函数声明:intunlink(constchar*path);
参数:传入要被删除文件的名字返回值:删除成功返回0失败返回-1,错误码被设置
#include <stdio.h>#include <unistd.h>#include <sys/types.h>#include <sys/stat.h>#define FILE_NAME "named_pipe"intmain()
{
umask(0); //将文件默认掩码设置为0//使用mkfifo创建命名管道文件intn=mkfifo(FILE_NAME, 0666);
if (n<0)
    { 
perror("mkfifo");
return-1;
    }
//删除管道文件n=unlink(FILE_NAME);
if(n<0)
    {
perror("unlink");
return-1;
    }
else    {
printf("管道文件删除成功\n");
    }
return0;
}

运行结果

image.png

小提示:assert不用乱使用,意料之中使用assert,意料之外使用if判断

2.3.5 使用命名管道实现serve&client通信

实现服务端(server)和客户端(client)之间的通信之前,我们需要先让服务端运行起来,我们需要让服务端运行后创建一个命名管道文件,然后再以读的方式打开该命名管道文件,之后服务端就可以从该命名管道当中读取客户端发来的通信信息了

共同的头文件:comm.hpp

客户端和服务端共用一个头文件

#pragma once#include <iostream>#include <string>#include <sys/types.h>#include <sys/stat.h>#include <unistd.h>#include <fcntl.h>#include <cassert>#include <cstring>usingnamespacestd;
#define NAMED_PIPE "named_pipe"//创建命名管道boolcreateFifo(conststring&path)
{
umask(0);
intn=mkfifo(path.c_str(), 0600);
if(n==0)//创建成功    {
returntrue;
    }
else//创建失败    {
cout<<"errno: "<<"errno string: "<<strerror(errno) <<endl;
returnfalse;
    }
}
//删除命名管道voidremoveFifo(conststring&path)
{
intn=unlink(path.c_str());
assert(n==0);//release下就没有了    (void)n;
}

服务端的代码如下:(server.cc)

#include "comm.hpp"
int main()
{
    //创建命名管道
    bool r = createFifo(NAMED_PIPE);
    assert(r);
    (void)r;
    cout << "server begin" << endl;
    int rfd = open(NAMED_PIPE, O_RDONLY);//打开命名管道,服务端以读方式打开
    if(rfd < 0)
        exit(-1);
    //read
    char buffer[1024];
    while(true)
    {
        ssize_t s = read(rfd, buffer, sizeof(buffer) - 1);
        if(s > 0)//读取正常
        {
            buffer[s] = '\0';
            cout << "client -> server# " << buffer << endl;
        }
        else if(s == 0)//client退出,server也退出
        {
            cout << "client quit, me too!" << endl;
            break;
        }
        else//读取错误
        {
            cout << "error string: " << strerror(errno) << endl;
            break;
        }
    }
    //关闭文件描述符
    close(rfd);
    //程序退出删除命名管道
    removeFifo(NAMED_PIPE);
    cout << "server end" << endl;
    return 0;
}

服务端代码:(client.cc)

#include "comm.hpp"
int main()
{
    cout << "client begin" << endl;
    int wfd = open(NAMED_PIPE, O_WRONLY);//打开命名管道,客户端以写的方式打开
    if(wfd < 0)
        exit(-1);
    //write
    char buffer[1024];
    while(true)
    {
        cout << "Please Say# ";
        fgets(buffer, sizeof(buffer), stdin);//输入信息
        if(strlen(buffer) > 0)
            buffer[strlen(buffer) - 1] = 0;//去掉输入多余的 \n
        ssize_t n = write(wfd, buffer, strlen(buffer));
        assert(n == strlen(buffer));
        (void)n;
    }
    close(wfd);
    cout << "client end" << endl;
    return 0;
}

运行的时候,服务端先运行,然后客户端再运行,客户端不输入数据,服务端会一直阻塞等待

6e4fb0040ed3401ea2ebfe7806084921.gif

2.3.6 匿名管道与命名管道的区别

  1. 匿名管道由pipe函数创建并打开。
  2. 命名管道由mkfifo函数创建,打开用open
  3. FIFO(命名管道)与pipe(匿名管道)之间唯一的区别在它们创建与打开的方式不同,一但这些工作完成之后,它们具有相同的语义

----------------我是分割线---------------

文章暂时到这里就结束了,下一篇即将更新

相关文章
|
2月前
|
资源调度 Linux 调度
Linux c/c++之进程基础
这篇文章主要介绍了Linux下C/C++进程的基本概念、组成、模式、运行和状态,以及如何使用系统调用创建和管理进程。
40 0
|
1天前
|
运维 监控 Linux
Linux操作系统的守护进程与服务管理深度剖析####
本文作为一篇技术性文章,旨在深入探讨Linux操作系统中守护进程与服务管理的机制、工具及实践策略。不同于传统的摘要概述,本文将以“守护进程的生命周期”为核心线索,串联起Linux服务管理的各个方面,从守护进程的定义与特性出发,逐步深入到Systemd的工作原理、服务单元文件编写、服务状态管理以及故障排查技巧,为读者呈现一幅Linux服务管理的全景图。 ####
|
26天前
|
缓存 监控 Linux
linux进程管理万字详解!!!
本文档介绍了Linux系统中进程管理、系统负载监控、内存监控和磁盘监控的基本概念和常用命令。主要内容包括: 1. **进程管理**: - **进程介绍**:程序与进程的关系、进程的生命周期、查看进程号和父进程号的方法。 - **进程监控命令**:`ps`、`pstree`、`pidof`、`top`、`htop`、`lsof`等命令的使用方法和案例。 - **进程管理命令**:控制信号、`kill`、`pkill`、`killall`、前台和后台运行、`screen`、`nohup`等命令的使用方法和案例。
99 4
linux进程管理万字详解!!!
|
17天前
|
存储 运维 监控
深入Linux基础:文件系统与进程管理详解
深入Linux基础:文件系统与进程管理详解
58 8
|
14天前
|
Linux
如何在 Linux 系统中查看进程占用的内存?
如何在 Linux 系统中查看进程占用的内存?
|
26天前
|
算法 Linux 定位技术
Linux内核中的进程调度算法解析####
【10月更文挑战第29天】 本文深入剖析了Linux操作系统的心脏——内核中至关重要的组成部分之一,即进程调度机制。不同于传统的摘要概述,我们将通过一段引人入胜的故事线来揭开进程调度算法的神秘面纱,展现其背后的精妙设计与复杂逻辑,让读者仿佛跟随一位虚拟的“进程侦探”,一步步探索Linux如何高效、公平地管理众多进程,确保系统资源的最优分配与利用。 ####
66 4
|
27天前
|
缓存 负载均衡 算法
Linux内核中的进程调度算法解析####
本文深入探讨了Linux操作系统核心组件之一——进程调度器,着重分析了其采用的CFS(完全公平调度器)算法。不同于传统摘要对研究背景、方法、结果和结论的概述,本文摘要将直接揭示CFS算法的核心优势及其在现代多核处理器环境下如何实现高效、公平的资源分配,同时简要提及该算法如何优化系统响应时间和吞吐量,为读者快速构建对Linux进程调度机制的认知框架。 ####
|
28天前
|
消息中间件 存储 Linux
|
2月前
|
运维 Linux
Linux查找占用的端口,并杀死进程的简单方法
通过上述步骤和命令,您能够迅速识别并根据实际情况管理Linux系统中占用特定端口的进程。为了获得更全面的服务器管理技巧和解决方案,提供了丰富的资源和专业服务,是您提升运维技能的理想选择。
49 1
|
2月前
|
算法 Linux 调度
深入理解Linux操作系统的进程管理
【10月更文挑战第9天】本文将深入浅出地介绍Linux系统中的进程管理机制,包括进程的概念、状态、调度以及如何在Linux环境下进行进程控制。我们将通过直观的语言和生动的比喻,让读者轻松掌握这一核心概念。文章不仅适合初学者构建基础,也能帮助有经验的用户加深对进程管理的理解。
27 1