Linux进程间通信总结

进程间通信的概念

每个进程各自有不同的用户地址空间,任何一个进程的变量在另一个进程中都是看不到的,所以进程之间要交换数据必须通过内核,在内核中开辟出一块缓冲区。一个进程把自己的数据从用户空间拷贝到内核缓冲区,另一个进程再从内核缓冲区把数据读走。内核提供的这种机制称为进程间通信(IPC,Inter Process Communication)

进程间通信的目的

数据传输:一个进程需要将它的数据发送给另一个进程,发送的数据量在一个字节到几M字节之间
共享数据:多个进程要操作共享数据,一个进程对共享数据
信息传递:一个进程需要向另一个进程发送消息,通知它发生了某种事件。
资源共享:多个进程之间共享同样的数据。为了做到这一点,需要内核提供锁和同步机制。
进程控制:有些进程希望完全控制另一个进程的执行,此时控制进程希望能够拦截另一个进程的所有陷入和异常,病能够及时知道它的状态改变

进程间通信的方式

信号 ( signal )

信号是unix系统响应某些条件而产生的一个事件,接收到该信号的进程会相应地采取一些行动。信号是由于某些错误条件而生成的,如内存段冲突、浮点处理器错误或非法指令等。它们由shell和终端处理器生成来引起中断,还可以作为在进程间传递消息或修改行为的一种方式,明确的由一个进程发送给另一个进程。信号可以被生成、捕获、响应或忽略。
信号名称定义位置:

include\linux\signal.h
*  +--------------------+------------------+
 *  |  POSIX signal      |  default action  |
 *  +--------------------+------------------+
 *  |  SIGHUP            |  terminate   |
 *  |  SIGINT            |  terminate   |
 *  |  SIGQUIT           |  coredump    |
 *  |  SIGILL            |  coredump    |
 *  |  SIGTRAP           |  coredump    |
 *  |  SIGABRT/SIGIOT    |  coredump    |
 *  |  SIGBUS            |  coredump    |
 *  |  SIGFPE            |  coredump    |
 *  |  SIGKILL           |  terminate(+)    |
 *  |  SIGUSR1           |  terminate   |
 *  |  SIGSEGV           |  coredump    |
 *  |  SIGUSR2           |  terminate   |
 *  |  SIGPIPE           |  terminate   |
 *  |  SIGALRM           |  terminate   |
 *  |  SIGTERM           |  terminate   |
 *  |  SIGCHLD           |  ignore      |
 *  |  SIGCONT           |  ignore(*)   |
 *  |  SIGSTOP           |  stop(*)(+)      |
 *  |  SIGTSTP           |  stop(*)     |
 *  |  SIGTTIN           |  stop(*)     |
 *  |  SIGTTOU           |  stop(*)     |
 *  |  SIGURG            |  ignore      |
 *  |  SIGXCPU           |  coredump    |
 *  |  SIGXFSZ           |  coredump    |
 *  |  SIGVTALRM         |  terminate   |
 *  |  SIGPROF           |  terminate   |
 *  |  SIGPOLL/SIGIO     |  terminate   |
 *  |  SIGSYS/SIGUNUSED  |  coredump    |
 *  |  SIGSTKFLT         |  terminate   |
 *  |  SIGWINCH          |  ignore      |
 *  |  SIGPWR            |  terminate   |
 *  |  SIGRTMIN-SIGRTMAX |  terminate       |
 *  +--------------------+------------------+

信号来源:硬件来源,比如我们按下了键盘或者其它硬件故障;软件来源,最常用发送信号的系统函数是kill, raise, alarm和setitimer以及sigqueue函数,软件来源还包括一些非法运算等操作。
进程对信号的响应:
进程可以通过三种方式来响应信号:(1)忽略信号,即对信号不做任何处理,但是有两个信号是不能忽略的:SIGKLL和SIGSTOP;(2)捕捉信号,定义信号处理函数,当信号发生时,执行相应的处理函数;(3)执行缺省操作,Linux对每种信号都规定了默认操作。
signal

sighandler_t signal(int signum, sighandler_t handler);

signal带有两个参数signum和handler,准备捕获或忽略的信号由参数signum给出,handler为接收到指定信号的回调函数。信号处理函数必须有int型参数返回类型为void。有两个特殊值代替信号处理函数。
SIG_IGN 忽略信号
SIG_DFL 恢复默认行为
demo1

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

