进程间的通讯方式

1. 共享内存

#include
#include
#include

int main ()
{
 int segment_id;
 char* shared_memory;
 struct shmid_ds shmbuffer;
 int segment_size;
 const int shared_segment_size = 0x6400;

 /* Allocate a shared memory segment. */
 segment_id = shmget (IPC_PRIVATE, shared_segment_size, IPC_CREAT | IPC_EXCL | S_IRUSR | S_IWUSR);
 /* Attach the shared memory segment. */
 printf ("the segment_id: %d/n", segment_id);
 shared_memory = (char*) shmat (segment_id, 0, 0);
 printf ("shared memory attached at address %p/n", shared_memory);
 /* Determine the segment’s size. */
 shmctl (segment_id, IPC_STAT, &shmbuffer);
 segment_size = shmbuffer.shm_segsz;
 printf ("segment size: %d/n", segment_size);
 /* Write a string to the shared memory segment. */
 sprintf (shared_memory, "Hello, world.");
 /* Detach the shared memory segment. */
 shmdt (shared_memory);
 /* Reattach the shared memory segment, at a different address. */
 shared_memory = (char*) shmat (segment_id, (void*) 0x5000000, 0);
 printf ("shared memory reattached at address %p/n", shared_memory);
 /* Print out the string from shared memory. */
 printf ("%s/n", shared_memory);
 /* Detach the shared memory segment. */
 shmdt (shared_memory);
 /* Deallocate the shared memory segment. */
 shmctl (segment_id, IPC_RMID, 0);
 return 0;
}

2. mapped memory

#include
#include
#include
#include
#include
#include
#include
#define FILE_LENGTH 0x100

/* Return a uniformly random number in the range [low, high]. */
int random_range (unsigned const low, unsigned const high)
{
 unsigned const range = high - low + 1;
 return low + (int)(((double) range) * rand() / (RAND_MAX + 1.0));
}

int main (int argc, char* const argv[])
{
 int fd;
 void* file_memory;

 /* Seed the random number generator. */
 srand (time (NULL));

 /* Prepare a file large enough to hold an unsigned integer. */
 fd = open (argv[1], O_RDWR | O_CREAT, S_IRUSR | S_IWUSR);
 lseek (fd, FILE_LENGTH+1, SEEK_SET);

 write (fd, "", 1);
 lseek (fd, 0, SEEK_SET);
 /* Create the memory mapping. */
 file_memory = mmap (0, FILE_LENGTH, PROT_WRITE, MAP_SHARED, fd, 0);
 close (fd);
 /* Write a random integer to memory-mapped area. */
 sprintf((char*) file_memory, "%d/n", random_range (-100, 100));
 /* Release the memory (unnecessary because the program exits). */
 munmap (file_memory, FILE_LENGTH);
 return 0;
}  
3. Pipes (管道)

管道是一种允许单向通讯的通讯设备. 数据写入写入端, 而从读出端读出. 数据以写入时的相应顺序被读出. 典型的,管道可以用于 一个进程中的两个线程 或 父进程和子进程之间 的通讯.  管道具有以下特性---1  管道的数据容量是有限的. 在写数据的时候, 如果没有空间可写, 那么写进程被阻塞, 直到有空间; 同样读数据的时候, 如果没有数据,都进程同样被阻塞, 知道有数据可读. 因此 管道是自动同步两个进程的 (写进程 和 读进程).  2 对于管道文件描述符的调用, 只有在创建它的进程及子进程中才是有效的. 一个进程的文件描述符是不能够传递给不相关进程的. 然而当一个进程调用 fork 时, 文件描述符被拷贝到子进程. 因此, 管道只能链接有关系的进程.

以下是个应用示例:

#include
#include
#include

/* Write COUNT copies of MESSAGE to STREAM, pausing for a second between each. */
void writer (const char* message, int count, FILE* stream)
{
 for (; count > 0; --count) {
  printf ("In writer function. /n");
  /* Write the message to the stream, and send it off immediately. */
  fprintf (stream, "%s/n", message);
  fflush (stream);
  /* Snooze a while. */
  sleep (1);
 }
}

/* Read random strings from the stream as long as possible. */
void reader (FILE* stream)
{
 char buffer[1024];
 /* Read until we hit the end of the stream. fgets reads until
 either a newline or the end-of-file. */
 while (!feof (stream)
 && !ferror (stream)
 && fgets (buffer, sizeof (buffer), stream) != NULL){
  printf ("===/n");
  fputs (buffer, stdout);
  printf ("In reader function./n");
 }
}

