《APUE》chapter 15 Interprocess Communication 学习笔记(加上自己的代码)

时间:2022-05-18 22:50:32

Interprocess Communication



Pipes


Pipes have two limitations.
1.  Historically ,they  have  been  half  duplex  (i.e.,  data  flows  in  only  one  direction). Some systems now provide full-duplex pipes, but for maximum portability ,we should never assume that this is the case.

2.  Pipes can  be  used  only  between  processes  that  have  a  common  ancestor.  Normally,a pipe is created by a process, that process calls fork,and the pipe is  used between the parent and the child



A pipe is created by calling the pipe function.
#include <unistd.h>
int pipe(int fd[2] );
Returns: 0 if OK,−1 on error
          Two file descriptors are returned through the fd argument: fd[0] is open for reading, and fd[1] is open for writing. The output of fd[1] is the input for fd[0] .


《APUE》chapter 15 Interprocess Communication 学习笔记(加上自己的代码)


            We  can test for a pipe with the S_ISFIFO macro. A pipe in a single process is next to useless. Normally ,the process that calls pipe then  calls fork,creating  an  IPC  channel  from  the  parent  to  the  child,  or  vice  versa




When one end of a pipe is closed, two rules apply.

1.  If we read from  a  pipe  whose  write  end  has  been  closed, read re turns  0  to  indicate  an  end  of  file  after  all  the  data  has  been  read. 

2.  If we write to  a  pipe  whose  read  end  has  been  closed,  the  signal SIGPIPE is  generated.  If we  either  ignore the  signal  or  catch  it  and  return  from  the  signal  handler, write returns −1 with errno set to EPIPE


/***************************************************************************************
code writer :EOF
code date : 2014.04.10
e-mail : jasonleaster@gmail.com

code purpose :
        Just a changed and test demo.
        If there is something wrong with my code, please touch me by
e-mail. If you know about why there is no competition between the child
process and parent process when they "read" and "write", please touch
me. I am very happy to share my code with you. Wish it could
help you to understand pipe. Thank you.


***************************************************************************************/
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>

#define MAXLINE 4096

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

        if(pipe(fd) < 0)
        {
                printf("pipe error\n");
        }

        if((pid = fork()) < 0)
        {
                printf("fork error\n");
        }
        else if( pid > 0)
        {
                sleep(1);
                //It's interesting that let child go first 
                //and I found that the read from pipe  would wait for pipe input finished

                close(fd[0]);
                write(fd[1],"hello world!\n",13);
                waitpid(-1,NULL,0);//It's neccessary to wait for child to terminate.
        }
        else
        {
                close(fd[1]);
                n = read(fd[0],line,MAXLINE);
                write(STDOUT_FILENO,line,n);
        }

        return 0;
}



jasonleaster@ubuntu:/Ad_Pro_in_Unix/chapter_15$ ./a.out ./temp.txt 
hello world!

        APUE 书上的例子并没有给出那个sleep(1)。是我自己加上去的,我不理解为什么作者没有考虑child process 和parent process 的竞争的问题。


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

#define DEF_PAGER       "/bin/more"
#define MAXLINE         4096

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

        FILE    *fp;

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

        if(argc != 2)
        {
                printf("usage: a.out <pathname>\n");
                return 0;
        }

        if((fp = fopen(argv[1],"r")) < 0)
        {
                printf("can't open %s\n",argv[1]);
                return 0;
        }

        if(pipe(fd) < 0)
        {
                printf("pipe error\n");
        }

        if((pid = fork()) < 0)
       {
                printf("fork error\n");
        }
        else if(pid > 0)
        {
                close(fd[0]);
                while(fgets(line,MAXLINE,fp) != NULL)
                {
                        n = strlen(line);
                        if(write(fd[1],line,n) != n)
                        {
                                printf("write error to pipe\n");
                        }
                }

                if(ferror(fp))
                {
                        printf("fgets error\n");
                }

                close(fd[1]);

                if(waitpid(pid, NULL,0) < 0)
                {
                        printf("waitpid error\n");
                }
                exit(0);
        }
        else
        {
                close(fd[1]);

                if(fd[0] != STDIN_FILENO)
                {
                        if(dup2(fd[0],STDIN_FILENO) != STDIN_FILENO)
                        {
                                printf("dup2 error to stdin\n");
                        }

                        close(fd[0]);
                }

                if((pager = getenv("PAGER")) == NULL)
                {
                        pager = DEF_PAGER;
                }

                if((argv0 = strrchr(pager,'/')) != NULL)
                {
                        argv0++;
                }
                else
                {
                        argv0 = pager;
                }

                if(execl(pager,argv0,NULL) < 0)
                {
                        printf("execl error for %s\n",pager);
                }

                return 0;
        }


        return 0;
}


