进程间通信
一、进程间通信简介
1.进程间是相互独立的,每个进程都有自己的进程虚拟地址空间,二进程通讯需要介子,使得两个进程都能访问的公共资源;
2.进程间通讯的目的:
- 数据传输:一个进程需要将他的数据发送给另一个进程
- 资源共享:多个进程间共享同样的资源
- 通知事件:一个进程需要向另一个或一组进程发送消息,通知它们发生了某种事件(如子进程退出时要通知父进程回收其资源等)
- 进程控制:有些进程希望完全控制另一个进程的执行,此时控制进程希望能够拦截另一个进程所有陷入和异常,并能及时的知道他的改变状态
二、进程间通讯的发展及分类:
1.管道
- 匿名管道pipe
- 命名管道
2.System V进程间通讯
- System V消息共享队列
- System V共享内存
- System V信号量
3.POSIX进程间通讯
- 消息队列
- 共享内存
- 信号量
- 互斥量
- 条件变量
- 读写锁
三、管道
1.什么是管道:管道是内核中的一块内存,构成一个队列,使用一对文件描述符来进行访问管理这个内存,读文件描述符相当于从这个内存中取数据,写文件描述符相当于往这块内存中写数据
2.匿名管道
匿名管道的创建
int pipe(int pipefd[2]);
功能:是创建一个无名管道;
参数:是两个输出型参数,fd表示文件描述符数组,其中fd[0]端表示读端,fd[1]端表示写端。
返回值:创建成功返回0;失败返回错误码。
测试代码:
例子:从键盘读取数据,写入管道,读取管道,写到屏幕
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
int main( void )
{
int fds[2];
char buf[100];
int len;
if ( pipe(fds) == -1 )
perror("make pipe"),exit(1);
// read from stdin
while ( fgets(buf, 100, stdin) ) {
len = strlen(buf);
// write into pipe
if ( write(fds[1], buf, len) != len ) {
perror("write to pipe");
break;
}
memset(buf, 0x00, sizeof(buf));
// read from pipe
if ( (len=read(fds[0], buf, 100)) == -1 ) {
perror("read from pipe");
break;
}
// write to stdout
if ( write(1, buf, len) != len ) {
perror("write to stdout");
break;
}
}
3.用fork来共享管道
站在文件描述符的角度:
站在内核角度:
共享管道实用的注意事项:
-
管道使用完后一定要及时关闭文件描述符,防止资源泄漏
-
创建子进程时,子进程会进程父进程的文件描述符表,此时子进程也就能访问同一个管道
-
如果父子进程同时读取管道的内容,只有其中一个能够成功读取到管道中的数据,一旦有一个进程成功读取到管道中的内容,那么该数据就不在管道中了,相当于把数据从管道中出队了。
-
管道内置了“同步互斥机制”,不会出现两个进程一人读取管道一般数据的情况。其中有如下几种情况:
a.多个进程同时去读写管道,管道数据不会错乱(内置同步互斥)
b.如果管道为空,再去尝试读取管道数据,此时程序就会在read处阻塞。
c.如果管道已满,再去尝试往管道中写数据,此时程序就会在write处阻塞;
四、minishell
#include <stdio.h>
#include <sys/wait.h>
#include <ctype.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>
char g_command[1024];
static char* pipe_command[] = {0};
//该函数的功能是获取命令行输入的数据
int GetCommand()
{
//因为开始获得的内存中的内容是不定的,所以使用前要先初始化
memset(g_command,'\0',sizeof(g_command));
//这里需要为'\0'留一个位置,不能把g_command全部用来读取内容,否者就没有结束标志,容易发生内存访问越界
if(fgets(g_command,sizeof(g_command)-1,stdin)==NULL)
{
printf("fgets is error!\n");
return -1;
}
//printf("g_command:%s\n",g_command);
return 0;
}
//解析字符串
char** DealCommand(char* command)
{
if(command==NULL&&*command=='\0')
{
printf("dealcommand is error\n");
return NULL;
}
//用来保存命令
static char* argv[1024]={0};
int argc=0;
while(*command)
{
//isspace函数用来去掉多余的空格
//isspace的返回值:如果当前字符是空格返回1,否则返回0
//注意'\0'在issapce函数中不算空格的,所以要进行判断
while(!isspace(*command)&&*command!='\0')
{
argv[argc]=command;
argc++;
//去找下一个空格
while(!isspace(*command)&&*command!='\0')
{
command++;
}
*command='\0';
}
command++;
}
argv[argc]=NULL;
//for(int i=0;i<argc;i++)
//{
// printf("%d:%s ",i,argv[i]);
//}
//printf("\n");
return argv;
}
//进行重定向
int redirect(char * g_command)
{
char* ptr = g_command;
char* file = NULL;
int fd ;
//用来标记是清空重定向还是追加重定向
int redirect_type = -1;
while(*ptr !='\0')
{
//如果当前字符是 > ,把他置为'\0',并判断下一个位置是否为'\0'
if(*ptr == '>')
{
*ptr++ = '\0';
redirect_type++;
if(*ptr == '>')
{
*ptr++ = '\0';
redirect_type++;
}
//去掉多余的空格
while(isspace(*ptr))
{
ptr++;
}
//file就是空格后面的第一个字符串
file = ptr;
//继续找空格,在这两个空格之间就是文件的名称
while(!isspace(*ptr)&&*ptr != '\0')
{
ptr++;
}
*ptr='\0';
//如果redirect_type==0说明是清空重定向,如果==1说明是追加重新定向
if(redirect_type == 0)
{
fd = open(file,O_CREAT|O_TRUNC|O_WRONLY,0664);
}
else
{
fd = open(file,O_CREAT|O_APPEND|O_WRONLY,0664);
}
dup2(fd,1);
}
ptr++;
}
return 0;
}
//解析字符串,获取管道的数量
int do_command(char * g_command)
{
int pipe_num = 0;
char * ptr =g_command;
pipe_command[pipe_num]=ptr;
while(*ptr != '\0')
{
if(*ptr == '|')
{
pipe_num++;
*ptr++ = '\0';
pipe_command[pipe_num] = ptr;
continue;
}
ptr++;
}
pipe_command[pipe_num + 1] = NULL;
return pipe_num;
}
//进行程序替换
int exec()
{
redirect(g_command);
char** argv=DealCommand(g_command);
pid_t pid =fork();
if(pid<0)
{
printf("foek is error!\n");
return -1;
}
else if(pid==0)
{
//child
//如果argc中为NULL,就直接返回
if(argv[0]==NULL)
{
exit(-1);
}
//进行替换,execvp第一个参数是可执行程序名,第二个参数是该可执行程序的参数组成的数组
execvp(argv[0],argv);
//execl("/usr/bin/ls","ls","-a",NULL);
}
else
{
//father
waitpid(pid,NULL,0);
}
return 0;
}
//添加管道功能
int do_pipe(int pipe_num)
{
int pid = 0;
int i;
int pipefd[10][2] = {{0}};
char** argv = {NULL};
for (i = 0;i <= pipe_num;i++)
{
pipe(pipefd[i]);
}
for(i = 0;i <= pipe_num; i++)
{
pid = fork();
if(pid < 0)
{
perror("fork is error!\n");
continue;
}
else if(pid == 0)
{
redirect(pipe_command[i]);
argv = DealCommand(pipe_command[i]);
if(i != 0)
{
close(pipefd[i][1]);
dup2(pipefd[i][0],0);
}
if(i != pipe_num)
{
close(pipefd[i+1][0]);
dup2(pipefd[i+1][1],1);
}
execvp(argv[0],argv);
}
else
{
close(pipefd[i][0]);
close(pipefd[i][1]);
waitpid(pid,NULL,0);
}
}
return 0;
}
int main()
{
//循环读数据
while(1)
{
printf("[dev@localhost dev]$ ");
int ret = GetCommand();
if(ret == -1)
{
//如果读取失败,继续循环读取,不能直接break调
continue;
}
//处理解析数据
//char** argv = DealCommand(g_command);
//进行替换
//exec(argv);
//exec();
int num = do_command(g_command);
do_pipe(num);
}
return 0;
}
来源:CSDN
作者:wolf鬼刀
链接:https://blog.csdn.net/wolfGuiDao/article/details/103866181