linux双向通信的步骤,Linux进程间的通信机制

概述

每个进程各自有不同的用户地址空间,任何一个进程的全局变量在另一个进程中都看不到,所以进程之间要交换数据必须通过内核,在内核中开辟一块缓冲区,进程1把数据从用户空间拷到内核缓冲区,进程2再从内核缓冲区把数据读走,内核提供的这种机制称为进程间通信。

431aeca69b07

进程间的通信.png

管道通信

管道是一种最基本的通信机制。

在内核中开辟一块缓冲区(称为管道)用于通信,它有一个读端一个写端,进程1往管道中写数据,进程2读管道中的数据,这样就实现了进程间的通信。对用户程序来说,管道就像一个文件。

创建管道的函数:

#include

int pipe(int filedes[2]);

调用pipe函数时在内核中开辟一块缓冲区(称为管道)用于通信,它有一个读端一个写端,然后通过filedes参数传出给用户程序两个文件描述符,filedes[0]指向管道的读端,filedes[1]指向管道的写端(很好记,就像0是标准输入1是标准输出一样)。所以管道在用户程序看起来就像一个打开的文件,通过read(filedes[0]);或者write(filedes[1]);向这个文件读写数据其实是在读写内核缓冲区。pipe函数调用成功返回0,调用失败返回-1。

管道通信的过程:

父进程创建管道;

父进程fork出子进程;

父进程关闭fileds[0],子进程关闭fileds[1];

管道是用队列实现的,由于是队列,故在两个进程间通信只能由一端到另一端,如果要双向通信则需创建两个管道。由于是通过文件描述符来指向管道的,那么只能在父子进程间通信。

写个简单的例子

#include

#include

int main(void)

{

int fd[2];

int n;

pid_t pid;

char line[80];

pipe(fd);

pid = fork();

if(pid > 0){

close(fd[0]);

write(fd[1], "hello world\n", 12);

waitpid(pid,NULL,0);

}else if(pid == 0){

close(fd[1]);

n = read(fd[0], line, 80);

printf("msg:%s", line);

}

return 0;

}

431aeca69b07

运行截图.png

其他通信机制-FIFO

用命令行来测试:

先创建一个FIFO文件

mkfifo test

然后读取FIFO的内容,此时因为FIFO为空,所以会挂起

cat < ./test

打开另一个终端往test中写入信息

echo "just a test" > ./test

这时你会发现第一个终端会输出"just a test"并且退出。

用程序测试:

(1)和命令行一样采取阻塞式,代码如下

写FIFO

//write.c

#include

#include

#include

#include

#include

#include

#include

#define FIFO_FILE "./myfifo"

int main()

{

int fd = 0;

int n;

int re;

char buf[15] = "hello world\n";

unlink( FIFO_FILE );

re = mkfifo( FIFO_FILE, 0777 );

fd = open(FIFO_FILE, O_WRONLY);

if(fd >= 0){

n = write(fd, buf, 15);

close(fd);

printf("%d writed\n", n);

}else{

perror("open error");

exit(1);

}

return 0;

}

读FIFO

//read.c

#include

#include

#include

#include

#include

#include

#include

#define FIFO_FILE "./myfifo"

int main()

{

char buf[80];

int n = 0;

int fd;

fd = open(FIFO_FILE, O_RDONLY);

if(fd < 0){

perror("open error");

exit(-1);

}

n = read(fd, buf, 80);

close(fd);

printf("n = %d\n", n);

printf("msg:%s\n", buf);

return 0;

}

运行

./write &

./read &

编译运行截图

431aeca69b07

编译运行阻塞式FIFO

(2)非阻塞式

代码稍微修改一下

写FIFO

#include

#include

#include

#include

#include

#include

#include

#define FIFO_FILE "/home/lkxiaolou/c/fifo/myfifo"

int main()

{

int fd = 0;

int n;

int re;

char buf[15] = "hello world\n";

fd = open(FIFO_FILE, O_WRONLY|O_NONBLOCK);

if(fd >= 0){

n = write(fd, buf, 15);

close(fd);

printf("%d writed\n", n);

}else{

perror("open error");

exit(1);

}

return 0;

}

读FIFO

#include

#include

#include

#include

#include

#include

#include

#define FIFO_FILE "/home/lkxiaolou/c/fifo/myfifo"

int main()

{

char buf[80];

int n = 0;

int fd;

int re;

unlink( FIFO_FILE );

re = mkfifo( FIFO_FILE, 0777 );

fd = open(FIFO_FILE, O_RDONLY | O_NONBLOCK);

if(fd < 0){

perror("open error");

exit(-1);

}

while(n == 0){

n = read(fd, buf, 80);

sleep(1);

}

close(fd);

printf("n = %d\n", n);

printf("msg:%s\n", buf);

return 0;

}

编译运行结果

431aeca69b07

编译运行非阻塞式FIFO

其他通信机制-UNIX Domain Socket

socket API原本是为网络通讯设计的,但后来在socket的框架上发展出一种IPC机制,就是UNIX Domain Socket。UNIX Domain Socket是全双工的,API接口语义丰富,相比其它IPC机制有明显的优越性,目前已成为使用最广泛的IPC机制

由于是全双工,则可以互相通信,不存在读写端,这里称为服务端和客户端。

服务端工作流程:

socket 建立一个socket

bind 将这个socket绑定在文件(端口)上

