今天还多充实
时间:2006-01-09 来源:晏东
2006年1月10日星期一 阴见多云
一、 进程间通信
a) 双工管道
输入始终是fd[1],输出是fd[0],其他的不用管。因此如果是将数据从父进程发到子进程:
parent process: close(fd[0]);write(fd[1]);
child process:close(fd[1]);read(fd[0]);
即兴练习:
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <string.h>
#include <stdlib.h>
#include <fcntl.h>
int main()
{
int fd[2];
pid_t pid;
char buf[100];
strcpy(buf, "hello child process!\n");
pipe(fd);
if ((pid=fork()) < 0)
exit(-1);
else if (pid == 0)
{
close(fd[1]);
read(fd[0], buf, sizeof(buf));
printf("recv from parent process:%s", buf);
exit(0);
}else
{
close(fd[0]);
write(fd[0], buf, sizeof(buf));
}
exit(0);
}
以上程序示意图为:
规则如下:
1) 如果从fd[0]读数据,读完后read返回0
2) 如果fd[1]关闭,而向起写数据则,产生SIGPIPE,忽略或捕捉此信号产生EPIPE错误。
文件描述符dup2原型:
#include <unistd.h>
int dup2(int oldfd, int newfd);
成功返回新的文件描述符,调用失败返回-1,其他返回错误码。例:
dup2(0, fd[1]);//将标准输入复制到管道的fd[1],同时关闭标准输入0。
快速建立管道popen:
#include <stdio.h>
FILE *popen(const char *cmd, const char *type);
其中:cmd是shell命令,type中只取其第一个字符(r/w)。默认情况下,w说明是向shell输入命令(***|cmd),执行结果到标准输出;r说明是shell的执行结果,然后输出到标准输出。
例:sort的popen+w使用
int main()
{
FILE *pipe_f;
char *strs[5] = {"a", "b", "d", "c", "e"};
pipe_f = popen("sort", "w");
for (int i=0; i<5; i++)
{
fputs(strs[i], pipe_f);
fputs("\n", pipe_f);
}
pclose(pipe_f);
exit(0);
}
sort的popen+r使用:
int main()
{
FILE *pipe_f;
char strs[200][200];
pipe_f = popen("ls|sort", "r");
for(int i=0; i<200; i++)
{
if (fgets(strs[i], 200, pipe_f))
printf("%s", strs[i]);
else break;
}
pclose(pipe_f);
exit(0);
}
以上调用和shell调用完全一样!!
注意:popen操作是自动操作,在linux下PIPE_BUF为4096,在posix下是512,如果读取和写入的字节数在此限制以下,则进行自动操作,否则不行。
在使用过程中,可以建立两个半双工管道,一个用于读,一个用于写。在使用半双工管道时,父进程中建立的pipe,只能在次父进程的衍生进程使用,说明半双工具有局部衍生性。
b) FIFO命名管道
i. 利用mknod建立FIFO管道
int mknod(char *filepath, mode_t mode, dev_t dev);
成功返回0;失败-1;其他errno.
示例:
umask(0);//临时清空umask
mknod(“./myfifo”, S_IFIFO | 0666, 0);
ii. FIFO建立后一般放在后台,等待客户端,示例:
int main(int argc, char *argz[])
{
/* //server
FILE *fp;
char readbuf[80];
umask(0);
mknod("./myfifo", S_IFIFO | 0666, 0);
while(1)
{
fp = fopen("./myfifo", "r");
fgets(readbuf, 80, fp);
printf("Receive Data from FIFO:%s\n", readbuf);
fclose(fp);
}
*/
//client
FILE *fp;
fp = fopen("./myfifo", "w");
fputs(argz[1], fp);
exit(0);
}
iii. 注意:一个FIFO管道写打开后即进入阻塞,直到数据的到来,如果不想阻塞须调用open()设置O_NONBLOCK选项。此外,一个FIFO管道必须同时有读写进程,如果只有写,没有读,就会产生SIGPIPE。
c) 消息队列
i. IPC关键字,产生示例:
Key_t key;
Key = ftok(“.”, ‘a’);
成功返回关键字,失败返回-1,可以使用stat()获得错误信息。
ii. 消息缓冲区
struct msgbuf{
long mtype;
(other data);
}//other data为自定义类型
msgbuf最大字节数为4056(包括long mtype),实际最大内容为4052
iii. 创建或者取一个消息队列msgget()
原型:int msgget(key_t key, int msgflag);
成功返回0,失败返回-1:errno;
IPC_CREAT用于创建或取已有队列
IPC_EXCL和IPC_CREAT连用时,只能用于创建新的队列
iv. 练习:通过消息队列,由一个父进程发送到一个子进程。(终于在下班前编译运行通过,我真的是猪,尽犯低级错误)
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <string.h>
#include <stdlib.h>
#include <fcntl.h>
#include <sys/ipc.h>
#include <sys/msg.h>
struct mymsgbuf{
long mtype;
char data[20];
};
int main()
{
key_t key;
pid_t pid;
int qid;
struct mymsgbuf sndmsgbuf;
struct mymsgbuf rcvmsgbuf;
//data initialize
sndmsgbuf.mtype = 1;
strcpy(sndmsgbuf.data, "Queue message!");
printf("Prepare:%s\n", sndmsgbuf.data);
//ganerate key
key = ftok(".", 'm');
//create
if ((qid = msgget(key, IPC_CREAT | IPC_EXCL | 0666)) ==-1)
{
perror("msgget error");
exit(-1);
}
if ((pid = fork()) < 0)
{
perror("fork");
exit(-1);
}else if (pid > 0)
{
//send
if (msgsnd(qid, &sndmsgbuf, sizeof(struct mymsgbuf)-sizeof(long), 0/*IPC_NOWAIT*/) == -1)
{
perror("msgsnd error");
exit(-1);
}
}else
{
if (msgrcv(qid, &rcvmsgbuf, sizeof(struct mymsgbuf)-sizeof(long), 0, 0) == -1)
{
perror("msgrcv error");
exit(-1);
}
printf("Data rcv from parent:%s\n", rcvmsgbuf.data);
exit(0);
}
exit(0);
}