int main ()
{
 int fds[2];
 pid_t pid;
 /* Create a pipe. File descriptors for the two ends of the pipe are
 placed in fds. */
 pipe (fds);
 /* Fork a child process. */
 pid = fork ();
 if (pid == (pid_t) 0) {
  FILE* stream;

  printf ("In the child process. /n");
  /* This is the child process. Close our copy of the write end of
  the file descriptor. */
  close (fds[1]);
  /* Convert the read file descriptor to a FILE object, and read
  from it. */
  stream = fdopen (fds[0], "r");
  reader (stream);
  close (fds[0]);
 }
 else {
  /* This is the parent process. */
  FILE* stream;

  printf ("In the parent process. /n");
  /* Close our copy of the read end of the file descriptor. */
  close (fds[0]);
  /* Convert the write file descriptor to a FILE object, and write
  to it. */
  stream = fdopen (fds[1], "w");
  writer ("Hello, world.", 5, stream);
  close (fds[1]);
 }
 return 0;
}

应用中的一种情况: 重定向标准 输入, 输出 和 错误流

你经常可能遇到创建一个子进程, 然后把管道的一端作为它的标准输入 或 输出. 调用 dup2 , 你可以把一个文件描述符 和 另一个文件描述符等同起来. 例如, 把一个进程的标准输入重定向到一个文件描述符 fd, 可以这样做:

dup2 (fd, STDIN_FILENO);

#include
#include
#include
#include

int main ()
{
 int fds[2];
 pid_t pid;
 /* Create a pipe. File descriptors for the two ends of the pipe are
 placed in fds. */
 pipe (fds);
 /* Fork a child process. */
 pid = fork ();
 if (pid == (pid_t) 0) {
  /* This is the child process. Close our copy of the write end of
  the file descriptor. */
  close (fds[1]);
  /* Connect the read end of the pipe to standard input. */
  dup2 (fds[0], STDIN_FILENO);
  /* Replace the child process with the “sort” program. */
  execlp ("sort", "sort", 0);
 }
 else {
  /* This is the parent process. */
  FILE* stream;
  /* Close our copy of the read end of the file descriptor. */
  close (fds[0]);
  /* Convert the write file descriptor to a FILE object, and write
  to it. */
  stream = fdopen (fds[1], "w");
  fprintf (stream, "This is a test./n");
  fprintf (stream, "Hello, world./n");
  fprintf (stream, "My dog has fleas./n");
  fprintf (stream, "This program is great./n");
  fprintf (stream, "One fish, two fish./n");
  fflush (stream);
  close (fds[1]);
  /* Wait for the child process to finish. */
  waitpid (pid, NULL, 0);
 }
 return 0;
}

针对以上应用 (从子进程接受数据 或 向子进程发送数据) 有种更为简单的方法,利用 popen, pclose 方法.

#include
#define MAXSTRS 5

int main(void)
{
 int cntr;
 FILE *pipe_fp;
 char *strings[MAXSTRS] = {"echo", "bravo", "alpha", "charlie", "delta"};
 
 /* Create one way pipe line with call to popen() */
 if((pipe_fp = popen("sort", "w")) == NULL)
 {
  perror("popen");
  exit(1);
 }

 /* Processing loop */
 for(cntr=0; cntr  fputs(strings[cntr], pipe_fp);
  fputc('/n', pipe_fp);
 }

 /* Close the pipe */
 pclose(pipe_fp);
 return(0);
}
popen ("sort", "w") 创建一个子进程执行 sort 命令, "w" 参数表明, 此进程想要发送数据给子进程. popen 返回管道的一端, 另一端被连接到子进程的标准输入端. 当写操作完成的时候, pclose 关闭了子进程的流, 等待子进程的结束.  如果第二个参数是 "r" , 那么popen返回子进程的标准输出流, 父进程就可以从此流读出数据. 如果有错误发生, popen 将会返回 NULL 指针.

4 FIFOs

A first-in, first-out (FIFO) file is a pipe that has a name in the filesystem. 任何进程都可以打开或关闭 FIFO; 管道两端的进程不需要有关系 (例如, 父子关系). FIFOs 亦可称作 named pipes (命名管道).  一个 FIFO 可以有多个 reader 或 writer.

#include
#include
#include
#include ]
#include
#define FIFO_FILE "MYFIFO"

int main(void)
{
 FILE *fp;
 char readbuf[80];
 /* Create the FIFO if it does not exist */
 umask(0);
 mknod(FIFO_FILE, S_IFIFO|0666, 0);
 while(1)
 {
  printf("i am zhaoyognliang!/n");
  fp = fopen(FIFO_FILE, "r");
  fgets(readbuf, 80, fp);
  printf("Received string: %s/n", readbuf);
  fclose(fp);
 }
 return(0);
}

#include
#include
#define FIFO_FILE "MYFIFO"