listen 开始监听

accept 如果客户端连接,则接受并建立一个新的socket来和客户端通信

read/write 读取或者发送消息

close 关闭连接

客户端工作流程:

socket 建立一个socket

connect 主动连接服务端的文件(端口)

read/write 接收或者发送消息

close 关闭连接

找了一个例子,稍微修改了一下:

server代码:

#include

#include

#include

#include

#include

#include

#include

// the max connection number of the server

#define MAX_CONNECTION_NUMBER 5

/* * Create a server endpoint of a connection. * Returns fd if all OK, <0 on error. */

int unix_socket_listen(const char *servername)

{

int fd;

struct sockaddr_un un;

if ((fd = socket(AF_UNIX, SOCK_STREAM, 0)) < 0){

return(-1);

}

int len, rval;

unlink(servername); /* in case it already exists */

memset(&un, 0, sizeof(un));

un.sun_family = AF_UNIX;

strcpy(un.sun_path, servername);

len = offsetof(struct sockaddr_un, sun_path) + strlen(servername);

/* bind the name to the descriptor */

if (bind(fd, (struct sockaddr *)&un, len) < 0){

rval = -2;

} else{

if (listen(fd, MAX_CONNECTION_NUMBER) < 0){

rval = -3;

}else{

return fd;

}

}

int err;

err = errno;

close(fd);

errno = err;

return rval;

}

int unix_socket_accept(int listenfd, uid_t *uidptr)

{

int clifd, len, rval;

time_t staletime;

struct sockaddr_un un;

struct stat statbuf;

len = sizeof(un);

if ((clifd = accept(listenfd, (struct sockaddr *)&un, &len)) < 0){

return(-1);

}

/* obtain the client's uid from its calling address */

len -= offsetof(struct sockaddr_un, sun_path); /* len of pathname */

un.sun_path[len] = 0; /* null terminate */

if (stat(un.sun_path, &statbuf) < 0){

rval = -2;

}else{

if (S_ISSOCK(statbuf.st_mode) ){

if (uidptr != NULL) *uidptr = statbuf.st_uid; /* return uid of caller */

unlink(un.sun_path); /* we're done with pathname now */

return clifd;

}else{

rval = -3; /* not a socket */

}

}

int err;

err = errno;

close(clifd);

errno = err;

return(rval);

}

void unix_socket_close(int fd)

{

close(fd);

}

int main(void)

{

int listenfd,connfd;

listenfd = unix_socket_listen("foo.sock");

if(listenfd<0){

printf("Error[%d] when listening...\n",errno);

return 0;

}

printf("Finished listening...\n",errno);

uid_t uid;

connfd = unix_socket_accept(listenfd, &uid);

unix_socket_close(listenfd);

if(connfd<0){

printf("Error[%d] when accepting...\n",errno);

return 0;

}

printf("Begin to recv/send...\n");

int i,n;

int size = 0;

char rvbuf[2048];

while(size == 0)

{

//===========接收==============

size = recv(connfd, rvbuf, 12, 0);

if(size>0){

printf("Recieved Data[%d]:%s\n",size,rvbuf);

}

if(size==-1){

printf("Error[%d] when recieving Data:%s.\n",errno,strerror(errno));

}

}

unix_socket_close(connfd);

printf("Server exited.\n");

}

client代码:

#include

#include

#include

#include

#include

#include

#include

/* Create a client endpoint and connect to a server. Returns fd if all OK, <0 on error. */

int unix_socket_conn(const char *servername)

{

int fd;

if ((fd = socket(AF_UNIX, SOCK_STREAM, 0)) < 0){ /* create a UNIX domain stream socket */

return(-1);

}

int len, rval;

struct sockaddr_un un;

memset(&un, 0, sizeof(un)); /* fill socket address structure with our address */

un.sun_family = AF_UNIX;

sprintf(un.sun_path, "scktmp%05d", getpid());

len = offsetof(struct sockaddr_un, sun_path) + strlen(un.sun_path);

unlink(un.sun_path); /* in case it already exists */

if (bind(fd, (struct sockaddr *)&un, len) < 0){

rval= -2;

}else{

/* fill socket address structure with server's address */

memset(&un, 0, sizeof(un));

un.sun_family = AF_UNIX;

strcpy(un.sun_path, servername);

len = offsetof(struct sockaddr_un, sun_path) + strlen(servername);

if (connect(fd, (struct sockaddr *)&un, len) < 0) {

rval= -4;

}else{

return (fd);

}

}

int err;

err = errno;

close(fd);

errno = err;

return rval;

}

void unix_socket_close(int fd)

{

close(fd);

}

int main(void)

{

srand((int)time(0));

int connfd;

connfd = unix_socket_conn("foo.sock");

if(connfd<0){

printf("Error[%d] when connecting...",errno);

return 0;

}

printf("Begin to recv/send...\n");

int i,n,size;

//=========发送======================

char rvbuf[12]="hello world";

size = send(connfd, rvbuf, 12, 0);

if(size>=0){

printf("Data[%d] Sended:%s\n",size,rvbuf);

}

if(size==-1){

printf("Error[%d] when Sending Data:%s.\n",errno,strerror(errno));

}

unix_socket_close(connfd);

printf("Client exited.\n");

}

编译运行截图:

431aeca69b07

unix domain socket运行截图

好了,进程通信就写到这吧,累死我了--!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值