这段
close(fd[1]);

if(fd[0] != STDIN_FILENO)
{
          if(dup2(fd[0],STDIN_FILENO) != STDIN_FILENO)
         {
                   printf("dup2 error to stdin\n");
         }

          close(fd[0]);
}

如果没看懂,就戳这里:http://blog.csdn.net/cinmyheart/article/details/19243733

jasonleaster@ubuntu:/Ad_Pro_in_Unix/chapter_15$ ./a.out ./temp.txt 
hello world!
stay hungry stay foolish stay patient




之前signal那章有TELL_WAIT之类的函数,没有实现的,或者用signal去实现的,这里可以用pipe实现进程通信
#include <stdio.h>
#include <fcntl.h>

static pfd1[2],pfd2[2];

void TELL_WAIT(void)
{
        if(pipe(fpd1) < 0 || pipe(pfd2) < 0)
        {
                printf("pipe error\n");
        }
}


void TELL_PARENT(void)//tell to parent
{
        if(write(pfd2[1],"c",1) != 1)
        {
                printf("write error\n");
        }
}

void WAIT_PARENT(void)//wait for parent
{
        char c;

        if(read(pfd1[0],&c,1) != 1)
        {
                printf("read error\n");
        }

        if(c != 'p')
        {
                printf("WAIT_PARENT: incorrect data\n");
        }
}

void TELL_CHILD(pid_t pid)//tell to child
{
        if(write(pfd1[1],"p",1) != 1)
        {
                printf("write error\n");
        }
}

void WAIT_CHILD(void)//wait for child
{
        char c;
        if(read(pdf2[0],&c,1) != 1)
        {
                printf("read error\n");
        }

        if(c != 'c')
        {
                printf("WAIT_CHILD : incorrect data\n");
        }
}


《APUE》chapter 15 Interprocess Communication 学习笔记(加上自己的代码)




popen and pclose Functions

#include <stdio.h>
FILE *popen(const char *cmdstring,const char *type );
Returns: file pointer if OK, NULL on error
int pclose(FILE *fp );
Returns: termination status ofcmdstring,or −1 on error


If type is "r" ,t he  file  pointer  is  connected  to  the  standard output of cmdstring.
If type is "w" ,t he file pointer is connected to the standard input of cmdstring。



#include <stdio.h>
#include <sys/wait.h>
#include <fcntl.h>

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

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

        if(argc != 2)
        {
                printf("usage: a.out <pathname>\n");
        }

        if((fpin = fopen(argv[1],"r")) == NULL)
        {
                printf("fopen error\n");
        }

        if((fpout = popen(PAGER,"w")) == NULL)//single direction pipe
        {
                printf("popen error\n");
        }

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

        if(ferror(fpin))
        {
                printf("fgets error\n");
        }

        if(pclose(fpout) == -1)
        {
                printf("pclose error\n");
        }

        return 0;
}

jasonleaster@ubuntu:/Ad_Pro_in_Unix/chapter_15$ ./a.out ./temp.txt 
hello world!
stay hungry stay foolish stay patient



         Consider an application that writes a prompt to standard output and reads a line from standard input.  With  the popen function,  we  can  interpose  a  program  between  the application and its input to transform the input.

《APUE》chapter 15 Interprocess Communication 学习笔记(加上自己的代码)
#include <stdio.h>
#include <ctype.h>

int main()
{
        int c;

        while((c = getchar()) != EOF)
        {
                if(isupper(c))
                {
                        c = tolower(c);
                }

                if(putchar(c) == EOF)
                {
                        printf("output error\n");
                }

                if(c == '\n')
                {
                        fflush(stdout);
                }
        }

        return 0;
}

liuzjian@ubuntu:/Ad_Pro_in_Unix/chapter_15$ ./a.out
HELLO WORLD  I AM JASON LEASTER
hello world  i am jason leaster