#include <signal.h>
/*回调函数*/
void ouch(int sig)
{
    printf("OUCH! - I got signal %d\n", sig);
    (void) signal(SIGINT, SIG_DFL);
}

int main()
{
/*接收ctrl+c信号*/
    (void) signal(SIGINT, ouch);
    
    while(1) {
        printf("Hello World!\n");
        sleep(1);
    }
}

运行结果:
/*
$ ./ctrlc
Hello World!
Hello World!
Hello World!
Hello World!
^COUCH! - I got signal 2
Hello World!
Hello World!
Hello World!
Hello World!
Hello World!
Hello World!
Hello World!
^C
*/
  • kill
int kill(pid_t pid, int sig)

kill函数把参数sig发送给pid对应的进程,成功返回0失败返回-1.失败的原因可能为信号无效,发送进程权限不够,目标进程不存在
想要发送信号必须拥有相应权限

  • pause
int pause(void);

函数功能,把程序的执行挂起知道有一个信号出现为止

demo2
通过闹钟模拟程序,fork函数启动新进程,子进程休眠5S后向父进程发送SIGALRM信号

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <signal.h>

static int alarm_fired = 0;

void ding(int sig)
{
    alarm_fired = 1;
}

int main()
{
    pid_t pid;
    
    printf("alarm application starting\n");
    
    pid = fork();
    printf("pid:%d\n", pid);
    switch(pid) {
        case -1:
            perror("fork failed");
            exit(1);
        case 0:
            printf("child getppid:%d!\n",getppid());
            sleep(5);
            /*getpid返回当前进程标识,getppid返回父进程标识*/
            kill(getppid(), SIGALRM);
            exit(0);
    }
    printf("wait for alarm to go off\n");
    (void)signal(SIGALRM, ding);
    
    pause();
    
    if(alarm_fired)
        printf("Ding!\n");
    
    printf("done\n");
    exit(0);
}

/*
$ ./alarm
alarm application starting
pid:2437
wait for alarm to go off
pid:0
child getppid(): 2436!
Ding!
done
*/

sigaction

int sigaction(int signum, const struct sigaction *act,
                     struct sigaction *oldact);
struct sigaction {
               void     (*sa_handler)(int);
               void     (*sa_sigaction)(int, siginfo_t *, void *);
               sigset_t   sa_mask;
               int        sa_flags;
               void     (*sa_restorer)(void);
           };

demo3
替换demo1的api

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

#include <signal.h>

void ouch(int sig)
{
    printf("OUCH! - I got signal %d\n", sig);
}

int main()
{
    struct sigaction act;
    
    act.sa_handler = ouch;
    sigemptyset(&act.sa_mask);
    act.sa_flags = 0;
    
    sigaction(SIGINT, &act, 0);
    
    while(1) {
        printf("Hello World!\n");
        sleep(1);
    }
}

运行结果:
/*
$ ./ctrlc2
Hello World!
Hello World!
Hello World!
^COUCH! - I got signal 2
Hello World!
Hello World!
Hello World!
^COUCH! - I got signal 2
Hello World!
Hello World!
Hello World!
^COUCH! - I got signal 2
Hello World!
Hello World!
^_Hello World!
Hello World!
Hello World!
Hello World!
^\退出 (核心已转储)
*/

信号集的说明

NAME
       sigemptyset, sigfillset, sigaddset, sigdelset, sigismember - POSIX signal set operations

SYNOPSIS
       #include <signal.h>

       int sigemptyset(sigset_t *set);

       int sigfillset(sigset_t *set);

       int sigaddset(sigset_t *set, int signum);

       int sigdelset(sigset_t *set, int signum);

       int sigismember(const sigset_t *set, int signum);

   Feature Test Macro Requirements for glibc (see feature_test_macros(7)):

       sigemptyset(), sigfillset(), sigaddset(), sigdelset(), sigismember():
           _POSIX_C_SOURCE >= 1 || _XOPEN_SOURCE || _POSIX_SOURCE

