进程间通信1

时间:2020-12-19 19:02:53

管道通信
管道是单向、先进先出的,他把一个进程的输出和另一个进程的输入连在一起。

两个程序之间传递数据的一种简单方法是使用popen和pclose。

#include <stdio.h>
FILE *popen(const char *command, const char *type);
int pclose(FILE *stream);

popen函数允许一个程序将另一个程序作为新进程来启动,并可以传递数据给它或者通过它接收数据。command字符串是要运行的程序名和相应的参数。type必须是”r”或”w”。
如果type是”r”,被调程序的输出就可以被调用程序使用,调用程序利用popen函数返回的FILE *文件流指针,可以读取被调程序的输出;如果type是”w”,调用程序就可以向被调程序发送数据,而被调程序可以在自己的标准输入上读取这些数据。
pclose函数只在popen启动的进程结束后才返回。如果调用pclose时它仍在运行,pclose将等待该进程的结束。

管道类型
无名管道:用于父子进程之间的通信。

无名管道由pipe()函数创建:
int pipe(int filedis[2]);
当一个管道建立时,它会创建两个文件描述符:
filedis[0]用于读管道,filedis[1]用于写管道。
顺序不能颠倒。

//代码例子
#include <unistd.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>

int main()
{
int pipe_fd[2];

if (pipe(pipe_fd) < 0)
{
perror("pipe");
return -1;
}
else
{
printf ("pipe create success\n");
}

close(pipe_fd[0]);
close(pipe_fd[1]);

return 0;
}

管道用于不同进程间通信,通常先创建一个管道,再用fork()函数创建一个子进程,该子进程会继承父进程所创建的管道描述符。必须在fork()前调用pipe(),否则子进程将不会继承文件描述符

#include <stdio.h>
#include <unistd.h>

#define SIZE 1024*100

int main()
{
int fd[2];

int ret = pipe(fd);
if (ret == -1)
{
perror ("pipe");
return -1;
}

ret = write (fd[1], "hello", 5);

printf ("写入 %d 个字节\n", ret);
char ch;
while (1)
{
//如果管道里面没有数据可读,read会阻塞
ret = read (fd[0], &ch, 1);
if (ret == -1)
{
perror ("read");
return -1;
}

printf ("读到 %d 个字节: %c\n", ret, ch);
}

close (fd[0]);
close (fd[1]);

return 0;
}

命名管道:又称有名管道,它和无名管道最大的区别就是社交范围广,可以让同一系统下不相关的进程之间也能交换数据。

#include <sys/types.h>
#include <sys/stat.h>

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

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

pathname: FIFO文件名
mode:属性(同文件操作)

一旦创建了一个FIFO,就可用open打开它,一般的文件访问函数(close、read、write等)都可用于FIFO。
当打开FIFO时,非阻塞标识(O_NONBLOCK)将对以后的读写产生影响:
1、没有使用O_NONBLOCK:访问要求无法满足时进程将阻塞。如果试图读取空的FIFO,将导致进程阻塞。
2、使用O_NONBLOCK:访问要求无法满足时不阻塞,立刻出错返回。errno是ENXIO。

共享内存
共享内存实现分两个步骤:
1、创建共享内存,使用shmget函数
2、映射共享内存,将这段创建的共享内存映射到具体的进程空间去,使用shmat函数。

int shmget(key_t key, int size, int shmflg)
key:
1、0/IPC_PRIVATE:当key的取值为IPC_PRIVATE,则函数shmget()将创建一块新的共享内存;如果key取值为0,而参数shmflg中又设置IPC_PRIVATE这个标志,则同样会创建一块新的共享内存。
2、大于0的32位整数:视参数shmflg来确定操作。
size:
1、大于0的整数:新建的共享内存大小,以字节为单位0:
2、只获取共享内存时指定为0
shmflg:模式标志参数,使用时需要与IPC对象存取权限(如0600)进行|运算来确定共享内存的存取权限
1、0:取共享内存标识符,若不存在则函数会报错
2、IPC_CREAT:当shmflg&IPC_CREAT为真时,如果内核中不存在键值与key相等的共享内存,则新建一个共享内存;如果存在这样的共享内存,返回此共享内存的标识符
3、IPC_CREAT|IPC_EXCL:如果内核中不存在键值 与key相等的共享内存,则新建一个共享内存;如果存在这样的共享内存则报错
返回值:如果成功,返回共享内存标识符;如果失败,返回-1。


void* shmat(int shmid, char *shmaddr, int flag)
参数:
第一个参数,shm_id是由shmget函数返回的共享内存标识。
第二个参数,shm_addr指定共享内存连接到当前进程中的地址位置,通常为空,表示让系统来选择共享内存的地址。
第三个参数,shm_flg是一组标志位,通常为0。
返回值:
如果成功,则返回共享内存映射到进程中的地址;如果失败,则返回-1。


当一个进程不再需要共享内存时,需要把它从进程地址空间中脱离int shmdt(char *shmaddr);
参数shmaddr是shmat函数返回的地址指针,调用成功时返回0,失败时返回-1.
该函数用于将共享内存从当前进程中分离。注意,将共享内存分离并不是删除它,只是使该共享内存对当前进程不再可用。


int shmctl(int shm_id, int command, struct shmid_ds *buf);
第一个参数,shm_id是shmget函数返回的共享内存标识符。
第二个参数,command是要采取的操作,它可以取下面的三个值 :
IPC_STAT:把shmid_ds结构中的数据设置为共享内存的当前关联值,即用共享内存的当前关联值覆盖shmid_ds的值。
IPC_SET:如果进程有足够的权限,就把共享内存的当前关联值设置为shmid_ds结构中给出的值
IPC_RMID:删除共享内存段
第三个参数,buf是一个结构指针,它指向共享内存模式和访问权限的结构。
shmid_ds结构至少包括以下成员:
struct shmid_ds
{
uid_t shm_perm.uid;
uid_t shm_perm.gid;
mode_t shm_perm.mode;
};

代码例子

#include <stdio.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/types.h>
#include <string.h>

typedef struct _shm
{
int flag;
char msg[256];
}SHM;

int main()
{
//1.创建或者获取一个共享内存
int shmid = shmget((key_t)1234,sizeof(SHM),0666|IPC_CREAT);
if (shmid == -1)
{
perror("shmget");
return -1;
}

//2.将共享内存映射到当前的进程中
SHM *pshm = (SHM*)shmat(shmid, NULL, 0);
if (pshm == -1)
{
perror ("shmat");
return -1;
}

strcpy (pshm->msg, "hello");

//解除共享内存映射,解除的是当前进程不再使用共享内存
shmdt(pshm);
shmctl(shmid, IPC_RMID, struct NULL);

return 0;
}

消息队列和信号通信下一篇说。