#include <stdio.h>
#include <sys/wait.h>
#include <unistd.h>

#define MAXLINE 4096

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

        if((fpin = popen("./myuclc","r")) == NULL)
        {
                printf("popen error\n");
        }

        for(;;)
        {
                fputs("prompt >",stdout);
                fflush(stdout);

                if(fgets(line,MAXLINE,fpin) == NULL)
                {
                        break;
                }

                if(fputs(line,stdout) == EOF)
                {
                        printf("fputs error to pipe\n");
                }
        }

        if(pclose(fpin) == -1)
        {
                printf("pclose error\n");
        }

        putchar('\n');

        return 0;
}


这个demo里面那个myuclc我始终不明白是怎么回事,今天问问别人。。。
update:2014/04/13
其实myuclc就是上一个demo编译成的可执行文件
注意一下,popen打开myuclc的时候记得用./不然popen找不到myuclc的

jasonleaster@ubuntu:/Ad_Pro_in_Unix/chapter_15$ ./a.out
prompt >hello world
hello world
prompt >wakaka
wakaka
prompt >jasonleaster
jasonleaster
prompt >









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

#define MAXLINE 4096

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

        while((n = read(STDIN_FILENO,line,MAXLINE)) > 0)
        {
                line[n] = 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)
                        {
                                printf("write error\n");
                        }
                }
                else
                {
                        if(write(STDOUT_FILENO,"invalid args\n",13) != 13)
                        {
                                printf("write error\n");
                        }
                }
        }

        return 0;
}


jasonleaster@ubuntu:/Ad_Pro_in_Unix/chapter_15$ ./a.out
10 90
100
11 88
99
1 1
2





pro_15_18.c
这是个相当令人头疼的问题:待解决,高手路过请看一下,下面这个代码
update: 2014/04/13 问题已经解决
#include <stdio.h>
#include <string.h>
#include <signal.h>
#include <unistd.h>
#include <fcntl.h>

#define MAXLINE 4096

static void sig_pipe(int signo)
{
        printf("SIGPIPE caught\n");
}

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

        struct sigaction sa;

        sigemptyset(&sa.sa_mask);
        sa.sa_flags = 0;
        sa.sa_handler = sig_pipe;
        sigaddset(&sa.sa_mask,SIGPIPE);

        if(sigaction(SIGPIPE,&sa,NULL) < 0)
        {
                printf("sigaction error\n");
                return 0;
        }

        if(pipe(fd2) < 0 || pipe(fd1) < 0)
        {
                printf("pipe error\n");
                return 0;
        }

        if((pid = fork()) < 0)
        {
                printf("fork error\n");
        }
        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)
                        {
                                printf("write error\n");
                        }

                        if(read(fd2[0],line,MAXLINE) < 0)//Is there some problems that we use the same buffer--line
                        {
                                printf("read error from pipe\n");
                        }

                        if(n == 0)
                        {
                                printf("child close pipe\n");
                                break;
                        }

                        line[n] = 0;
                        if(fputs(line,stdout) == EOF)
                        {
                                printf("fputs error\n");
                        }
                }

                if(ferror(stdin))
                {
                        printf("fgets error\n");
                }

/*              if(waitpid(pid,NULL,0) < 0)
                {
                        printf("waitpid error\n");
                }
*/
                return 0;
        }
        else
        {
                close(fd1[1]);
                close(fd2[0]);

                if(fd1[0] != STDIN_FILENO)
                {
                        if(dup2(fd1[0],STDIN_FILENO) != STDIN_FILENO)
                        {
                                printf("dup2 error to stdin\n");
                        }


                        close(fd1[0]);
                }

                if(fd2[1] != STDOUT_FILENO)
                {
                        if(dup2(fd2[1],STDOUT_FILENO) != STDOUT_FILENO)
                        {
                                printf("dup2 error\n");
                        }
                        close(fd2[1]);
                }

                while(fgets(line,MAXLINE,stdin) != NULL)
                {
                        fputs(line,stdout);
                }
        //      pause();
/*              if(waitpid(getppid(),NULL,0) < 0)
                {
                        printf("waitpid error\n");
                }

                if(execl("./add2","add2",NULL) < 0)
                {
                        printf("execl error\n");
                }
*/
        }

        return 0;
}
APUE没给输出,只是大致说了一下,但是我debug的时候,不管怎么调都是会触发SIGPIPE。管道莫名其妙的就断了