DESCRIPTION
       These functions allow the manipulation of POSIX signal sets.

       sigemptyset() initializes the signal set given by set to empty, with all signals excluded from the set.

       sigfillset() initializes set to full, including all signals.

       sigaddset() and sigdelset() add and delete respectively signal signum from set.

       sigismember() tests whether signum is a member of set.

       Objects  of type sigset_t must be initialized by a call to either sigemptyset() or sigfillset() before being passed
       to the functions sigaddset(), sigdelset() and sigismember() or  the  additional  glibc  functions  described  below
       (sigisemptyset(), sigandset(), and sigorset()).  The results are undefined if this is not done.

RETURN VALUE
       sigemptyset(), sigfillset(), sigaddset(), and sigdelset() return 0 on success and -1 on error.
       sigismember() returns 1 if signum is a member of set, 0 if signum is not a member, and -1 on error.

       On error, these functions set errno to indicate the cause of the error.

ERRORS
       EINVAL sig is not a valid signal.

ATTRIBUTES
       For an explanation of the terms used in this section, see attributes(7).

       ┌────────────────────────────────┬───────────────┬─────────┐
       │Interface                       │ Attribute     │ Value   │
       ├────────────────────────────────┼───────────────┼─────────┤
       │sigemptyset(), sigfillset(),    │ Thread safety │ MT-Safe │
       │sigaddset(), sigdelset(),       │               │         │
       │sigismember(), sigisemptyset(), │               │         │
       │sigorset(), sigandset()         │               │         │
       └────────────────────────────────┴───────────────┴─────────┘
CONFORMING TO
       POSIX.1-2001, POSIX.1-2008.

NOTES
       When  creating a filled signal set, the glibc sigfillset() function does not include the two real-time signals used
       internally by the NPTL threading implementation.  See nptl(7) for details.

   Glibc extensions
       If the _GNU_SOURCE feature test macro is defined, then <signal.h> exposes three other  functions  for  manipulating
       signal sets:

       int sigisemptyset(const sigset_t *set);
       int sigorset(sigset_t *dest, const sigset_t *left,
                     const sigset_t *right);
       int sigandset(sigset_t *dest, const sigset_t *left,
                     const sigset_t *right);

       sigisemptyset() returns 1 if set contains no signals, and 0 otherwise.
 sigorset()  places  the  union of the sets left and right in dest.  sigandset() places the intersection of the sets
       left and right in dest.  Both functions return 0 on success, and -1 on failure.

       These functions are nonstandard (a few other systems provide similar functions) and their use should be avoided  in
       portable applications.

SEE ALSO
       sigaction(2), sigpending(2), sigprocmask(2), sigsuspend(2)

COLOPHON
       This  page is part of release 4.04 of the Linux man-pages project.  A description of the project, information about
       reporting bugs, and the latest version of this page, can be found at http://www.kernel.org/doc/man-pages/.

管道 pipe

管道从一个进程连接数据流到另一个进程,通常把一个进程的输出通过管道连接到另一个进程的输入。比如shell命令

cmd1 | cmd2

管道是一种半双工的通信方式,数据只能单向流动,而且只能在具有亲缘关系的进程间使用。进程的亲缘关系通常是指父子进程关系。
pipe

int pipe(int pipefd[2]);

DESCRIPTION
       pipe()  creates  a  pipe, a unidirectional data channel that can be used for interprocess communication.  The array
       pipefd is used to return two file descriptors referring to the ends of the pipe.  pipefd[0] refers to the read  end
       of the pipe.  pipefd[1] refers to the write end of the pipe.  Data written to the write end of the pipe is buffered
       by the kernel until it is read from the read end of the pipe.  For further details, see pipe(7).

demo1

#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <sys/wait.h>
 
void sys_err(const char *str)
{
    perror(str);
    exit(1);
}
 
int main(void)
{
    pid_t pid;
    char buf[1024];
    int fd[2];
    char *p = "test for pipe\n";
    
   if (pipe(fd) == -1) 
       sys_err("pipe");
 
   pid = fork();
   if (pid < 0) {
       sys_err("fork err");
   } else if (pid == 0) {
        close(fd[1]);
        int len = read(fd[0], buf, sizeof(buf));
        printf("read end.\n");
        write(STDOUT_FILENO, buf, len);
        printf("print end.\n");
        close(fd[0]);
   } else {
       close(fd[0]);
       write(fd[1], p, strlen(p));
       printf("write end.\n");
       wait(NULL);
       close(fd[1]);
   }
 
    return 0;
}

