您的位置:首页 > 其它

第十五章 IPC

2016-07-21 16:32 316 查看
1. 管道

  局限性:历史上,它们是半双工;管道只能在具有公共祖先的两个进程间使用。

  int pipe(int fd[2]); //创建管道

  fd[0]为读而打开,fd[1]为写而打开;fd[1]的输出是fd[0]的输入。

  i. 当读一个写端已被关闭的管道时,在所有数据都被读取后,read返回0,表示文件结束

  ii. 当写一个读端已被关闭的管道时,则产生信号SIGPIPE。

<span style="font-size:18px;">#include "apue.h"

int main(void)
{
int n;
int fd[2];
pid_t pid;
char line[MAXLINE];

if(pipe(fd) < 0)
err_sys("pipe error");
if((pid = fork()) < 0){
err_sys("fork error");
} else if(pid == 0){
close(fd[0]);
write(fd[1], "hello world\n", 12);
} else{
close(fd[1]);
n = read(fd[0], line, MAXLINE);
write(STDOUT_FILENO, line, n);
}
exit(0);
}
</span>


  //每次一页的显示已产生的输出

<span style="font-size:18px;">#include "apue.h"
#include <sys/wait.h>

#define DEF_PAGER "/bin/more"

int main(int argc, char *argv[])
{
int n;
int fd[2];
pid_t pid;
char *pager, *argv0;
char line[MAXLINE];
FILE *fp;

if(argc != 2)
err_quit("usage: a.out <pathname>");

if((fp = fopen(argv[1], "r")) == NULL)
err_sys("can't open %s", argv[1]);
if(pipe(fd) < 0)
err_sys("pipe error");

if((pid = fork()) < 0){
err_sys("fork error");
} else if(pid > 0){
close(fd[0]);

//parent copies argv[1] to pipe
while(fgets(line, MAXLINE, fp) != NULL){
n = strlen(line);
if(write(fd[1], line, n) != n)
err_sys("write error to pipe");
}

if(ferror(fp))
err_sys("fgets error");

close(fd[1]);

if(waitpid(pid, NULL, 0) < 0)
err_sys("waitpid error");
exit(0);
} else{
close(fd[1]);
if(fd[0] != STDIN_FILENO){
if(dup2(fd[0], STDIN_FILENO) != STDIN_FILENO)
err_sys("dup2 error to stdin");
close(fd[0]); // ??? don't need this after dup2
}

//get arguments for excel()
if((pager = getenv("PAGER")) == NULL)
pager = DEF_PAGER;
if((argv0 = strrchr(pager, '/')) != NULL)
argv0 ++;
else
argv0 = pager;

if(execl(pager, argv0, (char *)0) < 0)
err_sys("excel error for %s", pager);
}
exit(0);
}
</span>


2. popen函数和pclose函数 

  //创建一个管道,fork一个子进程,关闭未使用的管道段,执行一个shell运行命令,然后等待命令终止

  FILE *popen(const char *cmdstring, const char* type); 

  int pclose(FILE* fp);

  //将1中的第二个程序重新写

<span style="font-size:18px;">#include "apue.h"
#include <sys/wait.h>

#define PAGER "${PAGER:-more}"

int main(int argc, char *argv[])
{
char line[MAXLINE];
FILE *fpin, *fpout;

if(argc != 2)
err_quit("usage: a.out <pathname>");

if((fpin = fopen(argv[1], "r")) == NULL)
err_sys("can't open %s", argv[1]);

if((fpout = popen(PAGER, "w")) == NULL)
err_sys("popen error");

while(fgets(line, MAXLINE, fpin) != NULL){
if(fputs(line, fpout) == EOF)
err_sys("fputs error to pipe");
}

if(ferror(fpin))
err_sys("fgets error");
if(pclose(fpout) == -1)
err_sys("pclose error");

exit(0);

}
</span>


  popen和pclose函数

<span style="font-size:18px;">#include "apue.h"
#include <errno.h>
#include <fcntl.h>
#include <sys/wait.h>

static pid_t *childpid = NULL;

static int maxfd;