update: 2014/04/13 afternoon
问题是自己最了解,所以。。。最可能解决问题的人,还是自己。。。

是coprocess错了。不应该是随便的一个可执行程序或者是pro_15_19.c编译出来东西,而是pro_15_17.c(就是本篇blog这个问题代码的上面一个demo就是pro_15_17.c ) 编译的可执行程序!。。。。

我看到高级IPC的时候才知道。。。pro_15_17才是那个coprocess。。。我去。。。


jasonleaster@ubuntu:/Ad_Pro_in_Unix/chapter_15$ ./a.out
1 2
3
1 1
2















#include <stdio.h>

#define MAXLINE 4096

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

        while(fgets(line,MAXLINE,stdin) != NULL)
        {
                if(sscanf(line,"%d%d",&int1,&int2) == 2)
                {
                        if(printf("%d\n",int1+int2) == EOF)
                        {
                                printf("printf error\n");
                        }
                }
                else
                {
                        if(printf("invalid args\n") == EOF)
                        {
                                printf("printf error\n");
                        }
                }
        }

        return 0;
}

jasonleaster@ubuntu:/Ad_Pro_in_Unix/chapter_15$ ./a.out
1 2
3
12 34
46





FIFOs

     

       FIFOs  are sometimes  called  named  pipes. Unnamed  pipes  can  be  used  only  between related processes when a common ancestor has created the pipe. 


        Creating a FIFO is similar to creating a file. Indeed, the pathname for a FIFO exists in the file system.

#include <sys/stat.h>
int mkfifo(const char * path ,mode_tmode);
int mkfifoat(int fd ,const char *path ,mode_tmode);
Both return: 0 if OK, −1 on error

The  specification  of  the mode argument  is  the  same  as  for  the open function

            Once we have used mkfifo or mkfifoat to create a FIFO, we open it using open. Indeed,  the  normal  file  I/O  functions  (e.g., close, read, write, unlink)all  work with FIFOs.


When we open a FIFO, the nonblocking flag ( O_NONBLOCK)affects what happens.
•In the  normal  case  (without  O_NONBLOCK),  an open for  read-only  blocks  until  some  other  process opens  the  FIFO  for  writing. Similarly,an open for  write-only blocks until some other process opens the FIFO for reading.

•If O_NONBLOCK is specified, an open for read-only returns immediately.But an  open for  write-only  returns −1 with errno set  to ENXIO if  no  process  has  the  FIFO open for reading.

        As with a pipe, if we write to a FIFO that no process has open for reading, the signal SIGPIPE is generated. When the last writer for a FIFO closes the FIFO, an end of file is generated for the reader of the FIFO

There are two uses for FIFOs.
1.  FIFOs are used  by  shell  commands  to  pass  data  from  one  shell  pipeline  to  another without creating intermediate temporary files.

2.  FIFOs are used  as  rendezvous  points  in  client–server  applications  to  pass  data  between the clients and the servers.




XSI IPC


          The three types of IPC that we call XSI IPC—message queues, semaphores, and shared memory — have many similarities.


Identifier sand  Keys

 
      Unlike file  descriptors,  IPC  identifiers  arenot  small  integers. Indeed,  when  a given  IPC  structure is created  and  then  removed,  the  identifier  associated  with  that structure continually  increases  until  it  reaches  the  maximum  positive  value  for  an integer,and then wraps around to 0


#include <sys/ipc.h>
key_t ftok(const char * path ,int id );
Returns: key if OK, (key_t)−1 on error


       The path argument must refer to an existing file. Only the lower 8 bits of id are used when generating the key

       To  reference an existing queue (normally done by a client), key must equal the key that was specified when the queue was created, and IPC_CREAT must not be specified. Note  that  it’s  never  possible  to  specify IPC_PRIVATE to  reference  an  existing queue, since this special key value always creates a new queue.


Permission  Structure


ipc.h 里面的定义
/* Mode bits for `msgget', `semget', and `shmget'.  */
#define IPC_CREAT       01000           /* Create key if key does not exist. */
#define IPC_EXCL        02000           /* Fail if key exists.  */
#define IPC_NOWAIT      04000           /* Return error on wait.  */