/*
运行结果:
$ ./pipe
write end.
read end.
test for pipe
print end.
*/

命名管道 FIFO

命名管道是一种特殊类型的文件,它在文件系统中以文件名的形式存在,但是他的行为与匿名管道类似。
命名管道也是半双工的通信方式,但是它允许无亲缘关系进程间的通信。
fifo

#include <unistd.h>
#include <stdlib.h>
#include <fcntl.h>
#include <limits.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <stdio.h>

#define FIFO_NAME "/tmp/my_fifo"

int main(int argc, char *argv[])
{
    int res;
    int open_mode = 0;
    int i;
    
    if(argc < 2) {
        fprintf(stderr, "Usage: %s\n", *argv);
        exit(EXIT_FAILURE);
    }

    for(i=1; i<argc; i++) {
        if(strncmp(*++argv, "O_RDONLY", 8) == 0)
            open_mode |= O_RDONLY;
        if(strncmp(*argv, "O_WRONLY", 8) == 0)
            open_mode |= O_WRONLY;
        if(strncmp(*argv, "O_NOBLOCK", 10) == 0)
            open_mode |= O_NONBLOCK;
    }

    if(access(FIFO_NAME, F_OK) == -1) {
        res = mkfifo(FIFO_NAME, 0777);
        if(res != 0) {
            fprintf(stderr, "Could not create fifo %s\n", FIFO_NAME);
            exit(EXIT_FAILURE);
        }
    }

    printf("Process %d opening FIFO\n", getpid());
    res = open(FIFO_NAME, open_mode);
    printf("Process %d result %d\n", getpid(), res);
    sleep(5);
    if (res != -1)
        (void)close(res);
    printf("Process %d finished\n", getpid());
    exit(EXIT_SUCCESS);
}

/*
./fifo O_RDONLY &
./fifo O_WRONLY

运行结果:
./fifo2 O_RDONLY &
[1] 5405
Process 5405 opening FIFO
./fifo2 O_WRONLY
Process 5406 opening FIFO
Process 5405 result 3
Process 5406 result 3
Process 5405 finished
Process 5406 finished
[1]+  已完成               ./fifo2 O_RDONLY
*/

system v IPC
由于这些机制出现在同一个版本中并且有相似的编程接口,他们又被称为IPC机制,或被更常见的称为System V IPC
分类 创建函数 控制函数 独立函数
消息队列 msgget msgctl msgsnd,msgrcv
信号量 semget semctl semop
共享内存 shmget shmctl shmat,shmdt

信号量Semaphore

信号量是一个计数器,可以用来控制多个进程对共享资源的访问。它常作为一种锁机制,防止某进程正在访问共享资源时,其他进程也访问该资源。因此,主要作为进程间以及同一进程内不同线程之间的同步手段。
semget

int semget(key_t key, int nsems, int semflg);
key:

nsems:

semflg:

semop

union semun {
    int val;
    struct semid_ds *buf;
    unsigned short *array;
};







semctl


demo

#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <sys/sem.h>

union semun {
    int val;
    struct semid_ds *buf;
    unsigned short *array;
};

static int set_semvalue(void);
static void del_semvalue(void);
static int semaphore_p(void);
static int semaphore_v(void);

static int sem_id;

int main(int argc, char *argv[])
{
    int i;
    int pause_time;
    char op_char = 'O';
    
    srand((unsigned int)getpid());
    
    sem_id = semget((key_t)1234, 1, 0666 | IPC_CREAT);
    
    if(argc > 1) {
        if(!set_semvalue()) {
            fprintf(stderr, "Failed to init semaphore\n");
            exit(EXIT_FAILURE);
        }
        op_char = 'X';
        sleep(2);
    }
    
    for(i = 0; i < 10; i++) {
        if(!semaphore_p())
            exit(EXIT_FAILURE);
        printf("%c", op_char);
        fflush(stdout);
        pause_time = rand() % 3;
        sleep(pause_time);
        printf("%c", op_char);
        fflush(stdout);
        if(!semaphore_v())
            exit(EXIT_FAILURE);
        pause_time = rand() % 2;
        sleep(pause_time);
    }
    printf("\n%d - finished\n", getpid());
    
    if(argc > 1) {
        sleep(10);
        del_semvalue();
    }
    exit(EXIT_SUCCESS);
}

