Linux进程间通信(Linux IPC)

Linux 进程间通信 Linux进程通信继承了传统的UNIX进程间通信方式,包括无名管道、有名管道以及信号;还有System V进程间通信(IPC),包括System V消息队列、System V信号量以及System V共享内存;套接字。 阅读详情

前言

        Linux环境下,进程地址空间相互独立、彼此隔离,因此进程间的数据不能之间访问。如果要交换数据,必须要通过内核,在内核中开辟一块缓冲区,进程A把数据从用户空间拷贝到内核缓冲区,进程B再把数据从内核缓冲区拷贝走,内核提供的这种机制称为进程间通信(IPC, InterProcess Communication)

在这里插入图片描述

         在Linux下由很多种进程间通信的方式,在下面主要介绍八种,分别是:匿名管道(PIPE)、命名管道(FIFO)、信号、共享内存映射、共享内存、消息队列、信号量、UNIX域套接字。现在常用的进程间通信方式有:

  1. 管道(使用简单)
  2. 信号(开销最小)
  3. 共享内存(效率最高)
  4. UNIX域套接字(最稳定)

        

1、匿名管道(PIPE)

        管道是一种最基本的IPC,用于有血缘关系的进程之间。使用的系统调用如下:

int pipe(int pipefd[2]);

管道有如下特征:

  • 本质是一个伪文件(实为内核缓冲区)
  • 由两个文件描述符引用,一个表示写端,一个表示读端
  • 数据从管道的写端流入,从读端流出
  • 采用半双工通信方式,数据只能从一个方向上流动。如果需要全双工,可以创建两个管道。

**管道的原理:**管道实为内核使用环形队列机制,借助内核缓冲区实现(大小4K)

父子进程通过管道通信:

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

int main() {
    int fds[2];
    pid_t pid;
    int ret;
    char buf[1024];

    if (pipe(fds) < 0) {
        perror("open pipe error:");
        exit(1);
    }
    pid = fork();
    if (pid < 0) {
        perror("fork error:");
        exit(2);
    } else if (pid > 0) {  // parend process
        // parent process write to pipe
        close(fds[0]);   // close read end
        ret = write(fds[1], "I am your father", strlen("I am your father"));
        if (ret < 0) {
            perror("parent write pipe error:");
            exit(3);
        }
        printf("parent process id:%u, son:%u, write success\n", getpid(), pid);

        wait(NULL);
    } else {  // child process
        // child process read from pipe
        close(fds[1]);           // close wirte end
        ret = read(fds[0], buf, sizeof(buf));
        if (ret < 0) {
            perror("child read pipe error:");
            exit(3);
        }
        buf[ret] = '\0';
        printf("child process id:%u, father:%u, read from pipe:%s\n", getpid(), getppid(), buf);
    }
    
    return 0;
}

运行结果:

parent process id:2663, son:2664, write success
child process id:2664, father:2663, read from pipe:I am your father

管道读写的注意事项:

读管道:

  • 管道中有数据,读出数据,返回读到的字节数
  • 管道中无数据:①如果写端被关闭,read返回0 ②写端没有关闭,read阻塞

写管道:

  • 管道读端全部关闭:进程异常终止(收到SIGPIPE信号,最好捕捉SIGPIPE信号,是进程不终止)
  • 管道读端没有全部关闭:①管道已满,write阻塞 ②管道未满,将数据写入,返回写入的字节数

匿名管道的优劣:

优点:使用简单

缺点:①只能单向通信 ②只能用于父子、兄弟进程间通信

        

2、命名管道(FIFO)

        FIFO常被称为命名管道,以区分管道(PIPE)。PIPE只能用于有"血缘关系"的进程间通信,但FIFO可以在不相关的进程间实现通信。

        FIFO是Linux基础文件中的一种,但FIFO在磁盘上没有数据块,仅仅用来标识内核中的一条通道。各进程可以打开这个文件进行read/write,实际上是在读写内核通道,这样就实现了进程间通信。

函数原型:

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

        一旦使用mkfifo创建了一个FIFO,就可以使用open打开它,常见的文件I/O函数都可用于fifo。如:close、read、write、unlink等。

FIFO进程间通信案例:

/* reader.c  */
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <stdlib.h>
#include <fcntl.h>

int main() {
    int fd;
    char buf[1024];
    int n;
    
    if (mkfifo("/tmp/myfifo", 0644) < 0) {
        perror("mkfifo error:");
        exit(1);
    }

    if ((fd = open("/tmp/myfifo", O_RDONLY)) < 0) {
        perror("open fifo error:");
        exit(1);
    }
    if ( ( n = read(fd, buf, sizeof(buf))) < 0) {
        perror("read msg error:");
        exit(1);
    }
    buf[n] = '\0';

    printf("msg:%s\n", buf);

    unlink("/tmp/myfifo");
    return 0;
}