/* Control commands for `msgctl', `semctl', and `shmctl'.  */
#define IPC_RMID        0               /* Remove identifier.  */
#define IPC_SET         1               /* Set `ipc_perm' options.  */
#define IPC_STAT        2               /* Get `ipc_perm' options.  */
#ifdef __USE_GNU
# define IPC_INFO       3               /* See ipcs.  */
#endif


/* Special key values.  */
#define IPC_PRIVATE     ((__key_t) 0)   /* Private key.  */




/* Data structure used to pass permission information to IPC operations.  */
struct ipc_perm
  {
    __key_t __key;                      /* Key.  */
    __uid_t uid;                        /* Owner's user ID.  */
    __gid_t gid;                        /* Owner's group ID.  */
    __uid_t cuid;                       /* Creator's user ID.  */
    __gid_t cgid;                       /* Creator's group ID.  */
    unsigned short int mode;            /* Read/write permission.  */
    unsigned short int __pad1;
    unsigned short int __seq;           /* Sequence number.  */
    unsigned short int __pad2;
    __syscall_ulong_t __unused1;
    __syscall_ulong_t __unused2;
  };


Configuration Limits


jasonleaster@ubuntu:/Ad_Pro_in_Unix/chapter_15$ ipcs -l


------ Shared Memory Limits --------
max number of segments = 4096
max seg size (kbytes) = 32768
max total shared memory (kbytes) = 8388608
min seg size (bytes) = 1


------ Semaphore Limits --------
max number of arrays = 128
max semaphores per array = 250
max semaphores system wide = 32000
max ops per semop call = 32
semaphore max value = 32767


------ Messages Limits --------
max queues system wide = 3986
max size of message (bytes) = 8192
default max size of queue (bytes) = 16384


Advantages and  Disadvantages

 
      if  we  create  a  message  queue,  place  some messages on the queue, and then terminate, the message queue and its contents are not deleted. 


      With a FIFO, although the name stays in the file  system  until  explicitly  removed,  any  data  left  in  a  FIFO  is  removed  when  the  last process to reference the FIFO terminates


       We  can’t see the IPC objects with an ls command, we can’t remove them with the rm command, and we  can’t  change  their  permissions  with  the chmodcommand.  Instead, two  new commands — ipcs(1) and ipcrm(1) —wer e added





Message  Queues



       A message queue is a linked list of messages stored within the kernel and identified by  a message queue identifier.

linux里面msqid_ds 结构体的定义
struct msqid_ds
{
  struct ipc_perm msg_perm;     /* structure describing operation permission */
  __time_t msg_stime;           /* time of last msgsnd command */
#ifndef __x86_64__
  unsigned long int __unused1;
#endif
  __time_t msg_rtime;           /* time of last msgrcv command */
#ifndef __x86_64__
  unsigned long int __unused2;
#endif
  __time_t msg_ctime;           /* time of last change */
#ifndef __x86_64__
  unsigned long int __unused3;
#endif
  __syscall_ulong_t __msg_cbytes; /* current number of bytes on queue */
  msgqnum_t msg_qnum;           /* number of messages currently on queue */
  msglen_t msg_qbytes;          /* max number of bytes allowed on queue */
  __pid_t msg_lspid;            /* pid of last msgsnd() */
  __pid_t msg_lrpid;            /* pid of last msgrcv() */
  __syscall_ulong_t __unused4;
  __syscall_ulong_t __unused5;
};


      The  first  function  normally  called  is msgget to  either  open  an  existing  queue  or create a new queue.

#include <sys/msg.h>
int msgget(key_t key,int flag);
Returns: message queue ID if OK, −1 on error

#include <sys/msg.h>
int msgctl(int msqid,int cmd ,struct msqid_ds *buf );
Returns: 0 if OK,−1 on error

#include <sys/msg.h>
int msgsnd(int msqid,const void *ptr,size_tnbytes ,int flag);
Returns: 0 if OK,−1 on error


也不知道。。。怎么用这几个API,如果以后用涉及了消息队列再回头demo吧。。。