static int set_semvalue(void)
{
    union semun sem_union;
    
    sem_union.val = 1;
    if(semctl(sem_id, 0, SETVAL, sem_union) == -1)
        return 0;
    
    return 1;
}

static void del_semvalue(void)
{
    union semun sem_union;
    
    if(semctl(sem_id, 0, IPC_RMID, sem_union) == -1)
        fprintf(stderr, "Failed to delete semaphore\n");
}

static int semaphore_p(void)
{
    struct sembuf sem_b;
    
    sem_b.sem_num = 0;
    sem_b.sem_op = -1;
    sem_b.sem_flg = SEM_UNDO;
    if (semop(sem_id, &sem_b, 1) == -1) {
        fprintf(stderr, "semaphore_p failed\n");
        return 0;
    }
    return 1;
}

static int semaphore_v(void)
{
    struct sembuf sem_b;
    
    sem_b.sem_num = 0;
    sem_b.sem_op = 1;
    sem_b.sem_flg = SEM_UNDO;
    if (semop(sem_id, &sem_b, 1) == -1) {
        fprintf(stderr, "semaphore_p failed\n");
        return 0;
    }
    return 1;    
}

/*
./sem 1 &
[3] 5553
[2]   已完成               ./sem 1
./sem
OOXXOOXXOOXXXXOOXXXXOOXXOOXXOOXXOOXXO
5553 - finished
OOO
5554 - finished
*/

共享存储SharedMemory

共享内存就是映射一段能被其他进程所访问的内存,这段共享内存由一个进程创建,但多个进程都可以访问。共享内存是最快的 IPC 方式,它是针对其他进程间通信方式运行效率低而专门设计的。它往往与其他通信机制,如信号量,配合使用,来实现进程间的同步和通信。

shmget

shmat

shmdt

shmctl

shm_com.h

#ifndef SHM_COM_H
#define SHM_COM_H

#define TEXT_SZ 2048
struct shared_use_st {
    int written_by_you;
    char some_text[TEXT_SZ];
};

#endif

shm1.c

/* 消费者 */
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>

#include <sys/ipc.h>
#include <sys/shm.h>

#include "shm_com.h"

int main()
{
    int running = 1;
    void *shared_memory = (void *)0;
    struct shared_use_st *shared_stuff;
    int shmid;
    
    srand((unsigned int)getpid());
/*1.创建共享内存*/    
    shmid = shmget((key_t)1234, sizeof(struct shared_use_st), 0666 | IPC_CREAT);
    
    if(shmid == -1) {
        fprintf(stderr, "shmget failed!\n");
        exit(EXIT_FAILURE);
    }
/*2.让程序可以访问这个共享内存*/    
    shared_memory = shmat(shmid, (void *)0, 0);
    if(shared_memory == (void *)-1) {
        fprintf(stderr, "shmat failed!\n");
        exit(EXIT_FAILURE);        
    }
    
    printf("Memory attached at %x\n", (int)shared_memory);
/*3.分配共享内存给shared_stuff*/    
    shared_stuff = (struct shared_use_st *)shared_memory;
    shared_stuff->written_by_you = 0; 
    while(running) {
        if (shared_stuff->written_by_you) {
            printf("You wrote: %s", shared_stuff->some_text);
            sleep(rand() % 4);
            shared_stuff->written_by_you = 0;
            if(strncmp(shared_stuff->some_text, "end", 3) == 0) {
                running = 0;
            }
        }
    }
/*5.分离删除共享内存*/    
    if (shmdt(shared_memory) == -1) {
        fprintf(stderr, "shmdt failed!\n");
        exit(EXIT_FAILURE);
    }
    
    if (shmctl(shmid, IPC_RMID, 0) == -1) {
        fprintf(stderr, "shmctl failed!\n");
        exit(EXIT_FAILURE);
    }
    
    exit(EXIT_SUCCESS);
}

shm2.c

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

#include <sys/ipc.h>
#include <sys/shm.h>

#include "shm_com.h"