int main(int argc, char* argv[])
{
 FILE *fp;
 if(argc!=2){
  printf("USAGE:fifoclient [string]/n");
  exit(1);
 }
 if(NULL == (fp = fopen(FIFO_FILE, "w"))){
  perror("fopen");
  exit(1);
 }
 fputs(argv[1], fp);
 fclose(fp);
 return(0);
}

5. Sockets

Socket 是一个双向通信设备,可以用来和在同一机器或是不同机器上的进程通信. 当创建socket的时候, 你必须指定三个参数: communication style, namespace, and protocol. communication style 控制socket怎样对待传输数据 和 定义通讯伙伴的数量. 当数据通过socket发送的时候, 数据被分割成一块块. (packets) Communication style 决定这些数据包怎么被处理 以及是怎么标识的. 有两种communication style: 1. Connection 2. Datagram .( 具体含义请查阅其它文献资料.) For the communication style parameter, useconstants beginning with SOCK_. Use SOCK_STREAM for a connection-style socket, or use SOCK_DGRAM for a datagram-style socket.

namespace 定义socket地址怎么书写. 有两种namespace : 1 local namespace. 2 internet namespace.   For the namespace parameter, use constants beginning with PF_ (abbreviating“protocol families”). For example, PF_LOCAL or PF_UNIX specifies the local namespace,and PF_INET specifies Internet namespaces.

protocol 指定数据如何传输. 有以下协议: 1. TCP/IP 2. AppleTalk network protocol 3. UNIX local communication protocol.

Related System Calls:

socket—Creates a socket
closes—Destroys a socket
connect—Creates a connection between two sockets
bind—Labels a server socket with an address
listen—Configures a socket to accept conditions
accept—Accepts a connection and creates a new socket for the connection

注意: 只有运行在同一机器上的进程才能用 local namespace sockets 进行通信.

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <unistd.h>

/* Read text from the socket and print it out. Continue until the
socket closes. Return nonzero if the client sent a “quit”
message, zero otherwise. */
int server (int client_socket)
{
 while (1) {
  int length;
  char* text;
  /* First, read the length of the text message from the socket. If
  read returns zero, the client closed the connection. */
  if (read (client_socket, &length, sizeof (length)) == 0)
   return 0;
  /* Allocate a buffer to hold the text. */
  text = (char*) malloc (length);
  /* Read the text itself, and print it. */
  read (client_socket, text, length);
  printf ("%s/n", text);

  /* If the client sent the message “quit,” we’re all done. */
  if (!strcmp (text, "quit")){
   free (text);
   return 1;
  }
     /* Free the buffer. */
  free (text);
 }
}

int main (int argc, char* const argv[])
{
 const char* const socket_name = argv[1];
 int socket_fd;
 struct sockaddr_un name;
 int client_sent_quit_message;
 /* Create the socket. */
 socket_fd = socket (PF_LOCAL, SOCK_STREAM, 0);
 /* Indicate that this is a server. */
 name.sun_family = AF_LOCAL;
 strcpy (name.sun_path, socket_name);
 bind (socket_fd, &name, SUN_LEN (&name));
 /* Listen for connections. */
 listen (socket_fd, 5);
 /* Repeatedly accept connections, spinning off one server() to deal
 with each client. Continue until a client sends a “quit” message. */
 do {
  struct sockaddr_un client_name;
  socklen_t client_name_len;
  int client_socket_fd;
  /* Accept a connection. */
  client_socket_fd = accept (socket_fd, &client_name, &client_name_len);
  /* Handle the connection. */
  client_sent_quit_message = server (client_socket_fd);
  /* Close our end of the connection. */
  close (client_socket_fd);
 }
 while (!client_sent_quit_message);
 /* Remove the socket file. */
 close (socket_fd);
 unlink (socket_name);
 return 0;
}

#include <stdio.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <unistd.h>

/* Write TEXT to the socket given by file descriptor SOCKET_FD. */
void write_text (int socket_fd, const char* text)
{
 /* Write the number of bytes in the string, including
 NUL-termination. */
 int length = strlen (text) + 1;
 write (socket_fd, &length, sizeof (length));
 /* Write the string. */
 write (socket_fd, text, length);
}

int main (int argc, char* const argv[])
{
 const char* const socket_name = argv[1];
 const char* const message = argv[2];
 int socket_fd;
 struct sockaddr_un name;
 /* Create the socket. */
 socket_fd = socket (PF_LOCAL, SOCK_STREAM, 0);
 /* Store the server’s name in the socket address. */
 name.sun_family = AF_LOCAL;
 strcpy (name.sun_path, socket_name);
 /* Connect the socket. */
 connect (socket_fd, &name, SUN_LEN (&name));
 /* Write the text on the command line to the socket. */
 write_text (socket_fd, message);
 close (socket_fd);
 return 0;
}

 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值