/* writer.c */
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <stdlib.h>
#include <fcntl.h>
#include <string.h>

int main() {
    int fd;
    const char *msg = "hello from writer"; 

    if ((fd = open("/tmp/myfifo", O_WRONLY)) < 0) {
        perror("open fifo error:");
        exit(1);
    }
    if (write(fd, msg, strlen(msg)) < 0) {
        perror("write msg error:");
        exit(1);
    }

    return 0;
}

        

3、信号

        信号是由用户、系统或进程发送给目标进程的信息,以通知目标进程某个状态的改变或系统异常。

在shell终端输入kill -l 来查看支持的信号:

[root@master ~]# kill -l
 1) SIGHUP       2) SIGINT       3) SIGQUIT      4) SIGILL       5) SIGTRAP
 6) SIGABRT      7) SIGBUS       8) SIGFPE       9) SIGKILL     10) SIGUSR1
11) SIGSEGV     12) SIGUSR2     13) SIGPIPE     14) SIGALRM     15) SIGTERM
16) SIGSTKFLT   17) SIGCHLD     18) SIGCONT     19) SIGSTOP     20) SIGTSTP
21) SIGTTIN     22) SIGTTOU     23) SIGURG      24) SIGXCPU     25) SIGXFSZ
26) SIGVTALRM   27) SIGPROF     28) SIGWINCH    29) SIGIO       30) SIGPWR
31) SIGSYS      34) SIGRTMIN    35) SIGRTMIN+1  36) SIGRTMIN+2  37) SIGRTMIN+3
38) SIGRTMIN+4  39) SIGRTMIN+5  40) SIGRTMIN+6  41) SIGRTMIN+7  42) SIGRTMIN+8
43) SIGRTMIN+9  44) SIGRTMIN+10 45) SIGRTMIN+11 46) SIGRTMIN+12 47) SIGRTMIN+13
48) SIGRTMIN+14 49) SIGRTMIN+15 50) SIGRTMAX-14 51) SIGRTMAX-13 52) SIGRTMAX-12
53) SIGRTMAX-11 54) SIGRTMAX-10 55) SIGRTMAX-9  56) SIGRTMAX-8  57) SIGRTMAX-7
58) SIGRTMAX-6  59) SIGRTMAX-5  60) SIGRTMAX-4  61) SIGRTMAX-3  62) SIGRTMAX-2
63) SIGRTMAX-1  64) SIGRTMAX

Linux信号可由下面的条件产生:

  • 对于前台进程,用户可以输入特殊终端字符来发送,比如输入Ctrl+C通常会给正在运行的进程发送一个中断信号
  • 系统异常,比如浮点异常和非法内存段访问
  • 系统状态变化,比如alarm定时器到期时将引起SIGALRM信号
  • 在终端运行kill命令或在程序中调用kill函数,例如:如果要杀死一个进程,我们可以使用 kill -9 pid 来杀死进程,-9表示发送9号信号,也就是SIGKILL信号,pid为发送信号的目标进程的进程ID

发送信号的系统调用:

int kill(pid_t pid, int sig);   // 给进程ID为pid的进程发送sig信号

信号的机制:

        进程或用户A给一个进程B发送信号,B在收到信号之前执行自己的代码,当B进程收到信号后,不管程序执行到什么位置,都要暂停运行,去处理信号,也就是调用信号处理函数,处理完再继续执行。与硬件中断类似——异步模式。但信号是软件层面实现的中断,早期常被成为“软中断”。

信号的特质:由于信号是通过软件方法实现,其实现手段导致信号有很强的延时性。但对于用户来说,这个延迟时间非常短,不易察觉。

每个进程收到的所有信号,都是由内核负责发送的,内核处理。
        

4、共享内存映射

        存储映射I/O (Memory-mapped I/O) 使一个磁盘文件与存储空间中的一个缓冲区相映射。于是当从缓冲区中取数据,就相当于读文件中的相应字节。于此类似,将数据存入缓冲区,则相应的字节就自动写入文件。这样,就可在不适用read和write函数的情况下,使用地址(指针)完成I/O操作。

         使用这种方法,首先应通知内核,将一个指定文件映射到存储区域中。这个映射工作可以通过mmap函数来实现。

在这里插入图片描述

相关系统调用:

/*
	建立映射
	addr: 建立映射区的地址,一般传NULL来由内核指定
	length:创建的映射区的大小
	prot:映射区权限
	flags:标志位参数(常用于设定更新物理区域、设置共享、创建匿名映射区)
	fd:用来建立映射区的文件描述符
	offset:映射文件的偏移(4K的整数倍)
*/
void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset);
/*
	同malloc函数申请内存空间类似的,mmap建立的映射区在使用结束后也应调用类似free的函数来释放。
*/
int munmap(void *addr, size_t length);

mmap父子进程通信

        父子等有血缘关系的进程之间也可以通过mmap建立的映射区来完成数据通信。但相应的要在创建映射区的时候指定对应的标志位参数flags:

  • MAP_PRIVATE: (私有映射) 父子进程各自独占映射区;
  • MAP_SHARED: (共享映射) 父子进程共享映射区;

mmap无血缘关系进程间通信

        实质上mmap是内核借助文件帮我们创建了一个映射区,多个进程之间利用该映射区完成数据传递。由于内核空间多进程共享,因此无血缘关系的进程间也可以使用mmap来完成通信。只要设置相应的标志位参数flags即可。若想实现共享,当然应该使用MAP_SHARED了。

        

5、共享内存

        共享内存允许多个进程访问同一内存空间,是在多个进程间共享数据最高效的方式。操作系统将不同进程之间共享内存安排为同一段物理内存,进程可以将共享内存链接到它们自己的地址空间中,如果某个进程修改了共享内存中的数据,其它进程读到的数据也将会改变。同样进程也可以和这块共享内存解除关联,接触关联后就不能操作这块共享内存了。

        共享内存并未提供锁机制,也就是说,在一个进程对共享内存进行读写时,不会阻止其它进程的读写。共享内存需要借助其它机制来保证进程间的数据同步(比如信号量)。

        使用ipcs命令可以查看共享内存、消息队列和信号量的相关信息:

[root@master uds]# ipcs

------ Message Queues --------
key        msqid      owner      perms      used-bytes   messages    

------ Shared Memory Segments --------
key        shmid      owner      perms      bytes      nattch     status      

------ Semaphore Arrays --------
key        semid      owner      perms      nsems     

共享内存相关函数:

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


/*
	创建或获取共享内存
	key: 共享内存的键值,是共享内存在系统中的编号,不同共享内存编号不能相同,用十六进制比较好
	size: 共享内存的大小,单位byte
	shmflg: 共享内存的访问权限,与文件差不多, 0666 | IPC_CREAT 表示所有用户都可读写,IPC_CREAT 表示如果不存在则创建
	return val: 成功返回一个id,失败返回-1,并设置errno
*/
int shmget(key_t key, size_t size, int shmflg);

/*
	将共享内存链接到当前进程的地址空间
	shmid: shmflg返回的id
	shmaddr: 共享内存链接到当前进程中的地址位置,通常为NULL,表示让系统来选择
	shmflg: 标志位,通常为0
	return val: 成功返回共享内存段的地址,失败返回 (void *)-1, 并设置errno
*/
void *shmat(int shmid, const void *shmaddr, int shmflg);

/*
	将共享内存从当前进程分离
	shmaddr: 共享内存地址
	return val: 成功返回0, 失败返回-1, 设置errno
*/
int shmdt(const void *shmaddr);

/*
	操作共享内存,可以用来删除一个共享内存
	shmid: 共享内存id
	cmd: 执行的操作, IPC_RMID表示删除
	buf: 设置为NULL
	return val: 失败返回-1, 设置errno
*/
int shmctl(int shmid, int cmd, struct shmid_ds *buf);

使用案例:一个进程从文件读出内容写入共享内存,另一个进程读共享内存数据,保存入一个新文件

/* sharemem_r.c */

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <stdlib.h>
#include <fcntl.h>
#include <errno.h>
#include <string.h>
#include <sys/ipc.h>
#include <sys/shm.h>

#define MAXBUFSIZE 4096
#define SHAREMEMESIZE 4096

int write_file(const char *buf, int size, const char *filename);

int main() {
    int shm_id;
    char buf[MAXBUFSIZE] = {0};
    char *shm_p = NULL;
    
    /* 
        获取共享内存,如果不存在则创建
    */
    if ( (shm_id = shmget(0x666, SHAREMEMESIZE, 0644)) < 0) {
        perror("shmget error");
        return 1;
    }

    /*
        把共享内存链接到当前进程的地址空间
     */
    shm_p = (char *)shmat(shm_id, NULL, 0);
    if (shm_p == (void *) -1) {
        perror("shmat error:");
        return 1;
    }

    // 拷贝共享内存中的数据
    memcpy(buf, shm_p, MAXBUFSIZE);

    // 将共享内存中的内容写入write_text文件中
    write_file(buf, strlen(buf), "write_text");

    printf("write to file: %s\n", buf);

    // 把共享内存从当前进程分离
    shmdt(shm_p);

    //删除共享内存
    if (shmctl(shm_id, IPC_RMID, 0) == -1) { 
        printf("shmctl failed\n"); 
        return -1; 
    }

    return 0;
}

// 将buf中的数据写入文件
int write_file(const char *buf, int size, const char *filename) {
    int fd, n;

    // 打开文件,不存在则创建
    if ((fd = open(filename, O_WRONLY | O_CREAT, 0644)) < 0) {
        perror("open file error:");
        return -1;
    } 

    // 写入内容
    n = write(fd, buf, size);
    if (n < 0) {
        perror("write file error");
        return -1;
    }

    close(fd);
    return n;
}

/* sharemem_w.c */

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <stdlib.h>
#include <fcntl.h>
#include <string.h>
#include <errno.h>
#include <sys/ipc.h>
#include <sys/shm.h>

#define MAXBUFSIZE 4096
#define SHAREMEMESIZE 4096

int read_file(char *buf, int size, const char *filename);

int main() {
    int n, shm_id;
    char buf[MAXBUFSIZE] = {0};
    char *shm_p = NULL;

    /* 
        获取共享内存,如果不存在则创建
    */
    if ( (shm_id = shmget(0x666, SHAREMEMESIZE, 0644 | IPC_CREAT)) < 0) {
        perror("shmget error");
        return 1;
    }

    /*
        把共享内存链接到当前进程的地址空间
     */
    shm_p = (char *)shmat(shm_id, NULL, 0);
    if (shm_p == (void *) -1) {
        perror("shmat error:");
        return 1;
    }

    // 从文件中读取数据
    n = read_file(buf, MAXBUFSIZE, "./read_text");
    if (n == -1) {
        return 1;
    }

    printf("read from file: %s\n", buf);

    // 将数据拷贝到共享内存
    memcpy(shm_p, buf, strlen(buf));

    // 把共享内存从当前进程分离
    shmdt(shm_p);

    return 0;
}

// 读文件内容到buf
int read_file(char *buf, int size, const char *filename) {
    int fd, n;

     // 打开read_test
    if ( (fd = open(filename, O_RDONLY)) < 0) {
        perror("open file_read_test error:");
        return -1;
    }
    // 读取文件内容
    n = read(fd, buf, size);
    if (n < 0) {
        perror("read file error:");
        return -1;
    }

    // 关闭文件
    close(fd);

    return n;
}

        

6、消息队列

        消息队列本质是存在与内核中的消息链表,一个消息队列由一个标识符来标识,进程可以从中读写数据。消息队列不属于某个进程,创建之后,即使该进程结束,消息队列依然存在,其他进程依然可以读写数据。消息队列与后面介绍的UNIX域套接字相比,在速度上没有多少优势。

相关系统调用:

/*
	获取或创建一个消息队列
*/
int msgget(key_t key, int msgflg);

/*
	向消息队列中发送消息
	msgp: 指向类似下面结构的结构体
		struct msgbuf {
        	long mtype;       
       		char mtext[1];    
        };
	msgsz: 上面结构体的mtext的大小由该字段指定,发送的时候可以使用strlen(buf.mtext), 接收的时候sizeof(buf.mtext)
	msgflg: 通常为0
*/
int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);

/*
	从消息队列中取出一条消息
*/
ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp,
               int msgflg);

生产者消费者示例:

/* producer.c */
#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <string.h>

typedef struct {
    long mtype;
    char mtext[1024];
}msgbuf;


int main(int argc, char const *argv[])
{
    key_t key;
    int msgid;

    if ((key = ftok(".", 666)) == -1) {
        perror("ftok error:");
        return 1;
    }

    if ((msgid = msgget(key, 0644 | IPC_CREAT)) == -1) {
        perror("msgget error:");
        return 1;
    }

    msgbuf buf = {1, "surprise, mother fucker"}; 

    if (msgsnd(msgid, &buf, strlen(buf.mtext), 0) == -1) {
        perror("msgsnd error");
        return 1;
    }

    return 0;
}

/* consumer.c */
#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <string.h>

typedef struct {
    long mtype;
    char mtext[1024];
}msgbuf;


int main(int argc, char const *argv[])
{
    key_t key;
    int msgid;

    if ((key = ftok(".", 666)) == -1) {
        perror("ftok error:");
        return 1;
    }

    if ((msgid = msgget(key, 0644 | IPC_CREAT)) == -1) {
        perror("msgget error:");
        return 1;
    }

    msgbuf buf;

    if (msgrcv(msgid, &buf, sizeof(buf.mtext), 1, 0) == -1) {
        perror("msgrcv error");
        return 1;
    }

    printf("recv msg, type:%ld, text:%s\n", buf.mtype, buf.mtext);

    msgctl(msgid, IPC_RMID, NULL);

    return 0;
}

        

7、信号量

        信号量是一个计数器,它主要用于在多个进程间的数据同步。

信号量的PV操作:

  • P操作:我们将申请信号量称为P操作,申请信号量的本质就是申请获得临界资源中某块资源的使用权限,当申请成功时临界资源中资源的数目应该减一,因此P操作的本质就是让计数器减一。
  • V操作:我们将释放信号量称为V操作,释放信号量的本质就是归还临界资源中某块资源的使用权限,当释放成功时临界资源中资源的数目就应该加一,因此V操作的本质就是让计数器加一。

信号量有两种:

System V信号量:常用于进程间同步

相关系统调用:

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

int semop(int semid, struct sembuf *sops, unsigned nsops);
int semtimedop(int semid, struct sembuf *sops, unsigned nsops, struct timespec *timeout);

int semctl(int semid, int semnum, int cmd, ...);

Posix信号量,常用于线程间同步

相关函数:

int sem_init(sem_t *sem, int pshared, unsigned int value);

int sem_wait(sem_t *sem);
int sem_trywait(sem_t *sem);
int sem_timedwait(sem_t *sem, const struct timespec *abs_timeout);

int sem_getvalue(sem_t *sem, int *sval);

int sem_post(sem_t *sem);
int sem_destroy(sem_t *sem);

        

8、UNIX域套接字(Unix Domain Socket)

        socket API原本是为网络通讯设计的,但后来在socket的框架上发展出一种IPC机制,就是Unix Domanin Socket(UDS)。虽然网络socket也可以用于同一台主机的进程间通讯,但是UDS用于IPC更有效率:不需要经过网络协议栈,不需要打包拆包、计算校验和、维护序号和应答号等,只是将应用层数据从一个进程拷贝到另一个进程。这是因为IPC是可靠的通讯,而网络协议是为不可靠通讯设计的。UDS也提供面向连接和面向数据报两种API接口,类似于TCP与UDP,但是面向数据报的UDS也是可靠的,消息既不会丢失也不会顺序错乱。

        UDS是全双工的,API接口语义丰富,相比于其它IPC有明显的优越性,目前已经成为使用最广泛的IPC机制。例如Docker cli与Docker 守护进程的通信就会使用UDS。

        使用UDS的步骤与网络socket差不多,也要使用到socket()、bind()、listen()、accept等系统调用。

        UDS与网络socket编程最明显的不同在于地址格式不同,用结构体sockaddr_un表示,网络编程的socket地址是IP地址加端口号,而UNIX Domain Socket的地址是一个socket类型的文件在文件系统中的路径,这个socket文件由bind()调用创建,如果调用bind()时该文件已存在,则bind()错误返回。

UDS的C/S架构案例:

/* server.c */
#include <stdlib.h>
#include <stdio.h>
#include <stddef.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>


int server_listen(const char *name) {
    int fd, len, err, ret;
    struct sockaddr_un un;

    // 创建unix domain socket
    if((fd = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) {
        perror("socket error");
        return -1;
    }

    // 以防存在,先删除
    unlink(name);

    memset(&un, 0, sizeof(un));
    un.sun_family = AF_UNIX;
    strcpy(un.sun_path, name);
    len = offsetof(struct sockaddr_un, sun_path) + strlen(name);

    // 绑定uds
    ret = bind(fd, (struct sockaddr *)&un, len);
    if( ret == -1) {
        close(fd);
        perror("bind error");
        return -1;
    }

    if(listen(fd, 128) < 0) {
        close(fd);
        perror("listen error");
        return -1;
    }

    return fd;
}

int server_accept(int listenfd, uid_t *uidptr)
{
	int clifd, len, err, rval;
	time_t staletime;
	struct sockaddr_un un;
	struct stat statbuf;

	len = sizeof(un);
	if ((clifd = accept(listenfd, (struct sockaddr *)&un, &len)) < 0) {
        return -1; 
    }
		
	len -= offsetof(struct sockaddr_un, sun_path); 
	un.sun_path[len] = 0; 

	if (stat(un.sun_path, &statbuf) < 0) {
		close(clifd);
        return -1;
	}
	if (S_ISSOCK(statbuf.st_mode) == 0) {
		close(clifd);
        return -1;
	}
	if (uidptr != NULL)
		*uidptr = statbuf.st_uid; 
	
	unlink(un.sun_path); 

	return clifd;
}



int main(int argc, char const *argv[])
{
    int lfd, cfd, n, i;
	uid_t cuid;
	char buf[1024];
	
	lfd = server_listen("tmp.sock");
    if (lfd < 0) {
         exit(-1);
     }
    while (1) {
        cfd = server_accept(lfd, &cuid);
        if (cfd < 0) {
            exit(-1);
        }

        while (1) {

            n = read(cfd, buf, 1024);
            if (n == -1) {
                break;
            } else if (n == 0) {
                printf("the other side has been closed.\n");
                break;
            }

            for (i = 0; i < n; i++) {
                buf[i] = toupper(buf[i]);
            }
            write(cfd, buf, n);
        } 
    }
    
	close(cfd);
	close(lfd);
	return 0;

}


/* client.c */
#include <stdio.h>
#include <stdlib.h>
#include <stddef.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <errno.h>

#define CLI_PATH "/var/tmp/" 

int cli_conn(const char *name)
{
	int fd, len, err, rval;
	struct sockaddr_un un;

	if ((fd = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) {
        return -1;
    }
		
	memset(&un, 0, sizeof(un));
	un.sun_family = AF_UNIX;
	sprintf(un.sun_path, "%s%05d", CLI_PATH, getpid());
	len = offsetof(struct sockaddr_un, sun_path) + strlen(un.sun_path);

	unlink(un.sun_path); 
	if (bind(fd, (struct sockaddr *)&un, len) < 0) {
        perror("bind error");
		close(fd);
        return -1;
	}

	/* fill socket address structure with server's address */
	memset(&un, 0, sizeof(un));
	un.sun_family = AF_UNIX;
	strcpy(un.sun_path, name);
	len = offsetof(struct sockaddr_un, sun_path) + strlen(name);
	if (connect(fd, (struct sockaddr *)&un, len) < 0) {
		perror("connect error");
        close(fd);
        return -1;
	}

    return fd;
}
int main(void)
{
	int fd, n;
	char buf[1024];

	fd = cli_conn("tmp.sock");
	if (fd < 0) {
		exit(-1);
	}

	while (fgets(buf, sizeof(buf), stdin) != NULL) {
		write(fd, buf, strlen(buf));
		n = read(fd, buf, sizeof(buf));
		write(STDOUT_FILENO, buf, n);
	}
	close(fd);

	return 0;
}

socketpair

        使用socketpair可以创建一对无名的相连的socket,可以用于有血缘关系的进程间的通信,使用起来比较类似于管道,但它是全双工的。

相关系统调用:

/*
	The socketpair() call creates an unnamed pair of connected sockets in the specified domain
	domain: 在linux上,只支持AF_UNIX
	sv: 一对用于通信的fd
*/
int socketpair(int domain, int type, int protocol, int sv[2]);
Linux进程通信 笔者在学习linux的过程中对linux进程通信进行记录学习。现在在 Linux 中使用较多的进程间通信方式主要有以下几种。 (1)管道(Pipe)及有名管道(named pipe):管道可用于具有亲缘关系进程间的通信, 有名管道,除具有管道所具有的功能外,它还允许无亲缘关系进程间的通信。 (2)信号(Signal):信号是在软件层次上对中断机制的一种模拟,它是比较复杂的通信 方式,用于通知接受进程有某事件发生,一个进程收到一个信号与处理器收到一个中断请求 效果上可以说是一样的。 (3)消息队列:消息 阅读详情

相关推荐

Linux——网络通信

包括多种机制,如管道、消息队列、信号、共享内存和信号量。这些机制都依赖于Linux内核提供的支持,用于实现不同进程之间的数据交换和同步。然而,这些通信方式在本地进程间通信中非常有用,但无法直接用于跨机器间的通信。

m0_73510889的博客 3468

Linux —— 进程间通信

进程间通信方式的介绍:管道通信、system V通信......

M的博客 6665

Linux进程间通信IPC的几种方式简介

Linux进程通信的源头       linux下的进程通信手段基本上是从Unix平台上的进程通信手段继承而来的。而对Unix发展做出重大贡献的两大主力AT&T(原为American Telephone & Telegraph的缩写,也是中文译名美国电话电报公司由来)的贝尔实验室及BSD(加州大学伯克利分校的伯克利软件发布中心)在进程间通信方面的侧重点有所不同。前者对Unix早期的进程间通信手段

Sunshine的专栏 7578

LINUX---进程间通信(IPC)

通信

weixin_48651223的博客 3493

linux——进程间通信IPC

Linux中七种IPC详解,进程间通信

a_zxswer的博客 793

【电子元件篇】电力电子中的变压器原理、作用、选型与测量指南-超简单解读

变压器虽小,却是电力电子的 “关键纽带”——从识别符号型号,到理解 “传交流隔直流” 的特点,再到按用途选参数、用万用表检测,只要抓住 “场景对应核心” 的逻辑,就能轻松掌握它的用法。

2301_81299416的博客 3025

Linux 进程间通信(IPC)详解

在操作系统里,不同进程间常常需要进行数据交换、同步协调等操作,进程间通信(Inter - Process Communication,IPC)机制应运而生。在Linux系统中,常见的IPC机制有管道、信号量、共享内存、消息队列和套接字。这些机制各自具备独特的特性,适用于不同的应用场景,并且在各类系统和应用程序开发中得到广泛应用,也是技术面试中的重点考查内容。

w20010915的博客 2248

11 CO配置-控制-成本中心会计-定义分割结构(OKES-Define Splitting Structure)

业务背景:定义分割结构 事务码: OKES SPRO路径:SPRO->控制->成本中心会计->实际过账->期末结算->作业分配->划分->定义分割结构 第1步,SPRO路径进入 第2步,选择“新的条目”创建 分割结构 第3步,选择分割结构代码 点击“分割规则”进行下一个界面,点击“新条目”创建 分割结构规则 第4步,点击“为规则选择” 为分割结构规则创建版本并保存 第5步,选择“分配”,点击“新的条目” 输入分割结构分配

4371

Linux进程间通信(IPC)深入解析

简单数据流传输:优先考虑管道(有亲缘关系)或FIFO(无亲缘关系)。高性能大规模数据共享共享内存是不二之选,但必须精心设计同步机制(信号量、文件锁等)。结构化消息传递消息队列或D-Bus(在桌面环境中)更为合适。进程同步与控制信号量用于同步,信号用于异步事件通知。网络通信或本地跨语言通信套接字提供了最广泛的支持和标准化接口。

X 1508

开源实战 | 基于SolidWorks与Matlab的并联索驱机器人轨迹规划全流程

本文详细介绍了基于SolidWorks与Matlab的并联索驱机器人轨迹规划全流程,涵盖从基础概念到实战技巧的各个环节。通过SolidWorks建模、Matlab运动学实现及轨迹优化策略,展示了如何实现高精度轨迹规划,适用于摄影、医疗等领域的机器人应用。

weixin_28331555的博客 403

3U 与 SV630A 伺服实现 CANLINK 通讯

SD410这组参数特别重要,这个32位特别重要,是防止数值乱码的关键。

weixin_42714122的博客 2694

Linux操作系统:进程间通信IPC方式

在 Linux 系统中,进程间通信至关重要。不同的进程通常拥有独立的虚拟地址空间,这保证了进程的独立性,但也使得进程间无法直接通信。为了解决这个问题,Linux 内核提供了多种进程间通信方式。数据传输:一个进程可能需要将数据发送给另一个进程。例如,在分布式计算环境中,不同的计算节点可能需要相互传递数据以完成复杂的任务。共享数据:多个进程可能希望操作共享的数据。当一个进程修改了共享数据时,其他共享该数据的进程应能立即看到这个变化。这在多线程编程或并行计算中非常常见,能够提高系统的效率和协同工作能力。

深度Linux 1422

AD9361 Fast Lock实战:Python脚本自动生成Profile寄存器配置(附完整代码)

本文详细介绍了利用Python脚本实现AD9361 Fast Lock Profile寄存器自动化配置的实战方案。通过解析频率合成公式、VCO约束及查表机制,脚本能根据目标频率一键生成完整的16进制寄存器序列,大幅提升射频系统开发中多频点切换的效率和准确性,并支持CSV、JSON、C头文件等多种输出格式,便于与FPGA/嵌入式系统集成。

weixin_29194693的博客 475

Linux第十讲:进程间通信IPC

Linux第十讲:进程间通信IPC

2301_79761834的博客 1165

conda upgrade出现Solving environment:failed CondaValueError Malformed version string invalid chars解决方案

本文主要介绍了conda install出现Solving environment:failed、CondaValueError: Malformed version string invalid character(s),希望能对使用conda的同学们有所帮助。 文章目录 1. 问题描述 2. 解决方案 2.1 步骤一 2.2 步骤二

weixin_43178406的博客 8万+

复现:Mamba-UNet:降水临近预报的创新解决方案

Mamba-UNet通过其创新的双分支Mamba融合模块、多尺度时空注意力模块和动态分位数加权损失函数,在降水临近预报领域取得了显著的性能提升。它不仅在多种降水场景下表现出色,尤其在预测强降水事件方面展现出了强大的能力。这一创新模型为降水临近预报提供了一种新的解决方案,有望在未来的研究和应用中发挥更大的作用,为应对气候变化带来的挑战提供有力支持。随着技术的不断发展和优化,我们期待Mamba-UNet在更广泛的气象预报任务中发挥更大的价值,为各行各业提供更精准、更可靠的降水预报服务。

hanfeng5268的博客 914

【Linux进程间通信】一、什么是IPC

本篇为Linux进程间通信第一篇文章,介绍什么是进程间通信IPC,关于进程间通信的具体方式及实现在后面会一一更新,更多内容请关注我的专栏《Linux从小白到大神》。

仅以此博客记录日常学习工作中所思所得,水平有限,难登大雅,万望海涵。 847

25、格式化字符串攻击全解析

本文深入解析了格式化字符串攻击的原理与利用方式,涵盖缓冲区溢出与格式化字符串漏洞的区别,详细介绍了printf函数的工作机制、常见格式化说明符及其安全隐患。文章分析了三类典型编程错误导致的漏洞,并展示了如何通过恶意格式化字符串实现拒绝服务、内存读取和写入攻击。同时,提供了有效的防御措施,包括输入验证、使用安全函数、避免直接使用用户输入作为格式化字符串以及启用编译器安全选项,帮助开发者防范此类高危漏洞。

beer8的博客 83

Linux进程控制与进程间通信(IPC)全面指南

Linux进程控制与进程间通信(IPC)全面指南

weixin_66055196的博客 1375

Data-Structures-and-Algorithm-Analysis-in-Java.pdf

作者: Weiss, Mark A. 出版社: Addison Wesley Data Structures and Algorithm Analysis in Java is an “advanced algorithms” book that fits between traditional CS2 and Algorithms Analysis courses. In the old ACM Curriculum Guidelines, this course was known as CS7. This text is for readers who want to learn good programming and algorithm analysis skills simultaneously so that they can develop such programs with the maximum amo...

STM32F407中文手册

STM32f407中文手册 本参考手册面向应用开发人员,提供有关使用 STM32F405xx/07xx、STM32F415xx/17xx、 STM32F42xxx 和 STM32F43xxx 微控制器存储器与外设的完整信息。STM32F405xx/07xx、STM32F415xx/17xx、STM32F42xxx 和 STM32F43xxx 构成一个微 控制器系列,各产品具有不同的存储器大小、封装和外设。有关订购信息以及器件的机械与电气特性,请参见数据手册。有关 ARM Cortex™-M4F 内核的信息,请参见《Cortex™-M4F 技术参考手册》。

Linux进程间通信(IPC)

pipe 函数接受一个参数,是包含两个整数的数组,如果调用成功,会通过 pipefd[] 传出给用户程序两个文件描述符,需要注意 pipefd[0] 指向管道的读端, pipefd[1] 指向管道的写端,那么此时这个管道对于用户程序就是一个文件,可以通过 read(pipefd [0]);如果有指向管道写端的文件描述符没关闭,而持有管道写端的进程也没有向管道中写数据,这时有进程从管道读端读数据,那么管道中剩余的数据都被读取后,再次read会阻塞,直到管道中有数据可读了才读取数据并返回。

m0_46417380的博客 1010

jre9下载 jdk9

(Java SE Runtime Environment 9.0) 9.0u175 官方最新版 【x86|x64】

Linux 进程间通信-IPC 机制

Linux 平台上在 Kernel 协调下完成进程之间的相互通信,有多种进程间通信 —— Inter Process Communication(IPC)方式。 1. IPC 分类 按照功能用途来看有三种基本的进程间通信类型,分别用户信息交换(Communication),同步(Synchronization)和信号(Signal)。另外,在基本的 IPC 通信机制之上还存在更为复杂和广泛应用的进程间通信机制,通常提供了丰富和更高层次的封装以便于应用开发,比如 Android 平台采用的 Binder

Hugo的博客 2184

WINCC7.3破解文件

WINCC7.3硬件够破解文件,有需要的下载!WINCC7.3硬件够破解文件,有需要的下载!

【Linux】进程间通信(IPC)

通过结合具体需求(数据量、实时性、开发成本)选择合适的IPC机制。

weixin_43510208的博客 1073

Linux进程间通信的秘密通道:IPC机制详解

在 Linux 操作系统中,进程是操作系统的基本运行单位。由于进程间具有独立的地址空间,如何实现进程之间的数据交换和同步成为系统设计中的一个核心问题。为了解决这一问题,Linux 提供了多种进程间通信(Inter-Process Communication, IPC)机制。这些机制在性能、适用场景和复杂性方面各具特点,开发者可以根据具体需求选择合适的通信方式。 本文将从概念出发,详细讲解 Linux 的 IPC 机制,包括管道、消息队列、共享内存、信号量和信号等,帮助读者深入理解 IPC 的原理和应用场景

fudaihb的博客 1032

linux进程间通信IPC方式

Linux环境下,进程地址空间相互独立、彼此隔离,因此进程间的数据不能直接访问。如果要交换数据,必须要通过内核,在内核中开辟一块缓冲区,进程A把数据从用户空间拷贝到内核缓冲区,进程B再把数据从内核缓冲区拷贝走,内核提供的这种机制称为进程间通信(IPC, InterProcess Communication)。在Linux下由很多种进程间通信的方式,分别是:匿名管道(PIPE)、命名管道(FIFO)、信号、共享内存、消息队列、信号量、UNIX域套接字。现在常用的进程间通信方式有:管道(使用简单)

lx123010的专栏 1218

Linux——进程间通信IPC

Linux环境下,进程地址空间相互独立,每个进程各自有不同的用户地址空间。任何一个进程的全局变量在另一个进程中都看不到,所以进程和进程之间不能相互访问,要交换数据必须通过内核,在内核中开辟一块缓冲区,进程1把数据从用户空间拷到内核缓冲区,进程2再从内核缓冲区把数据读走,内核提供的这种机制称为进程间通信(IPC,InterProcess Communication)。 ...

一个小孩 268
上一篇: Golang+Mysql+Vue+Nginx前后端分离项目--个人博客
下一篇: Golang源码探究 —— map
CoreDump丶
博客等级 码龄8年 451粉丝 70原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值