int main()
{
    int running = 1;
    void *shared_memory = (void *)0;
    struct shared_use_st *shared_stuff;
    int shmid;
    char buffer[BUFSIZ];
    
    srand((unsigned int)getpid());
    
    shmid = shmget((key_t)1234, sizeof(struct shared_use_st), 0666 | IPC_CREAT);
    
    if(shmid == -1) {
        fprintf(stderr, "shmget failed!\n");
        exit(EXIT_FAILURE);
    }
    
    shared_memory = shmat(shmid, (void *)0, 0);
    if(shared_memory == (void *)-1) {
        fprintf(stderr, "shmat failed!\n");
        exit(EXIT_FAILURE);        
    }
    
    printf("Memory attached at %x\n", (int)shared_memory);
    
    shared_stuff = (struct shared_use_st *)shared_memory;
    shared_stuff->written_by_you = 0;
    while(running) {
        while (shared_stuff->written_by_you == 1) {
            sleep(1);
            printf("waiting for client...\n");
        }
        printf("Enter some text: ");
        fgets(buffer, BUFSIZ, stdin);
        
        strncpy(shared_stuff->some_text, buffer, TEXT_SZ);
        shared_stuff->written_by_you = 1;
        if(strncmp(shared_stuff->some_text, "end", 3) == 0) {
            running = 0;
        }
    }
    
    if (shmdt(shared_memory) == -1) {
        fprintf(stderr, "shmdt failed!\n");
        exit(EXIT_FAILURE);
    }
    
    exit(EXIT_SUCCESS);
}

消息队列MessageQueue

消息队列是由消息的链表,存放在内核中并由消息队列标识符标识。消息队列克服了信号传递信息少、管道只能承载无格式字节流以及缓冲区大小受限等缺点。

msgget


msgsnd


msgrcv


msgctl


msg2.c

/* 生产者 */
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>

#include <sys/msg.h>

#define MAX_TEXT 512

struct my_msg_st {
    long int my_msg_type;
    char some_text[BUFSIZ];
};

int main()
{
    int running = 1;
    long int msg_to_receive = 0;
    struct my_msg_st some_data;
    int msgid;
    char buffer[BUFSIZ];
    
/*1.创建消息队列*/    
    msgid = msgget((key_t)1234, 0666 | IPC_CREAT);
    
    if(msgid == -1) {
        fprintf(stderr, "msgget failed!\n");
        exit(EXIT_FAILURE);
    }

/*2.分配共享内存给shared_stuff*/    
    while(running) {
        printf("Enter some text: ");
        fgets(buffer, BUFSIZ, stdin);
        
        some_data.my_msg_type = 1;
        strcpy(some_data.some_text, buffer);
        if (msgsnd(msgid, (void *)&some_data, MAX_TEXT, 0) == -1) {
            fprintf(stderr, "msgsnd failed with error: %d\n", errno);
            exit(EXIT_FAILURE);
        }
        
        if(strncmp(some_data.some_text, "end", 3) == 0) {
            running = 0;
        }
    }
    
    exit(EXIT_SUCCESS);
}

msg1.c

/* 消费者 */
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>

#include <sys/msg.h>

struct my_msg_st {
    long int my_msg_type;
    char some_text[BUFSIZ];
};

int main()
{
    int running = 1;
    long int msg_to_receive = 0;
    struct my_msg_st some_data;
    int msgid;
    
/*1.创建消息队列*/    
    msgid = msgget((key_t)1234, 0666 | IPC_CREAT);
    
    if(msgid == -1) {
        fprintf(stderr, "msgget failed!\n");
        exit(EXIT_FAILURE);
    }

/*3.分配共享内存给shared_stuff*/    
    while(running) {
        if (msgrcv(msgid, (void *)&some_data, BUFSIZ, msg_to_receive, 0) == -1) {
            fprintf(stderr, "msgget failed with error: %d\n", errno);
            exit(EXIT_FAILURE);
        }
        printf("You wrote: %s", some_data.some_text);
        
        if(strncmp(some_data.some_text, "end", 3) == 0) {
            running = 0;
        }
    }
    
    if (msgctl(msgid, IPC_RMID, 0) == -1) {
        fprintf(stderr, "msgid failed!\n");
        exit(EXIT_FAILURE);
    }
    
    exit(EXIT_SUCCESS);
}

/*
$ ./msg2
Enter some text: 1111111111111
Enter some text: eee
Enter some text: rrr
Enter some text: ttt
Enter some text: ggg
Enter some text: end
$ ./msg1
You wrote: 1111111111111
You wrote: eee
You wrote: rrr
You wrote: ttt
You wrote: ggg
You wrote: end
*/

