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.
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] .
#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] .
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!
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
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"); } }
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
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.
#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
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 >
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
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
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
------ 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_SETSet 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_RMIDRemove 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 nolonger 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
array[] from 6010a0 to 602040
stack around 7fffd110aa2c
malloced from 1a54010to 1a6c6b0
shared memory attached from 7fc8fa190000 to 7fc8fa1a86a0
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
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
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带来的影响