Linux进程间通讯

Linux 进程间通讯方式:


1.同主机进程间数据交互机制:无名管道(PIPE),有名管道(FIFO),消息队列(Message Queue),共享内存(Share Memory);

2.同主机进程间同步机制:信号量(Semaphore);

3.同主机进程间异步机制:信号(Signal);

4.网络主机数据交互机制:接套口(Socket);


无名管道的管理和应用

#include <unistd.h>



extern int pipe(int __pipe[2]) __THROW __wur
/*
参数:__pipe是一个整型数组,pipe将存储两个两个文件描述符于__pipe数组中,分别指向管道的两边。
如果系统调用失败则返回-1
1.无名管道只存在于进程通讯期间,通讯完成后自动消失,而且只存放临时文件
2.不能和常规文件一样存储大量信息,但是使用read/write函数读写
3.不能使用lseek函数修订读写位置
*/


extern ssize_t read(int __fd, void *__buf, size_t __nbytes)
/*
#define ssize_t int
typedef __kernel_size_t size_t;
typedef unsigned int __kernel_size_t;
如果读一个空管道将会阻塞
*/
extern ssize_t write(int __fd, __const void *__buf, size_t __n)
/*
每次读写建议小于等于系统的PIPE__BUF(默认为4096),以避免交叉写入
//come from /usr/include/limit.h
#define PIPE_BUF 4096
如果O_NONBLOCK和O_NDELAY标示被清除是出现设备忙的情况,write请求将会导致进程阻塞
*/


文件描述符重定向
1. cat<test01
2. cat>test02<test01
3. cat>test02 2>error<test01
4. cat>test02 1&2<test01
5. cat 1&2 1>test02<test01 #联合是不具有传递性的


复制文件描述符


#include <unistd.h>


extern int dup(int __fd)
/*
Duplicate FD, returning a new file descriptor on the same file
复制一份原来已经打开的文件描述符,新的描述符指向系统文件表中的下一个可用的最小文件描述符
与原来的文件描述符共享一个文件指针,并有相同的文件权限和模式
下列语句重定向输出至管道:
int f_des[2];
pipe(f_des);
close(fileno(stdout)); //关闭标准输出设备
dup(f_des[1]);//返回一个最低可用文件描述符,也就是关闭的标准输出设备
重定向实际上是关闭标准输入输出,同时将另外一个打开的文件的描述符设置为0,1,2
*/


extern int dup2(int __fd, int __fd2);
/*
Duplicate FD to FD2, closing FD2 and making it open on the same file.
fd2是小于文件描述最大允许的非负整数,如果fd2已经打开,则首先关闭该文件,在复制。
返回文件描述符df2
*/


使用dup2重定向标准输出
#include <unistd.h>
#include <stdio.h>
#include <errno.h>
#include <fcntl.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <stdlib.h>


#define BUFFER_SIZE 1024
int main(int argc, char *argv[])
{
int fd;
char buffer[BUFFER_SIZE];
if(argc != 2)
{
fprintf(stderr,"Usage:%s outfilename\n\a", argv[0]);
exit(EXIT_FAILURE);
}
if( (fd=open(agrv[1], O_WRONLY|O_CREAT|O_TRUNC,S_IRUSR|S_IWUSR)) == -1 )
{
fprintf(stderr,"Open file Error:%s\n\a", strerror(errno));
exit(EXIT_FAILURE);
}
if(dup2(fd, fileno(stdout)) == -1) // 关闭stdout,并复制fd为fileno(stdout)
{
fprintf(stderr,"Redirect stdout Error:%s\n\a", strerror(errno));
exit(EXIT_FAILURE);
}
fprintf(stderr, "Now please input string!\n");
fprintf(stderr, "(To quit use CTRL+D)\n");
while(1)
{
fgets(buffer, BUFFER_SIZE,stdin);
if(feof(stdin))
break;
write(file(stdout), buffer, strlen(buffer));
}
exit(EXIT_SUCCESS);
}
实现who|sort


#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/wait.h>
#include <stdlib.h>


int main(int argc, char *argv[])
{
int fds[2];
if(pipe(fds) == -1)
{
perror("Pipe");
exit(EXIT_FAILRUE);
}
if(fork() == 0)
{
char buf[128];
dup2(fds[0],0);
close(fds[1]);// must include, or block
execlp("sort", "sort", (char *)0);// sort command
}
else
{
if(fork() == 0)
{
dup2(fds[1], 1);
close(fds[0]);
execlp("who", "who", (char *) 0);
}
else
{
close(fds[0]);
close(fds[1]);
wait(NULL);
wait(NULL);
}
}
return 0;
}


