linux用户空间 - 多进程编程(三)

linux用户空间 - 多进程编程(三),第1张

管道用于有学园关系的进程之间。

管道的pipe 系统调用实际上就是创建出来两个文件描述符。

当父进P1程创建出 fd[2] 时,子进程P2 会继承父进程的所有,所以也会得到pipe 的 2个 文件描述符。

所以毫无瓜葛的两个进程,一定不会访问到彼此的pipe。无法用管道进行通信。

管道一般是单工的。f[0]读,f[1]写

管道也可以适用于 兄弟进程(只要有血缘即可)。由于管道是单工的,当两个进程之间需要双向通信,则需要两跟管道。

执行

ctrl-c(2号信号) + SIGUSR1 信号 绑了一个新函数。则 ctrl-c 无效。

查看进程的信号

号信号被捕获。

将2号信号忽略掉

9号信号 kill 和19号信号 stop 不能乱搞,只能用缺省。

其它信号甚至段信号也都可以捕获。

改变程序的执行现场,修改PC指针,有些像goto,只不过返回非0值

运行结果

making segment fault

after segment fault

程序不会死。

如果不忽略 page fault

则会产生 core dump.

不停的给data 赋值,同时每隔1s会有信号进来,打印 data的值。

理论上打印出来的结果同时为0,同时为1

但并非如此,是 0,1,交替随机的。

signal 异步的,随时都可以进来,所以打印出来的结果,并不是我想要的。

信号对于应用程序来说,很像中断对于内核,都是访问临界区数据

信号被屏蔽,延后执行。

写多线程的程序时,不要以为只有线程之间有竞争,其实信号也会有竞争

system v 的IPC 年代有些久远。

有血缘关系的进程 key_t 都是相同的。

Key 是私有key IPV PRIVATE

可能用消息队列,可能用共享内存,可能用信号量进行通讯。

利用 _pathname 路径,约定好一条路径。和tcp/ip地址很像,来生成一个key_t key, 用msg_get shm_get 得到共享内存or 信号量。

int id 可以理解为文件描述符 fd。

其中Sys V 的共享内存 最为常用。

一定要检查路径,如果仅仅有2个进程,你没有创建路径,两者都是 -1(相当于大家约定好了),那当然能通信拉。但更多的进程出现,则会有问题。

一定要检查返回值

依然依靠key,但是api 实在是太挫了。P&V *** 作都是 semop. (posix 的 ipc跟为简洁)

POSIX 共享内存当然也需要一个名字,但并不是路径。

无论读进程还是写进程,都需要传入相同的名字。

如果是unbuntu 会在以下路径生成文件

其实 2和3 是1 的符号链接。 只要保证是一个就能互相通信

关键点,mmap 内存的属性修改为 private 后,产生写时copy,虚拟地址一样,但是物理地址已经不同了

当然 如果子进程修改了程序背景,执行了 exec,那么完全不一样了,直接修改了内存逻辑。

在 child1 进程中,你把 fd[0] close 掉并设置成-1,这样等child2被fork出来的时候,它所继承的fd[0]也同样是-1,当然无法从中读到任何东西。 改动很简单,把你close(fd[0])的那两行注释掉就行了,参考下面的代码。 更合理的改法是,fork完 child2 后,再在 child1 中close(fd[0]),你可以自行线下实现。

#include<sys/types.h>

#include<unistd.h>

#include<cstring>

#include<iostream>

#include<stdio.h>

#include<stdlib.h>

int main(){

    int fd[2]

    if(pipe(fd)!=0){

        std::cout<<"pipe failed"<<std::endl

        exit(0)

    }

    pid_t pid=fork()

    if(pid<0){

        std::cout<<"fork failed"<<std::endl

        exit(0)

    }else if(pid == 0){

        //close(fd[0])

        //fd[0]=-1

        write(fd[1],"XXXXXX",10)

        std::cout<<"child1 process"<<std::endl

    }else{

        exit(0)

    }

    

    std::cout<<"================="<<std::endl

    pid_t pid1=fork()

    if(pid1<0){

        std::cout<<"fork1 failed"<<std::endl

        exit(0)

    }else if(pid1 == 0){

        std::cout<<"chiled2 process"<<std::endl

        close(fd[1])

        fd[1]=-1

        char buf[1024]={0}

        read(fd[0],&buf,10)

        std::cout<<buf<<std::endl

        std::cout<<strlen(buf)<<std::endl

    }else{

        exit(0)

    }

    return 0

}

#include <stdio.h>

main()

{

int i,r,p1,p2,fd[2]

char buf[50],s[50]

pipe(fd)//创建匿名管道,fd[0]为读端,fd[1]为写端

while((p1=fork())==-1)//创建子进程P1,直至成功为止(p1!=-1)

if(p1==0) //子进程P1执行逻辑

{

lockf(fd[1],1,0)//锁定管道写端,保证写入数据的完整性

sprintf(buf,"child process P1 is sending messages!\n") //在buf中填入准备写入管道的信息数据

printf("child processP1!\n")//打印“子进程P1正在运行”

write(fd[1],buf,50)//向管道写端fd[1]写入buf中的数据,写完后该数据即可以从读端fd[0]读出

sleep(5)//睡眠5秒

lockf(fd[1],0,0)//解锁管道写端

exit(0)//子进程P1退出

}

else //主进程的执行逻辑

{

while((p2=fork())==-1)//创建第二个子进程P2

if(p2==0) //子进程P2的执行逻辑

{

lockf(fd[1],1,0)//锁定管道写端,保证数据写入完整

sprintf(buf,"child process P2 is sending messages!\n")//在buf中填入准备写入管道的信息数据

printf("child processP2!\n")//打印“子进程P2正在运行”

write(fd[1],buf,50) //向管道写端fd[1]写入buf中的数据,写完后该数据即可从读端fd[0]读出

sleep(5) //睡眠5秒

lockf(fd[1],0,0) //解锁管道写端

exit(0)//子进程P2退出

}

//以下为主进程执行逻辑

wait(0)//等待某个子进程退出

if(r=read(fd[0],s,50)==-1) //从管道读端fd[0]读取P1或者P2写入的数据(视哪个子进程抢先执行到lockf函数)

{

printf(:can't read pipe\n") //读取失败,打印错误信息

}

else

{

printf(:%s\n",s)//打印出读到的信息数据

}

wait(0)//等待第二个子进程退出

if(r=read(fd[0],s,50)==-1) //从管道读端fd[0]读取出P1或者P2写入的数据(视哪个子进程后执行到lockf函数)

{

printf(:can't read pipe\n")//读取失败,打印错误信息

}

else

{

printf(:%s\n",s)//打印读取到的信息数据

}

exit(0)//主进程退出

}

}

总的说来,就是主进程创建了两个子进程P1、P2,这两个子进程分别向管道写入了一行文字,然后主进程从管道另一端将这两行文字读出并打印出来

由于进程的并发执行性,哪个子进程的信息先写到管道是随机的,因此该程序每次运行的输出可能并不相同,两行文字之间可能会相互交换


欢迎分享,转载请注明来源:内存溢出

原文地址:https://54852.com/yw/8728238.html

(0)
打赏 微信扫一扫微信扫一扫 支付宝扫一扫支付宝扫一扫
上一篇 2023-04-20
下一篇2023-04-20

发表评论

登录后才能评论

评论列表(0条)

    保存