FILE *popen(const char *cmdstring, const char *type)
{
int i;
int pfd[2];
pid_t pid;
FILE *fp;

// only allow 'r' or 'w'
if((type[0] != 'r' && type[0] != 'w') || type[1] != 0){
errno = EINVAL;
return NULL;
}

//first time through
if(childpid == NULL){
maxfd = open_max();
if((childpid = calloc(maxfd, sizeof(pid_t))) == NULL)
return NULL;
}

if(pipe(pfd) < 0)
return NULL;
if(pfd[0] >= maxfd || pfd[1] >= maxfd){
close(pfd[0]);
close(pfd[1]);
errno = EMFILE;
return NULL;
}

if((pid = fork()) < 0){
return NULL;
} else if(pid == 0){
if(*type == 'r'){
close(pfd[0]);
if(pfd[1] != STDOUT_FILENO){
dup2(pfd[1], STDOUT_FILENO);
close(pfd[1]);
}
} else{
close(pfd[1]);
if(pfd[0] != STDIN_FILENO){
dup2(pfd[0], STDIN_FILENO);
close(pfd[0]);
}
}

for(i = 0; i < maxfd; i ++)
if(childpid[i] > 0)
close(i);

excel("/bin/sh", "sh", "-c", cmdstring, ((char *)0));
_exit(127);
} else{
if(*type == 'r'){
close(pfd[1]);
if((fp = fdopen(pfd[0], type)) == NULL)
return NULL;
} else{
close(pfd[0]);
if((fp = fdopen(pfd[1], type)) == NULL)
return NULL;
}

childpid[fileno(fp)] = pid;
return fp;
}
}

int pclose(FILE *fp)
{
int fd, stat;
pid_t pid;

if(childpid == NULL){
errno = EINVAL;
return -1;
}

fd = fileno(fp);
if(fd >= maxfd){
errno = EINVAL;
return -1;
}

if((pid = childpid[fd]) == 0){
errno = EINVAL;
return -1;
}

childpid[fd] = 0;
if(fclose(fp) == EOF)
return -1;

while(waitpid(pid, &stat, 0) < 0)
if(errno != EINTR)
return -1;

return stat;

}
</span>
 

  //一个应用程序,它向标准输出写一个提示,然后从标准输入读1行。使用popen,可以在应用程序和输入之间插入一个程序一遍对输入进行变换控制 

<span style="font-size:18px;">#include "apue.h"
#include <ctype.h>

int main(void)
{
int c;
while((c = getchar()) != EOF){
if(isupper(c))
c = tolower(c);
if(putchar(c) == EOF)
err_sys("output error");
if(c == '\n')
fflush(stdout);
}
exit(0);
}

#include "apue.h"
#include <sys/wait.h>

int main(void)
{
char line[MAXLINE];
FILE *fpin;

if((fpin = popen("./myuclc", "r")) == NULL)
err_sys("popen error");

for( ; ; ){
fputs("promt> ", stdout);
fflush(stdout);
if(fgets(line, MAXLINE, fpin) == NULL)
break;
if(fputs(line, stdout) == EOF)
err_sys("fputs error to pipe");
}
if(pclose(fpin) == -1)
err_sys("pclose error");
putchar('\n');
exit(0);

}
/**************
promt> FSDF
fsdf
promt> fsdafA
fsdafa
promt> ^C
**************/
</span>


 3. 协同进程

  //当一个过滤程序既产生某个过滤程序的输入,又读取该过滤程序的输出时,它就变成了协同进程

  //一个简单的协同进程,它从标准输入读取两个数,计算它们的和,然后将和写至其标准输出

<span style="font-size:18px;">#include "apue.h"

int main(void)
{
int n, int1, int2;
char line[MAXLINE];

while((n = read(STDIN_FILENO, line, MAXLINE)) > 0){
line
= 0;
if(sscanf(line, "%d%d", &int1, &int2) == 2){
sprintf(line, "%d\n", int1 + int2);
n = strlen(line);
if(write(STDOUT_FILENO, line, n) != n)
err_sys("write error");
} else{
if(write(STDOUT_FILENO, "invalid args\n", 13) != 13)
err_sys("write error");
}
}
exit(0);
}

#include "apue.h"

static void sig_pipe(int);