Semaphores


        A semaphore is a counter  used  to  provide  access  to  a shared data object for multiple processes.

       When  a  process  is  done  with  a  shared  resource  that  is  controlled  by  a  semaphore,  the semaphore value is incremented by 1. If any other processes are asleep, waiting for the semaphore, they area wakened.






Shared Memory


         Shared memory allows two or more processes to share a given region of memory.This is the fastest form of IPC, because the data does not need to be copied between the client and  the  server .The  only  trick  in  using shared  memory  is  synchronizing  access  to  a given  region  among  multiple  processes.  


The  kernel  maintains  a  structure with  at  least  the  following  members  for  each shared memory segment:

linux下找到的shmid_ds的结构体定义
/* Data structure describing a shared memory segment.  */
struct shmid_ds
  {
    struct ipc_perm shm_perm;           /* operation permission struct */
    size_t shm_segsz;                   /* size of segment in bytes */
    __time_t shm_atime;                 /* time of last shmat() */
#ifndef __x86_64__
    unsigned long int __unused1;
#endif
    __time_t shm_dtime;                 /* time of last shmdt() */
#ifndef __x86_64__
    unsigned long int __unused2;
#endif
    __time_t shm_ctime;                 /* time of last change by shmctl() */
#ifndef __x86_64__
    unsigned long int __unused3;
#endif
    __pid_t shm_cpid;                   /* pid of creator */
    __pid_t shm_lpid;                   /* pid of last shmop */
    shmatt_t shm_nattch;                /* number of current attaches */
    __syscall_ulong_t __unused4;
    __syscall_ulong_t __unused5;
  };

ipc.h 里面关于IPC的一些宏定义
/* Mode bits for `msgget', `semget', and `shmget'.  */
#define IPC_CREAT       01000           /* Create key if key does not exist. */
#define IPC_EXCL        02000           /* Fail if key exists.  */
#define IPC_NOWAIT      04000           /* Return error on wait.  */


/* Control commands for `msgctl', `semctl', and `shmctl'.  */
#define IPC_RMID        0               /* Remove identifier.  */
#define IPC_SET         1               /* Set `ipc_perm' options.  */
#define IPC_STAT        2               /* Get `ipc_perm' options.  */
#ifdef __USE_GNU
# define IPC_INFO       3               /* See ipcs.  */
#endif


/* Special key values.  */
#define IPC_PRIVATE     ((__key_t) 0)   /* Private key.  */



#include <sys/shm.h>
int shmget(key_t key,size_tsize ,int flag);
Returns: shared memory ID if OK,−1 on error

The size parameter  is  the  size  of  the  shared  memory  segment  in  bytes.如果那个flag不用就置0就可以了


#include <sys/shm.h>
int shmctl(int shmid ,int cmd ,struct shmid_ds *buf );
Returns: 0 if OK,−1 on error

     The cmd argument  specifies  one  of  the  following  five  commands  to  be  performed, on the segment specified by shmid .

IPC_STAT 
Fetch  the shmid_ds structur efor  this  segment,  storing  it  in  the structure pointed to by buf.


IPC_SET 
         Set  the  following  three  fields  from  the  structure pointed  to  by buf in  the shmid_ds structure associated with this shared memory segment:  shm_perm.uid, shm_perm.gid,and shm_perm.mode .This  command  can  be executed  only  by  a  process  whose  effective  user  ID  equals shm_perm.cuid or shm_perm.uid or  by  a process  with  superuser privileges.


IPC_RMID 
              Remove  the  shared  memory  segment  set  from  the  system. Since  an  attachment  count  is  maintained  for shared  memory  segments  (the  shm_nattch field  in  the shmid_ds structure),  the  segment  is  not  removed  until  the  last  process  using  the  segment  terminates  or  detaches  it. Regardless  of  whether  the  segment  is  still  in  use,  the  segment’s  identifier  is  immediately  removed  so  that shmat can  no
longer attach the segment. This command can be executed only by a  process  whose  effective  user  ID equals shm_perm.cuid or  shm_perm.uid or by a process with superuser privileges




Once a shared memory segment has been created, a process attaches it to its address space by calling shmat


#include <sys/shm.h>
void *shmat(intshmid ,const void *addr ,int flag);
Returns: pointer to shared memory segment if OK,−1 on error


       The address in the calling process at which the segment is attached depends on the addr argument and whether the SHM_RND bit is specified in flag.

•If addr is 0, the segment is attached at the first available address selected by the  kernel.  This is the recommended technique.

•If addr is  nonzero and SHM_RNDis  not  specified,  the  segment  is  attached  at  the  address given by addr .

•If addr is  nonzero and SHM_RND is  specified,  the  segment  is  attached  at  the  address  given  by  ( addr − (addr modulus SHMLBA)).  The SHM_RND command  stands for ‘‘round.’’ SHMLBA stands for ‘‘low boundary address multiple’ ’and is  always a power of 2. What the arithmetic does is round the address down to the  next multiple of SHMLBA.




        When we’re done with a shared memory segment, we call shmdt to detach it. Note that  this  does  not  remove  the identifier  and  its  associated  data  structure from the system.  The identifier  remains  in  existence  until  some process  (often  a  server) specifically removes it by calling shmctl with a command of IPC_RMID.

#include <sys/shm.h>
int shmdt(const void *addr );
Returns: 0 if OK,−1 on error



#include <stdio.h>
#include <stdlib.h>
#include <sys/shm.h>

#define ARRAY_SIZE      4000
#define MALLOC_SIZE     100000
#define SHM_SIZE        100000
#define SHM_MODE        0600  //usr read/write mode

char    array[ARRAY_SIZE];

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

        printf("array[] from %lx to %lx\n",(unsigned long)&array[0],\
        (unsigned long)&array[ARRAY_SIZE]);

        printf("stack around %lx \n",(unsigned long)&shmid);

        if((ptr = malloc(MALLOC_SIZE)) == NULL)
        {
                printf("malloc error\n");
        }

        printf("malloced from %lxto %lx\n",(unsigned long)ptr,\
        (unsigned long)ptr+MALLOC_SIZE);

        if((shmid = shmget(IPC_PRIVATE,SHM_SIZE,SHM_MODE)) < 0)
        {
                printf("shmget error\n");
        }

        if((shmptr = shmat(shmid,0,0)) == NULL)
        {
                printf("shmat error\n");
        }
        printf("shared memory attached from %lx to %lx\n",(unsigned long)shmptr,\
        (unsigned long)shmptr+SHM_SIZE);

        if(shmctl(shmid,IPC_RMID,0) < 0)
        {
                printf("shmctl error\n");
        }

        return 0;
}



jasonleaster@ubuntu:/Ad_Pro_in_Unix/chapter_15$ ./a.out
array[] from 6010a0 to 602040
stack around 7fffd110aa2c 
malloced from 1a54010to 1a6c6b0
shared memory attached from 7fc8fa190000 to 7fc8fa1a86a0


《APUE》chapter 15 Interprocess Communication 学习笔记(加上自己的代码)


demo:

Memory Mapping of /dev/zero



       The device /dev/zero is an infinite source of 0 bytes when read.  This device also accepts  any  data  that  is  written  to  it,  ignoring  the  data.

      The  program  opens  the /dev/zero device  and  calls mmap,specifying  a  size  of  a long  integer .Note  that once the  region  is  mapped,  we  can close the  device. The process  then  creates  a  child. Since MAP_SHARED was specified  in  the  call  to mmap, writes to the memory-mapped region by one process are seen by the other process. (If we had specified MAP_PRIVATE instead, this example wouldn’t work.)

      The parent and the child then alternate running, incrementing a long integer in the shared  memory-mapped  region.

#include <stdio.h>
#include <fcntl.h>
#include <sys/mman.h>

#define NLOOPS  1000
#define SIZE    sizeof(long)

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

int main()
{
        int fd,i,counter;
        pid_t   pid;
        void * area;
        int fd1[2],fd2[2];

        if((fd = open("/dev/zero",O_RDWR)) < 0)
        {
                printf("open error\n");
        }

        if((area = mmap(0,SIZE,PROT_READ | PROT_WRITE,MAP_SHARED,fd,0)) == MAP_FAILED)
        {
                printf("mmap error\n");
        }

        close(fd);

        if(pipe(fd1) < 0 || pipe(fd2) < 0)
        {
                printf("pipe error\n");
        }

        if((pid = fork()) < 0)
        {
                printf("fork error\n");
        }
        else if(pid > 0)
        {


                close(fd1[0]);
                close(fd2[1]);


                int c;


                for(i = 0;i < NLOOPS;i++)
                {
                        if((counter = update((long*) area)) != i)
                        {
                                printf("parent: expect %d,got %d\n",i,counter);
                        }


//                      TELL_CHILD(pid);
                        if(write(fd1[1],"p",1) != 1)
                        {
                                printf("write error\n");
                        }


//                      WAIT_CHILD();
                        if(read(fd2[0],&c,1) != 1)
                        {
                                printf("read error\n");
                        }
                }
        }
        else
        {
                close(fd1[1]);
                close(fd2[0]);
                for(i = 0;i < NLOOPS;i++)
                {
                        if((counter = update((long*) area)) != i)
                        {
                                printf("parent: expect %d,got %d\n",i,counter);
                        }


//                      TELL_PARENT();
                        if(write(fd2[1],"c",1) != 1)
                        {
                                printf("write error\n");
                        }
                }
        }


        return 0;
}

jasonleaster@ubuntu:/Ad_Pro_in_Unix/chapter_15$ ./a.out | more
parent: expect 0,got 1
parent: expect 1,got 3
parent: expect 2,got 5
parent: expect 3,got 7
parent: expect 4,got 9
parent: expect 5,got 11
。。。。
中间还有很多类似的输出
parent: expect 998,got 1996
parent: expect 998,got 1997
parent: expect 999,got 1998
parent: expect 999,got 1999



Anonymous Memory Mapping



          Many  implementations  provide  anonymous  memory  mapping,  a  facility  similar  to  the /dev/zero feature.  To use this  facility, we specify  the MAP_ANON flag  to mmap and specify  the  file  descriptor  as −1.



#include <stdio.h>
#include <fcntl.h>
#include <sys/mman.h>

#define NLOOPS  1000
#define SIZE    sizeof(long)

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

int main()
{
        int fd,i,counter;
        pid_t   pid;
        void * area;
        int fd1[2],fd2[2];

/*      if((fd = open("/dev/zero",O_RDWR)) < 0)
        {
                printf("open error\n");
        }
*/
        if((area = mmap(0,SIZE,PROT_READ | PROT_WRITE,MAP_SHARED | MAP_ANON,-1,0)) == MAP_FAILED)
        {
                printf("mmap error\n");
        }

//      close(fd);

        if(pipe(fd1) < 0 || pipe(fd2) < 0)
        {
                printf("pipe error\n");
        }

        if((pid = fork()) < 0)
        {
                printf("fork error\n");
        }
        else if(pid > 0)
        {

                close(fd1[0]);
                close(fd2[1]);

                int c;

                for(i = 0;i < NLOOPS;i++)
                {
                        if((counter = update((long*) area)) != i)
                        {
                                printf("parent: expect %d,got %d\n",i,counter);
                        }

//                      TELL_CHILD(pid);
                        if(write(fd1[1],"p",1) != 1)
                        {
                                printf("write error\n");
                        }

//                      WAIT_CHILD();
                        if(read(fd2[0],&c,1) != 1)
                        {
                                printf("read error\n");
                        }
                }
        }
        else
        {
                close(fd1[1]);
                close(fd2[0]);

                for(i = 0;i < NLOOPS;i++)
                {
                        if((counter = update((long*) area)) != i)
                        {
                                printf("parent: expect %d,got %d\n",i,counter);
                        }

//                      TELL_PARENT();
                        if(write(fd2[1],"c",1) != 1)
                        {
                                printf("write error\n");
                        }
                }
        }

        return 0;
}


parent: expect 0,got 1
parent: expect 1,got 3
parent: expect 2,got 5
parent: expect 3,got 7
parent: expect 4,got 9
parent: expect 5,got 11
parent: expect 6,got 13
parent: expect 7,got 15
parent: expect 8,got 17
..............................
parent: expect 994,got 1989
parent: expect 995,got 1991
parent: expect 996,got 1993
parent: expect 997,got 1995
parent: expect 998,got 1997
parent: expect 999,got 1999



其实我想了半天还是没明白,异步IO和同步IO对这个demo的输出有什么改变
希望高手路过看见了,能帮忙讲讲。。。异步IO和同步IO对这个demo带来的影响




《APUE》chapter 15 Interprocess Communication 学习笔记(加上自己的代码)