流重定向
#if (defined __USE_POSIX2 || defined __USE_SVID || defined __USE_BSD || defined __USE_MISC)
/*Create a new stream connected to a pipe running the given command*/
extern FILE *popen(__const char *__command, __const char *__modes);
/*Close a stream opened by popen and return the status of its child*/
extern int pclose(FILE *__stream);


实现echo test | cat


#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <limits.h>
#include <string.h>


int main(int argc, char *argv[])
{
FILE *finput, foutput;
char buffer[PIPE_BUF];
int n;
finput = popen("echo test!","r");
foutput = popen("cat", "w");
read(fileno(finput), buffer, strlen("test!"));
write(fileno(foutput), buffer, strlen("test!"));
pclose(finput);
pclose(foutput);
printf("\n");
exit(EXIT_SUCCESS);
}


有名管道管理以及应用
亲缘关系的进程使用有名管道通讯
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#define FIFO "/tmp/fifo"


int main(void)
{
pid_t pid;
char buffer[80];
int fd;
unlink(FIFO);
/*Create a new FIFO PATH. with permission bits MODE
  come from /usr/include/sys/stat.h
extern int mkfifo(__const char *__path, __mode_t __mode);
如果打开管道的写端,则需要另外一个进程打开读端,否则系统将会暂时阻塞打开进程,直到另外的进程打开管道的另外一端。
在使用FIFO时,一定要使用两个进程分别打开读端和写端
*/
mkfifo(FIFO,0774);
if((pid = fork()) > 0)
{
/*父进程写数据*/
char s[] = "hello";
fd = open(FIFO, O_WRONLY);
printf("this is father write data us %s\n",s);
printf("father's pid is %d\n", getpid());
write(fd, s, sizeof(s));
close(fd);
exit(EXIT_SUCCESS);
}else if( pid == 0 )
{
/*子进程读数据*/
sleep(2);
fd = open(FIFO,O_RDONLY);
read(fd, buffer, 80);
printf("this is child read data is %s\n", buffer);
close(fd);
printf("child's pid is %d\n", getpid());
exit(EXIT_SUCCESS);
}else
{
perror("fork");
exit(EXIT_FAILURE);
}

return 0;
}
非亲缘关系进程使用有名管道
发送数据
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <limits.h>
#include <sys/types.h>
#include <sys/stat.h>


#define FIFO_NAME "/tmp/my_fifo"


int main(int argc, char *argv[])
{
int pipe_fd;
int res;
char buffer[] = "hello world!";
if(access(FIFO_NAME, F_OK) == -1)
{
res = mkfifo(FIFO_NAME, 0766);
if( res != 0)
{
fprintf(stderr, "Could not create fifo %s\n", FIFO_NAME);
exit(EXIT_FAILURE);
}
}
printf("Process %d opening FIFO O_WRONLY\n", getpid());
pipe_fd = open(FIFO_NAME, O_WRONLY);
printf("the file's desription is %d \n", pipe_fd);
if(pipe_fd != -1)
{
res = write(pipe_fd, buffer, sizeof(buffer));
if(res == -1)
{
fprintf(stderr, "Write error on pipe.\n");
exit(EXIT_FAILURE);
}
printf("write data is %s, %d bytes in write\n", buffer, res);
(void) close(pipe_fd);
}
else
exit(EXIT_FAILURE);


printf("Process %d finished\n", getpid());
exit(EXIT_SUCCESS);
return 0;
}
读取数据
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <limits.h>
#include <sys/types.h>
#include <sys/stat.h>


#define FIFO_NAME "/tmp/my_fifo"


int main(int argc, char *argv[])
{
int pipe_fd;
char buffer[4096];
int bytes_read = 0;
memset(buffer,'\0',sizeof(buffer));
printf("Process %d opening FIFO O_RDONLY\n", getpid());
pipe_fd = open(FIFO_NAME, O_RDONLY);
printf("the file's descriptor is %d\n", pipe_fd);
if(pipe_fd != -1)
{
bytes_read = read(pipe_fd, buffer, sizeof(buffer));
printf("the read data is %s\n", buffer);
close(pipe_fd);
}
else
{
exit(EXIT_FAILURE);
}
printf("Process %d finished, %d bytes read\n", getpid(), bytes_read);
exit(EXIT_SUCCESS);
}

管道的特点:

1.特殊类型的文件,满足先进先出的原则,无法定位读写;

2.单向,无名管道只能实现亲缘关系进程间的通讯;

3.无名管道创建是无需显示打开,如果当前进程向无名管道写数据时,必须确定另一端为某个进程所有,即有一个(多个)进程的文件描述符表中至少有一个成员指向管道的另外一端,如果写入数据超过最大值将阻塞

4.以读方式打开有名管道的前提:已经有一个进程以写的方式打开管道,否则将阻塞,知道条件满足。也可以用读写方式(O_RDWR)方式打开有名管道,进程能够继续执行,即当前进程写,当前进程读。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值