进程间的通信

网友投稿 541 2022-08-27

进程间的通信

进程间的通信

信号

信号是软件中断的模拟,可以在任何时候发给进程,如果进程处于未执行状态,该信号就由内核保存,直到进程恢复执行再传递给它。 SIGKILL和SEGSTOP是应用程序无法捕捉和忽略的。 几个常用的快捷键和信号: ​​​ctrl + C​​​ —— SIGINT 中断信号 ​​​ctrl + \​​​ —— SIGQUIT 退出信号 ​​​ctrl + Z​​​ —— SIGTSTP 进程挂起 functions about signals:

function

introduction

int kill(pid_t pid, int sig)

send signal to a process

int raise(int sig)

send a signal to the caller

unsigned int alarm(unsigned int seconds)

set an alarm clock for delivery of a signal

int pause(void)

wait for signal

typedef void (*sighandler_t)(int); sighandler_t signal(int signum, sighandler_t handler);

ANSI C signal handling

int sigemptyset(sigset_t *set)

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

int sigfillset(sigset_t *set)

initializes set to full, including all signals

int sigaddset(sigset_t *set, int signum)

add respectively signal signum from set

int sigdelset(sigset_t *set, int signum)

delete respectively signal signum from set

int sigismember(const sigset_t *set, int signum)

tests whether signum is a member of set

int sigprocmask(int how, const sigset_t *set, sigset_t *oldset)

examine and change blocked signals

下面是子进程给自己发送SIGSTOP信号,用于暂停进程。父进程中​​waitpid(pid,0,WNOHANG)​​ 用于子进程没有退出立即返回(return immediately if no child has exited.)。

#include #include #include #include #include #include #include int main(){ pid_t pid = fork(); if(pid == 0){ // child printf("child id is %d\n",getpid()); raise(SIGSTOP); } else if(pid > 0){ // father printf("father pid is %d\n",getpid()); if(waitpid(pid,0,WNOHANG) == 0){ int ret = kill(pid,SIGKILL); if(ret < 0) perror("ending child failed "); else printf("father process end child process.\n"); } } else { perror("fork "); exit(1); } return 0; }/*father pid is 6284father process end child process.*/

alarm用于设置多少秒后发出SIGALAR信号,时间片函数控制事件发生的时间点。

#include #include #include #include #include #include #include void handler(int sig){ printf("hello world\n");}int main(){ int i; alarm(3); signal(SIGALRM,handler); for(i=1;i<=6;i++){ printf("times is %d\n",i); sleep(1); } return 0; }/*times is 1times is 2times is 3hello worldtimes is 4times is 5times is 6*/

信号的阻塞:

#include #include #include void handler(){ printf("signal SIGINT has been blocked for 5 seconds, now it works.\n");}int main(){ signal(SIGINT,handler); int i; sigset_t set; if(sigemptyset(&set) == -1){ perror("sigemptyset "); } if(sigaddset(&set,SIGINT) == -1){ perror("sigaddset "); } if(sigprocmask(SIG_BLOCK,&set,NULL) == -1){ /* make set blocked */ perror("sigpromask "); } raise(SIGINT); for(i=0;i<5;i++){ printf("sleeps %d seconds...\n",i+1); sleep(1); } if(sigprocmask(SIG_UNBLOCK,&set,NULL) < 0){ perror("sigprocmask "); } return 0;}/*sleeps 1 seconds...sleeps 2 seconds...sleeps 3 seconds...sleeps 4 seconds...sleeps 5 seconds...signal SIGINT has been blocked for 5 seconds, now it works.*/

管道

进程通信方式中的管道是半双工的,仅用于父子进程或者兄弟进程,单独构成独立的文件系统,存在于内存之中。 相关的函数:

function

introduction

int pipe(int pipefd[2])

create pipe

int pipe2(int pipefd[2], int flags)

create pipe

FILE *popen(const char *command, const char *type)

opens a process by creating a pipe, forking, and invoking the shell

int pclose(FILE *stream)

waits for the associated process to terminate and returns the exit status of the command as returned by wait4(2)

int mkfifo(const char *pathname, mode_t mode)

make a FIFO special file (a named pipe)

例子:fork()创建进程,父进程给子进程通过管道发送消息“I’m father.”, 子进程读取;子进程给父进程发送消息“I’m child.”, 父进程再读取。

#include #include #include #include #include #include #include int main(){ int pipe_fd[2]; if(pipe(pipe_fd) < 0){ perror("pipe "); exit(1); } int bytes; char str[105] = {0}; int pid = fork(); if(pid < 0) perror("fork "); else if(pid == 0){ // child bytes = read(pipe_fd[0],str,104); if(bytes > 0){ printf("child read from pipe: %s\n",str); } else perror("child read "); //fdopen(pipe_fd[1],"w"); bytes = write(pipe_fd[1],"I'm child.",104); if(bytes <= 0){ perror("child write "); printf("child write %d\n",errno); exit(1); } } else { bytes = write(pipe_fd[1],"I'm father.",104); if(bytes <= 0){ perror("father write "); exit(1); } waitpid(pid,NULL,0); // wait for child process. bytes = read(pipe_fd[0],str,104); if(bytes > 0){ printf("father read from pipe: %s\n",str); } else perror("father read "); } return 0;}/*works:child read from pipe: I'm father.father read from pipe: I'm child.if we put "waitpid(pid,NULL,0);" after "else perror("father read ");",we would find read is blocked:father read from pipe: I'm father.^Cwe can also get that process has individual pipe_fd[] for himself.*/

使用管道实现​​ls |grep .c​​程序:

#include #include #include #include #include #include #include int main(){ FILE *fp = popen("ls","r"); /* read pipe */ if(fp == NULL){ perror("popen "); exit(1); } char buf[1005]={0}; if(fread(buf,1,1004,fp) <= 0){ /* store contents in buf */ perror("fread "); exit(1); } pclose(fp); fp = popen("grep -a .c","w"); /* write pipe */ if(fwrite(buf,1,1004,fp) <= 0){ /* write contents from buf to file stream fp */ perror("fwrite "); exit(1); } while(fgets(buf,1004,fp) != NULL){ printf("%s\n",buf); } pclose(fp); return 0;}/*it's equal to shell command `ls |grep .c`*/

popen()中的shell命令是在fork出来的子进程中执行的。

命名管道

命名管道FIFO是一种特殊的pipe,可以使用在任何两个进程中通信。 相关函数:

int mkfifo(const char *pathname, mode_t mode);int open(const char *pathname, int flags);int open(const char *pathname, int flags, mode_t mode);ssize_t read(int fd, void *buf, size_t count);ssize_t write(int fd, const void *buf, size_t count);int close(int

下面实现两个进程的命名管道通信。 (open操作管道,以读或者写的方式打开,如果没有另一个进程进行写或读,是会一直阻塞的,fifo存在自动阻塞的特性。可以使用读写的方式打开避免这样的尴尬。) processA.c:

#include #include #include #include #include #include #include #include #include #include #include int main(){ mkfifo("fifo1",0644); mkfifo("fifo2",0644); int wfd = open("fifo1",O_RDWR); int rfd = open("fifo2",O_RDWR); struct timeval timer = {0,0}; fd_set wfd_set, rfd_set; if(wfd == -1 || rfd == -1){ perror("open "); exit(1); } printf("process A:\n"); while(1){ FD_ZERO(&rfd_set); FD_SET(rfd,&rfd_set); FD_SET(fileno(stdin),&rfd_set); if(select(rfd+1,&rfd_set,NULL,NULL,&timer) <= 0) continue; if(FD_ISSET(rfd,&rfd_set)){ char str[105] = {0}; read(rfd,str,104); printf("message from process B: %s\n",str); } if(FD_ISSET(fileno(stdin),&rfd_set)){ char str[105] = {0}; fgets(str,104,stdin); if(write(wfd,str,strlen(str)) <= 0){ perror("write "); exit(1); } } } close(rfd); close(wfd); return 0;}/*process A:hello, I'm process Amessage from process B: I get itI'm glad to heard from you.message from process B: see you later.*/

processB.c

#include #include #include #include #include #include #include #include #include #include #include int main(){ mkfifo("fifo1",0644); mkfifo("fifo2",0644); int rfd = open("fifo1",O_RDWR); int wfd = open("fifo2",O_RDWR); struct timeval timer = {0,0}; fd_set wfd_set, rfd_set; if(wfd == -1 || rfd == -1){ perror("open "); exit(1); } printf("process B:\n"); while(1){ FD_ZERO(&rfd_set); FD_SET(rfd,&rfd_set); FD_SET(fileno(stdin),&rfd_set); if(select(rfd+1,&rfd_set,NULL,NULL,&timer) <= 0) continue; if(FD_ISSET(rfd,&rfd_set)){ char str[105] = {0}; read(rfd,str,104); printf("message from process A: %s\n",str); } if(FD_ISSET(fileno(stdin),&rfd_set)){ char str[105] = {0}; fgets(str,104,stdin); if(write(wfd,str,strlen(str)) <= 0){ perror("write "); exit(1); } } } close(rfd); close(wfd); return 0;}/*process B:message from process A: hello, I'm process AI get itmessage from process A: I'm glad to heard from you.see you later.*/

我们可以在当前目录下发现多了两个fifo文件:

$ ls -l fifo*prw-r--r-- 1 edemon edemon 0 1月 7 16:26 fifo1prw-r--r-- 1 edemon edemon 0 1月 7 16:26 fifo2

消息队列

消息队列是保存在内核中的消息列表。用户进程可以向消息队列中添加消息,也可以从消息队列中(自定义)读取消息。通信的进程没有关系上的 限制。 消息队列的相关函数 IPC: inter-process communication

functions

introduction

key_t ftok(const char *pathname, int proj_id)

convert a pathname and a project identifier to a System V IPC key

int msgget(key_t key, int msgflg)

get a System V message queue identifier

int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg)

send messages to a System V message queue

ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp, int msgflg)

receive messages from a System V message queue

int msgctl(int msqid, int cmd, struct msqid_ds *buf)

System V message control operations

例子:使用消息队列,父进程给子进程发送消息。

#include #include #include #include #include #include int main(){ key_t key = ftok(".",0); int msg_id = msgget(key,IPC_CREAT|0644); if(msg_id == -1){ perror("msgget "); exit(1); } printf("please enter message to transform: \n"); char message[105] = {0}; fgets(message,104,stdin); int pid = fork(); if(pid == -1){ perror("fork "); exit(1); } else if(pid == 0){ if(msgsnd(msg_id,message,strlen(message),0) == -1) { perror("msgsnd "); exit(1); } } else { waitpid(pid,NULL,0); memset(message,0,sizeof(message)); if(msgrcv(msg_id,message,104,0,0) <= 0){ perror("msgrcv "); exit(1); } printf("father process receive message from child process:\n%s",message); if(msgctl(msg_id,IPC_RMID,NULL) == -1){ perror("msgctl "); exit(1); } } return 0;}/*please enter message to transform: hello worldfather process receive message from child process:hello world*/

共享内存

共享内存用于多个进程之间的通信,因为不需要数据的来回复制,所以是最快的进程之间通信的机制。

function

introduction

void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset)

map files or devices into memory

int munmap(void *addr, size_t length)

unmap files or devices into memory

int shmget(key_t key, size_t size, int shmflg)

allocates a shared memory segment

void *shmat(int shmid, const void *shmaddr, int shmflg)

shared memory operations

int shmdt(const void *shmaddr)

detaches the shared memory segment

系统V共享内存的方式(The POSIX shared memory object),通过映射特殊文件系统shm中的文件实现共享内存的通信。 例子:利用系统V共享内存的通信方法,子进程给父进程发送信息。

#include #include #include #include #include #include #include /* For mode constants */#include /* For O_* constants */int main(){ int fd = shm_open("test",O_CREAT|O_RDWR,0644); if(fd == -1){ perror("shm_open "); exit(1); } ftruncate(fd,105); char *str = (char *)mmap(NULL,105,PROT_READ|PROT_WRITE,MAP_SHARED,fd,0); if((void*)-1 == str){ perror("mmap "); exit(1); } int pid = fork(); if(pid == -1) perror("fork "); else if(pid == 0){ printf("child process %d set message str.\n",getpid()); strcpy(str,"hello world, I'm child."); exit(0); } else { waitpid(pid,0,0); printf("father process get share memory message: %s\n",str); if(munmap(str,105)<0) perror("munmap "); str = NULL; } return 0;}/*shm_open, shm_unlink: Link with -lrt.[edemon@CentOS workspace]$ gcc shm_com.c -lrt[edemon@CentOS workspace]$ ./a.out child process 8049 set message str.father process get share memory message: hello world, I'm child.we can find a new file test in /dev/shm/[edemon@CentOS workspace]$ ls /dev/shmpulse-shm-1359020668 pulse-shm-2129357730 pulse-shm-3531010208 testpulse-shm-1643945986 pulse-shm-3326717209 pulse-shm-3944084079*/

如果使用普通文件映射,那么改变open的方式即可。​​int fd = open("test",O_CREAT|O_RDWR|O_TRUNC,0644);​​​ 如果使用匿名映射(没有文件和内存对应),那么不使用shm_open和open函数产生文件描述符,使用mmap(): ​​​char *str = (char *)mmap(NULL,105,PROT_READ|PROT_WRITE,MAP_SHARED|MAP_ANONYMOUS,-1,0);​​​ 在路径​​​/proc/sys/kernel​​中存在共享内存最大字节数的限制文件shmnmax, 最大标识数的限制文件shmmni

[edemon@CentOS kernel]$ cat shmmax4294967295[edemon@CentOS kernel]$ cat shmmni4096

文件系统shm安装点在交换分区上,系统V共享内存随着内核持续,系统重新引导(内核重新引导)后,所有的内存都将丢失。

[edemon@CentOS workspace]$ ls /dev/shm pulse-shm-1812859712 pulse-shm-2430381189 pulse-shm-264722293 pulse-shm-4074297125 pulse-shm-94801651

版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:ANSI C (2) —— str系列函数
下一篇:git learn (1)
相关文章

 发表评论

暂时没有评论,来抢沙发吧~