int main(void)
{
int n, fd1[2], fd2[2];
pid_t pid;
char line[MAXLINE];

if(signal(SIGPIPE, sig_pipe) == SIG_ERR)
err_sys("signal error");

if(pipe(fd1) < 0 || pipe(fd2) < 0)
err_sys("pipe error");

if((pid = fork()) < 0){
err_sys("fork error");
} else if(pid > 0){
close(fd1[0]);
close(fd2[1]);

while(fgets(line, MAXLINE, stdin) != NULL){
n = strlen(line);
if(write(fd1[1], line, n) != n)
err_sys("write error to pipe");
if((n = read(fd2[0], line, MAXLINE)) < 0)
err_sys("read error from pipe");
if(n == 0){
err_msg("child close pipe");
break;
}
line
= 0;
if(fputs(line, stdout) == EOF)
err_sys("fputs error");
}

if(ferror(stdin))
err_sys("fgets error on stdin");
exit(0);
} else{
close(fd1[1]);
close(fd2[0]);
if(fd1[0] != STDIN_FILENO){
if(dup2(fd1[0], STDIN_FILENO) != STDIN_FILENO)
err_sys("dup2 error to stdin");
close(fd1[0]);
}

if(fd2[1] != STDOUT_FILENO){
if(dup2(fd2[1], STDOUT_FILENO) != STDOUT_FILENO)
err_sys("dup2 error to stdin");
close(fd2[1]);
}
if(execl("./add2", "add2", (char *)0) < 0)
err_sys("excel error");
}
exit(0);
}

static void sig_pipe(int signo)
{
fprintf(stdout, "SIGPIPE caught\n");
exit(1);
}
/*************
12 345
357
ad fd
invalid args
123a46
invalid args
44
invalid args
^C
**************/
</span>


4. FIFO(命名管道)
  未命名管道只能在两个相关的进程之间,而且这两个的进程还要有一个共同创建了它们的祖先进程;但是,通过FIFO,不相关的进程也能交换数据

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

  int mkfifoat(int fd, const char *path, mode_t mode);

  用途:

  i. shell命令使用FIFO将数据从一条管道传送到另一条时,无需创建中间临时文件;

  ii. 客户进程-服务器进程应用程序中,FIFO用作汇聚点,在客户进程和服务进程二者之间传递数据。

5. XSI的IPC

  有三种称作XSI的IPC的IPC: 消息队列,信号量,以及共享存储器

  i. 标识符和键

    标识符是IPC对象的内部名。为使多个合作进程能够在同一IPC对象上汇聚,需要提供一个外部命名方案。为此,每个IPC对象都与一个键相关联,将这个键作为该对象的外部名。

  ii. 权限结构

  iii. 结构限制

6. 消息队列

  int msgget(key_t key, int flag); //打开一个现有队列或创建一个新队列

  int msgctl(int msqid, int cmd, struct msqid_ds *buf); //根据cmd的不同,对队列进行多种操作

  int msgsnd(int msqid, const void *ptr, size_t nbytes, int flag); //将数据放入消息队列中

  ssize_t msgrcv(int msqid, void *ptr, size_t nbytes, long type, int flag); //从队列中取消息

  //type == 0,返回队列中的第一个消息;type > 0,返回队列中消息类型为type的第一个消息;type < 0,返回队列中消息类型值小于等于type绝对值的消息。

7. 信号量

  它是一个计数器,用于为多个进程提供对共享数据对象的访问。

  int semget(key_t key, int nsems, int flag); //获得一个信号量ID

  int semctl(int semid, int semnum, int cmd, .../* union semum arg */); //根据cmd的不同,执行不同的操作

  int semop(int semid, struct sembuf semoparray[], size_t nops); //自动执行信号量集合上的操作数组

8. 共享存储

  共享存储允许两个或多个进程共享一个给定的存储区。因为数据不需要在客户进程和服务器进程之间复制,所以这是最快的一种IPC。

  int shmget(key_t key, size_t size, int flag); //获得一个共享存储标识符

  int shmctl(int shmid, int cmd, struct shmid_ds *buf); //根据cmd的不同,进行不同的操作

  void *shmat(int shmid, const void *addr, int flag); //一旦创建了一个共享存储段,将其连接到它的地址空间中

  //addr为0 ,则此段连接到内核选择的第一个可用地址上;若非0,则连接到指定地址上

  int shmdt(const void *arr); //当对共享存储段的操作已经结束时,与该段分离

  

  //看共享存储段所存放的位置

<span style="font-size:18px;">#include "apue.h"
#include <sys/shm.h>

#define ARRAY_SIZE 40000
#define MALLOC_SIZE 100000
#define SHM_SIZE 100000
#define SHM_MODE 0600

char array[ARRAY_SIZE];