套接字 socket

套解口也是一种进程间通信机制,与其他通信机制不同的是,它可用于不同机器间的进程通信。
套接字明确的区分开了客户和服务器。套接字的机制实现了将多个客户连接到一个服务器。
服务器端操作:
创建套接字
socket
绑定地址
bind
接受连接请求
listen

客户端操作:
创建套接字
socket
调用connect与服务器建立连接
connect

demo
服务器

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

#include <sys/socket.h>
#include <sys/types.h> 
#include <sys/un.h> 

int main()
{
    int server_sockfd, client_sockfd;
    int server_len, client_len;
    struct sockaddr_un server_address, client_address;

    unlink("server_socket");
    server_sockfd = socket(AF_UNIX, SOCK_STREAM, 0);
    
    server_address.sun_family = AF_UNIX;
    strcpy(server_address.sun_path, "server_socket");
    server_len = sizeof(server_address);
    
    bind(server_sockfd, (struct sockaddr *)&server_address, server_len);
    
    listen(server_sockfd, 5);
    
    while(1) {
        char ch;
        
        printf("server waiting\n");
        client_len = sizeof(client_address);
        client_sockfd = accept(server_sockfd,
            (struct sockaddr *)&client_address, &client_len);
        read(client_sockfd, &ch, 1);
        ch++;
        write(client_sockfd, &ch, 1);
        close(client_sockfd);
    }
    
    exit(0);
}

客户端

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

#include <sys/socket.h>
#include <sys/types.h> 
#include <sys/un.h> 

int main()
{
    int sockfd;
    int len;
    struct sockaddr_un address;
    int result;
    char ch = 'A';
/*为客户端创建套接字*/    
    sockfd = socket(AF_UNIX, SOCK_STREAM, 0);

    address.sun_family = AF_UNIX;
    strcpy(address.sun_path, "server_socket");
    len = sizeof(address);
    
    result = connect(sockfd, (struct sockaddr *)&address, len);
    if (result == -1) {
        perror("oops: client1");
        exit(1);
    }
    
    write(sockfd, &ch, 1);
    read(sockfd, &ch, 1);
    printf("char from server = %c\n", ch);
    close(sockfd);
    
    exit(0);
}

进程间通信的总结

优点 缺点
信号 signal

管道 pipe 简单方便 局限于单向通信
有名管道 fifo 提供给任意关系的进程使用 长期存在于系统之中,使用不当容易出错
信号量sem 信号量是一个计数器,可以用来控制多个进程对共享资源的访问。它常作为一种锁机制,防止某进程正在访问共享资源时,其他进程也访问该资源。因此,主要作为进程间以及同一进程内不同线程之间的同步手段

共享内存shm 共享内存是最快的IPC方式,它是针对其他进程间通信方式运行效率低而专门设计的。它往往与其他通信机制,如信号量,配合使用,来实现进程间的同步和通信。

消息队列msg 消息队列克服了信号传递信息少、管道只能承载无格式字节流以及缓冲区大小受限等缺点。

套接字 socket 可用于不同机器间的进程通信

参考资料

linux程序设计(第四版)

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
Linux中,进程间通信可以使用socket来实现。Socket是一种特殊的文件,它是应用层与TCP/IP协议族通信的中间软件抽象层,提供了一组简单的接口来组织数据,以符合指定的协议。在网络编程中,大部分的通信都是通过socket实现的。 使用TCP/IP协议的应用程序通常采用socket接口来实现网络进程之间的通信。无论是UNIX BSD的套接字还是UNIX System V的TLI(已经被淘汰),几乎所有的应用程序都是采用socket来进行通信。 此外,还有一种叫做Unix domain sockets的通信方式,它使用系统文件的地址作为进程间通信的身份,并且仅在系统内核内部进行通信,不会在网络中传播。两个进程可以同时打开一个Unix domain socket来进行通信。 总结来说,Linux中的进程间通信可以通过socket来实现,使用TCP/IP协议的应用程序通常采用socket接口进行通信,并且还可以使用Unix domain sockets进行通信。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* [LINUX进程间网络通信--SOCKET](https://blog.csdn.net/qq_44370382/article/details/107959541)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] - *3* [About AF_LOCAL in Linux](https://blog.csdn.net/frank_jb/article/details/77199834)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值