进程间通信-信号-pipe-fifo

发布时间 2023-12-13 09:38:52作者: 20211308wjc

进程间通信-信号-pipe-fifo

编译fifo文件夹的程序

运行fifo文件夹的程序


代码说明

1.consumer.c 文件包含一个用来从 FIFO(命名管道)读取数据的 C 程序。以下是它的主要组件和系统调用的分解:

main() 函数:

  • 初始化文件描述符(pipe_fd)、返回状态(res)、读取数据的缓冲区以及字节数计数(bytes)的变量。
  • 设置 FIFO 以只读模式(O_RDONLY)打开。
  • 使用 open() 系统调用以指定的模式和路径打开 FIFO。
  • 使用循环从 FIFO 读取数据:
  • 使用 read() 系统调用从 pipe_fd 读取数据到 buffer。读取的量存储在 res 中。
  • 它持续读取直到没有更多数据(res 变为 0)。
  • 在读取完成后使用 close() 系统调用关闭 FIFO。
  • 如果打开 FIFO 失败,程序以 EXIT_FAILURE 退出。
  • 程序在成功退出前打印进程 ID 和读取的数据量。
  • 这个程序作为 IPC(进程间通信)场景中的消费者,通过 FIFO 读取生产者进程发送的数据。使用 FIFO 允许进程之间的同步通信。

2.producer.c 文件是一个用于向 FIFO(命名管道)写入数据的 C 程序。以下是其主要组件和系统调用的分析:

main() 函数:

  • 初始化文件描述符(pipe_fd)、返回状态(res)、只写模式(O_WRONLY)、字节数计数(bytes)以及数据缓冲区(buffer)。
  • 检查 FIFO 是否存在(使用 access() 函数)。如果不存在,则创建一个新的 FIFO(使用 mkfifo())。
  • 以只写模式打开 FIFO(使用 open() 系统调用)。
  • 使用循环向 FIFO 写入数据:
  • 使用 write() 系统调用将 buffer 中的数据写入 pipe_fd。写入的量存储在 res 中。
  • 循环直到写入的总量达到 10MB。
  • 如果写入过程中出现错误(res 等于 -1),则打印错误信息并退出。
  • 关闭 FIFO(使用 close() 系统调用)。
  • 如果打开 FIFO 失败,程序以 EXIT_FAILURE 退出。
  • 程序在成功退出前打印进程 ID。
  • 这个程序充当 IPC 场景中的生产者,通过 FIFO 向消费者进程发送数据。通过检查和创建 FIFO,以及循环写入数据,它与消费者进程协调同步通信。 ​

3.testmf.c 文件是一个简单的 C 程序,用于创建 FIFO(命名管道)。以下是其主要组件的分析:

main() 函数:

  • 使用 mkfifo() 系统调用创建一个新的 FIFO。FIFO 的路径被设定为 /tmp/myfifo,权限设置为 0777(意味着任何用户都可以读写该 FIFO)。
  • mkfifo() 返回的结果存储在变量 res 中。如果 res 为 0,表示 FIFO 创建成功,程序会打印一条消息确认 FIFO 已创建。
  • 程序使用 exit(EXIT_SUCCESS) 退出,表明正常终止。
  • 这个程序的作用是创建一个命名管道,它允许 producer.c 和 consumer.c 中的进程进行通信。在 IPC 场景中,通常需要一个独立的程序或进程来创建共享的通信资源(如 FIFO),testmf.c 就承担了这个角色

综上,这三个程序共同构成了一个使用 FIFO 进行进程间通信的示例。testmf.c 负责创建 FIFO,producer.c 将数据写入 FIFO,而 consumer.c 则从 FIFO 读取数据。通过这种方式,两个进程可以安全地共享数据。

编译pipe文件夹下的程序

运行pipe文件夹下的程序







pipe文件夹代码说明

1.listargs.c 是一个用于演示命令行参数处理和标准错误输出的简单 C 程序。

功能:

  • 打印命令行参数的数量和每个参数的值。
  • 向标准错误流(stderr)输出一条消息。

代码结构:

  • main 函数接收两个参数:ac(参数个数)和 av(参数值数组)。
  • 使用 printf 输出参数个数(ac)和每个参数的值(通过遍历 av 数组)。
  • 使用 fprintf 将消息发送到标准错误流(stderr)。

2.分析 pipe.c

pipe.c 是一个演示如何在 C 程序中创建管道和进程分叉(forking)的程序。

功能:

  • 创建一个管道,然后创建一个子进程。
  • 子进程和父进程使用这个管道进行通信。
  • 父进程执行命令行中的第二个参数作为程序,子进程执行第一个参数作为程序。

代码结构:

  • 检查命令行参数的个数,如果不是 3(程序名和两个命令),则显示用法信息并退出。
  • 使用 pipe() 创建一个管道。
  • 使用 fork() 创建一个子进程。
  • 在父进程(pid > 0)中:
  • 关闭管道的写端。
  • 使用 dup2() 将标准输入重定向到管道的读端。
  • 执行 av[2] 指定的程序。
  • 在子进程(pid == 0)中:
  • 关闭管道的读端。
  • 使用 dup2() 将标准输出重定向到管道的写端。
  • 执行 av[1] 指定的程序。
  • 如果任何步骤失败,使用 oops() 函数打印错误信息并退出。

3.分析 pipedemo.c

pipedemo.c 是一个展示如何在 C 程序中使用管道进行数据读写的程序。

功能:

  • 创建一个管道,然后在一个循环中使用这个管道来读写数据。
  • 从标准输入(stdin)读取数据,写入管道的写端。
  • 从管道的读端读取数据,然后写入标准输出(stdout)。

代码结构:

  • 使用 pipe() 创建一个管道。
  • 在一个循环中:
  • 使用 fgets() 从标准输入读取数据到 buf。
  • 使用 write() 将 buf 中的数据写入管道的写端。
  • 将 buf 中的所有字符替换为 'X'。
  • 使用 read() 从管道的读端读取数据回到 buf。
  • 将读取到的数据写入标准输出。
  • 如果任何读写操作失败,打印错误信息并跳出循环。

3.分析 pipedemo2.c

pipedemo2.c 是一个演示如何在 C 程序中使用管道进行父子进程通信的程序。

功能:

  • 创建一个管道,并通过 fork() 创建一个子进程。
  • 子进程定期向管道写入一条消息。
  • 父进程定期从管道读取消息并将其写入标准输出。

代码结构:

  • 使用 pipe() 创建一个管道。
  • 使用 fork() 分叉进程:
  • 在子进程(fork() 返回 0)中:
  • 无限循环,定期向管道写入 CHILD_MESS(“I want a cookie\n”)。
  • 使用 sleep(5) 暂停 5 秒。
  • 在父进程(fork() 返回子进程的 PID)中:
  • 无限循环,定期向管道写入 PAR_MESS(“testing..\n”)。
  • 使用 sleep(1) 暂停 1 秒。
  • 从管道读取数据,并将其写入标准输出。
  • 如果任何读写操作失败,使用 oops() 函数打印错误信息并退出。

4.分析 stdinredir1.c

stdinredir1.c 是一个演示如何在 C 程序中重定向标准输入(stdin)的程序。

功能:

  • 读取并打印来自标准输入的三行文本。
  • 关闭当前的标准输入流(文件描述符 0)。
  • 打开 /etc/passwd 文件,并将其文件描述符重定向到标准输入。
  • 再次读取并打印三行文本,这次是从 /etc/passwd 文件中读取。

代码结构:

  • 使用 fgets() 三次从标准输入读取文本行并打印。
  • 使用 close(0) 关闭当前的标准输入。
  • 使用 open("/etc/passwd", O_RDONLY) 打开 /etc/passwd 文件,并确保它的文件描述符为 0(即标准输入)。
  • 如果文件描述符不是 0,则打印错误信息并退出。
  • 再次使用 fgets() 三次从新的标准输入(现在是 /etc/passwd 文件)读取文本行并打印。

5.分析 testtty.c

testtty.c 是一个非常简单的 C 程序,用于演示向标准输入(通常是终端)写入数据。

功能:

  • 向文件描述符 0(通常是标准输入)写入字符串 "abcde\n"。

代码结构:

  • 定义一个字符串 buf,内容为 "abcde\n"。
  • 使用 write() 函数向文件描述符 0 写入 buf 中的内容。

编译signal文件夹下的程序

运行signal文件夹下的程序




对上述代码进行解释

第一个文件 sigactdemo.c 包含了一个简单的信号处理示例程序。

  • 引入头文件:程序包括标准输入输出库 stdio.h、Unix标准函数库 unistd.h 和信号处理库 signal.h。
  • 定义常量:INPUTLEN 被定义为100,可能用于定义输入缓冲区的大小。
  • 信号处理函数:定义了一个名为 inthandler 的函数,该函数接受一个整数参数(通常是信号编号),打印接收到的信号,然后休眠一段时间,最后再次打印。
  • 主函数 (main):
  • 初始化 sigaction 结构体 newhandler,用于指定信号处理行为。
  • 设置 newhandler 的处理函数为 inthandler。
  • 设置 newhandler 的标志,包括 SA_RESTART(重新启动被信号打断的系统调用)、SA_NODEFER(在处理当前信号时不阻塞当前信号)、SA_RESETHAND(处理完信号后重置信号处理器为默认)。
  • 初始化一个信号集 blocked,并将 SIGQUIT 信号加入到这个集合中。这个集合被设置为 newhandler 的信号掩码,意味着在处理 SIGINT 信号时,SIGQUIT 信号会被阻塞。
  • 调用 sigaction 函数将 SIGINT(通常是通过Ctrl+C产生)的处理方式设置为 newhandler。
  • 程序进入一个无限循环,不断地读取输入并打印。
  • 这个程序演示了如何使用 sigaction 结构体和相关函数来处理信号。当用户按下 Ctrl+C 时,会触发 SIGINT 信号,程序会调用 inthandler 函数来处理该信号。在处理信号的过程中,SIGQUIT 信号会被阻塞。

第二个文件 sigactdemo2.c 包含了一个使用信号来实现睡眠功能的程序。程序的主要内容如下:

  • 引入头文件:程序包括 Unix 标准函数库 unistd.h、信号处理库 signal.h 和标准输入输出库 stdio.h。
  • 信号处理函数:定义了一个名为 sig_alrm 的函数,这个函数没有实际操作(空函数体),用于捕获 SIGALRM 信号。
  • 自定义的睡眠函数 (mysleep):
  • 接受一个表示秒数的参数 nsecs。
  • 定义和设置一个 sigaction 结构体 newact,用于指定 SIGALRM 信号的处理行为(在这里是 sig_alrm 函数)。
  • 调用 alarm 函数来设置一个定时器,当定时器到达指定的秒数时,会发送 SIGALRM 信号。
  • 调用 pause 函数使进程暂停,直到捕获到一个信号。
  • 当 SIGALRM 被捕获后,pause 返回,mysleep 函数再次调用 alarm 函数以获取剩余的睡眠时间。
  • 最后,将 SIGALRM 的处理方式恢复为原始状态,并返回未睡足的时间。
  • 主函数 (main):
  • 使用无限循环,每两秒打印一次信息,演示 mysleep 函数的使用。
  • 这个程序展示了如何结合信号处理和 alarm 函数实现一个简单的睡眠机制。当 alarm 计时器触发 SIGALRM 信号时,由于 sig_alrm 函数不执行任何操作,所以程序继续执行,从 pause 返回。

第三个文件 sigdemo1.c 包含了一个基本的信号处理示例程序。这个程序的主要内容包括:

  • 引入头文件:程序包括标准输入输出库 stdio.h 和信号处理库 signal.h。
  • 信号处理函数:定义了一个名为 f 的函数,该函数接受一个信号编号参数,并在被调用时打印 "OUCH!"。
  • 主函数 (main):
  • 使用 signal 函数将 SIGINT 信号(通常由 Ctrl+C 触发)的处理方式设置为 f 函数。
  • 进入一个循环,循环五次,每次循环打印 "hello" 并休眠两秒。
  • 这个程序是对信号处理的一个非常基础的演示。当用户在程序运行时按下 Ctrl+C,SIGINT 信号被触发,程序调用 f 函数来处理这个信号,从而打印 "OUCH!"。。 ​

第四个文件 sigdemo2.c 展示了如何忽略一个信号。这个程序的主要内容如下:

  • 引入头文件:程序包括标准输入输出库 stdio.h 和信号处理库 signal.h。
  • 主函数 (main):
  • 使用 signal 函数将 SIGINT 信号(通常由 Ctrl+C 触发)的处理方式设置为 SIG_IGN,意味着程序将忽略这个信号。
  • 打印 "you can't stop me!",然后进入一个无限循环。
  • 在循环中,每秒休眠一次,并打印 "haha"。
  • 这个程序通过将 SIGINT 信号的处理方式设置为 SIG_IGN,展示了如何使程序对 Ctrl+C 忽视不理。由于这种设置,当用户尝试使用 Ctrl+C 中断程序时,程序将不会响应,而是继续运行并每秒打印 "haha"。

第五个文件 sigdemo3.c 展示了如何处理多种信号。这个程序的主要内容包括:

  • 引入头文件:程序包括标准输入输出库 stdio.h、字符串处理库 string.h、信号处理库 signal.h 和 Unix 标准函数库 unistd.h。
  • 定义常量:INPUTLEN 被定义为100,用于定义输入缓冲区的大小。
  • 信号处理函数:定义了两个处理函数 inthandler 和 quithandler,这两个函数在接收到信号时打印信息,休眠一段时间,然后再次打印。
  • 主函数 (main):
  • 使用 signal 函数分别将 SIGINT(通常由 Ctrl+C 触发)和 SIGQUIT(通常由 Ctrl+\ 触发)的处理方式设置为 inthandler 和 quithandler。
  • 进入一个循环,提示用户输入消息,并使用 read 函数读取输入。
  • 如果读取到的字符数为 -1,则打印错误信息;否则打印用户输入的内容。
  • 当用户输入的内容以 "quit" 开头时,程序退出循环并结束。
  • 这个程序演示了如何为不同的信号设置不同的处理函数。用户在程序运行时可以通过发送特定的信号(如 Ctrl+C 或 Ctrl+\)来触发相应的处理函数。同时,它还提供了一个简单的用户交互方式,允许用户通过输入来控制程序流程。