int main(void)
{
int shmid;
char *ptr, *shmptr;

printf("array[] from %p to %p\n", (void *)array, (void *)(array + ARRAY_SIZE));
printf("stack around %p\n", (void *)(&shmid));

if((ptr = malloc(MALLOC_SIZE)) == NULL)
err_sys("malloc error");
printf("malloced from %p to %p\n", (void *)ptr, (void *)(ptr + MALLOC_SIZE));

if((shmid = shmget(IPC_PRIVATE, SHM_SIZE, SHM_MODE)) < 0)
err_sys("shmget error");
if((shmptr = shmat(shmid, 0, 0)) == (void *)-1)
err_sys("shmat error");
printf("shared memory attached from %p to %p\n", (void *)shmptr, (void *)(shmptr + SHM_SIZE));

if(shmctl(shmid, IPC_RMID, 0) < 0)
err_sys("shmctl error");
exit(0);
}
/**********************************************************
array[] from 0x6020e0 to 0x60bd20
stack around 0x7ffe3e84dbcc
malloced from 0x1602010 to 0x161a6b0
shared memory attached from 0x7fe18bc66000 to 0x7fe18bc7e6a0
***********************************************************/</span>


  /dev/zero的存储映射
<span style="font-size:18px;">#include "apue.h"
#include <fcntl.h>
#include <sys/mman.h>

#define NLOOPS 1000
#define SIZE sizeof(long)

static int update(long *ptr)
{
return ((*ptr) ++);
}

int main(void)
{
int fd, i, counter;
pid_t pid;
void *area;

if((fd = open("/dev/zero", O_RDWR)) < 0)
err_sys("open error");
if((area = mmap(0, SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0)) == MAP_FAILED)
err_sys("mmap error");
close(fd);

TELL_WAIT();

if((pid = fork()) < 0){
err_sys("fork error");
} else if(pid > 0){
for(i = 0; i < NLOOPS; i += 2){
if((counter = update((long *)area)) != i)
err_quit("parent: expected %d, got %d", i, counter);
TELL_CHILD(pid);
WAIT_CHILD();
}
} else{
for(i = 1; i < NLOOPS; i += 2){
WAIT_PARENT();

if((counter = update((long *)area)) != i)
err_quit("child: expected %d, got %d", i, counter);

TELL_PARENT(getppid());
}
}
exit(0);
}
</span>


  匿名存储映射(根据上面的程序进行修改)
  i. 删除了/dev/zero的open语句; ii. 删除了fd的close语句; iii. 将mmap调用修改:

  if((area = mmap(0, SIZE, PROT_READ |  PROT_WRITE, MAP_ANON | MAP_SHARED, -1, 0)) == MAP_FAILED)

  

9. POSIX信号量

  有两种形式:命名的和未命名的。 未命名的信号量只存在于内存中,并要求使用信号量的进程必须访问内存。命名信号量可以通过名字访问,因此可以被任何已知它们名字的进程中的线程使用。

  //创建一个新的命名信号量或者使用一个现有信号量

  sem_t *sem_open(const char *name, int oflag, ... /* mode_t mode, unsigned int value */);

  int sem_close(sem_t *sem); //释放任何信号量相关的资源

  int sem_unlink(const char *name); //销毁一个命名信号量

  int sem_trywait(sem_t *sem);  int sem_wait(sem_t *sem); //实现信号量减1操作

  int sem_timedwait(sem_t *restrict sem, const  struct timespec *restrict tsptr);

  int sem_post(sem_t *sem); //使信号量值增1

  int sem_init(sem_t *sem, int pshared, unsigned int value); //创建一个未命名的信号量

  int sem_destroy(sem_t *sem); //未命名的信号量的使用完成时,对其丢弃

  int sem_getvalue(sem_t *restrict sem, int *restrict valp); //检索信号量值

  基于信号量的互斥原语的实现

<span style="font-size:18px;">#include "slock.h"
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <errno.h>

struct slock* s_alloc()
{
struct slock *sp;
static int cnt;

if((sp = malloc(sizeof(struct slock))) == NULL)
return NULL;
do{
snprintf(sp->name, sizeof(sp->name), "%ld.%d", (long)getpid(), cnt++);
sp->semp = sem_open(sp->name, O_CREAT | O_EXCL, S_IRWXU, 1);
}while(sp->semp == SEM_FAILED && errno == EEIXST);
if(sp->semp == SEM_FAILED){
free(sp);
return NULL;
}
sem_unlink(sp->name);
return sp;
}

void s_free(struct slock *sp)
{
sem_close(sp->semp);
free(sp);
}

int s_lock(struct slock *sp)
{
return (sem_wait(sp->semp));
}

int s_trylock(struct slock *sp)
{
return (sem_trywait(sp->semp));
}

int s_unlock(struct slock *sp)
{
return (sem_post(sp->semp));
}
</span>


10. 客户进程-服务器进程属性
  
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: