目录
例子1 sigaction 函数来设置信号处理函数,并接收从其他进程发来的信号和附带的数据
例子2 sigqueue 函数发送一个信号以及一个与信号相关的值到一个特定的进程
例子3 信号量进行进程间同步,包括创建、初始化、P操作、V操作以及删除信号量。这里特别展示了在父子进程之间的资源竞争和同步控制
例子5 通过命令行参数接收信号编号和进程ID,并使用系统命令kill发送信号
例子 7 信号量的创建、初始化、基本操作(P 和 V 操作)
例子1 sigaction
函数来设置信号处理函数,并接收从其他进程发来的信号和附带的数据
#include <signal.h> // 包含信号处理相关的函数和数据结构
#include <stdio.h> // 包含标准输入输出函数库
// 自定义信号处理函数,使用了三个参数形式,以接收额外的信号信息
void handler(int signum, siginfo_t *info, void *context)
{
printf("get signum %d\n", signum); // 打印接收到的信号编号
if(context != NULL) { // 如果上下文指针不为空
printf("from: %d\n", info->si_pid); // 打印发送信号的进程ID
// 下面的注释掉的代码展示了如何访问发送的数据
// printf("get data=%d\n", info->si_int); // 如果数据是整型
// printf("get data=%d\n", info->si_value.sival_int); // 如果数据是通过 sigqueue 发送的整型
// printf("get data=%s\n", context); // 如果数据是字符串
printf("get data=%d\n", *(int *)(info->si_value.sival_ptr)); // 打印通过指针发送的数据
}
}
int main()
{
struct sigaction act; // 定义 sigaction 结构体变量,用于指定信号处理的相关设置
printf("pid = %d\n", getpid()); // 打印当前进程ID,便于其他进程向此进程发送信号
act.sa_sigaction = handler; // 设置信号处理函数
act.sa_flags = SA_SIGINFO; // 设置使用扩展信号处理函数,使函数能接收额外信息
sigaction(SIGUSR1, &act, NULL); // 设置 SIGUSR1 信号的处理函数为上面定义的 handler
while(1); // 让程序处于无限循环,等待接收信号
return 0;
}
例子2 sigqueue
函数发送一个信号以及一个与信号相关的值到一个特定的进程
#include <stdio.h> // 包含标准输入输出函数库
#include <signal.h> // 包含信号处理相关的函数和数据结构
int main(int argc, char **argv)
{
int signum; // 用于存储信号编号
int pid; // 用于存储目标进程的进程ID
// 从命令行参数获取信号编号和目标进程ID
signum = atoi(argv[1]); // 将命令行的第一个参数转换为整数,用作信号编号
pid = atoi(argv[2]); // 将命令行的第二个参数转换为整数,用作进程ID
int a = 10; // 定义一个整数变量a,并初始化为10
union sigval value; // 定义一个信号值的联合体
// 使用信号值联合体的sival_ptr成员指向整数a的地址
// 这样就可以向目标进程发送一个包含整数地址的信号
value.sival_ptr = &a;
// 使用sigqueue函数向目标进程发送信号及附加数据
// 参数pid是目标进程的进程ID,signum是信号编号,value是要发送的信号值
sigqueue(pid, signum, value);
// 打印发送信号的进程的进程ID,确认信号已发送
printf("%d, done\n", getpid());
return 0; // 程序正常结束
}
例子3 信号量进行进程间同步,包括创建、初始化、P操作、V操作以及删除信号量。这里特别展示了在父子进程之间的资源竞争和同步控制
#include<stdio.h>
#include<stdlib.h>
#include<sys/sem.h>
// 初始化信号量
int init_sem(int sem_id, int value)
{
union semun sem_union;
sem_union.val = value; // 设置信号量的初值
if(semctl(sem_id, 0, SETVAL, sem_union) == -1) // 初始化信号量
{
perror("Init Semaphore Error"); // 错误处理
return -1;
}
return 0;
}
// P操作: 等待信号量变为可用(减1操作)
int sem_p(int sem_id)
{
struct sembuf sbuf;
sbuf.sem_num = 0; // 信号量编号
sbuf.sem_op = -1; // P操作
sbuf.sem_flg = SEM_UNDO; // 确保程序未正常结束时恢复信号量状态
if(semop(sem_id, &sbuf, 1) == -1) // 执行P操作
{
perror("P operation Error");
return -1;
}
return 0;
}
// V操作: 释放信号量(加1操作)
int sem_v(int sem_id)
{
struct sembuf sbuf;
sbuf.sem_num = 0; // 信号量编号
sbuf.sem_op = 1; // V操作
sbuf.sem_flg = SEM_UNDO; // 确保程序未正常结束时恢复信号量状态
if(semop(sem_id, &sbuf, 1) == -1) // 执行V操作
{
perror("V operation Error");
return -1;
}
return 0;
}
// 删除信号量集
int del_sem(int sem_id)
{
union semun sem_union;
if(semctl(sem_id, 0, IPC_RMID, sem_union) == -1) // 删除信号量集
{
perror("Delete Semaphore Error");
return -1;
}
return 0;
}
int main()
{
int sem_id; // 信号量集ID
key_t key;
pid_t pid;
// 获取key值
if((key = ftok(".", 'z')) < 0)
{
perror("ftok error");
exit(1);
}
// 创建信号量集,其中只有一个信号量
if((sem_id = semget(key, 1, IPC_CREAT|0666)) == -1)
{
perror("semget error");
exit(1);
}
// 初始化:初值设为0资源被占用
init_sem(sem_id, 0);
if((pid = fork()) == -1)
perror("Fork Error");
else if(pid == 0) /*子进程*/
{
sleep(2); // 确保父进程先运行
printf("Process child: pid=%d\n", getpid());
sem_v(sem_id); /*子进程释放资源*/
}
else /*父进程*/
{
sem_p(sem_id); /*父进程等待资源*/
printf("Process father: pid=%d\n", getpid());
sem_v(sem_id); /*父进程释放资源*/
del_sem(sem_id); /*只有父进程删除信号量集*/
}
return 0;
}
例子4 忽略特定信号或为特定信号设置自定义处理函数
#include <signal.h> // 包含信号处理相关的头文件
#include <stdio.h> // 包含标准输入输出函数库
// 自定义的信号处理函数
void handler(int signum)
{
printf("get signum=%d\n", signum); // 打印接收到的信号编号
switch(signum){
case SIGINT: // 如果是中断信号
printf("SIGINT\n");
break;
case SIGKILL: // 如果是终止信号(注意,SIGKILL通常不能被捕捉或忽略)
printf("SIGKILL\n");
break;
case SIGUSR1: // 如果是用户定义信号1
printf("SIGUSR1\n");
break;
}
printf("never quit\n"); // 信号处理完毕后打印
}
int main()
{
// 设置对SIGINT(通常是Ctrl+C发出的中断信号)的处理为忽略
signal(SIGINT, SIG_IGN);
// 设置对SIGKILL的处理为忽略,但实际上这个设置是无效的,因为SIGKILL信号不能被忽略或捕捉
signal(SIGKILL, SIG_IGN);
// 设置对SIGUSR1的处理为自定义的handler函数
signal(SIGUSR1, handler);
while(1); // 使程序持续运行,等待信号
return 0; // 程序正常结束
}
例子5 通过命令行参数接收信号编号和进程ID,并使用系统命令kill
发送信号
#include <signal.h> // 包含信号处理相关的头文件
#include <stdio.h> // 包含标准输入输出函数库
#include <sys/types.h> // 包含数据类型,如pid_t
#include <stdlib.h> // 包含标准库函数,如atoi
int main(int argc, char **argv)
{
int signum; // 用于存储信号编号
int pid; // 用于存储进程ID
char cmd[128] = {0}; // 创建一个字符数组用来存储系统命令
// 使用atoi函数将命令行参数转换为整数
signum = atoi(argv[1]); // 第一个参数是信号编号
pid = atoi(argv[2]); // 第二个参数是目标进程ID
// 打印接收到的信号编号和进程ID
printf("num=%d, pid=%d\n", signum, pid);
// 使用sprintf函数构建kill命令,格式为"kill -信号编号 进程ID"
sprintf(cmd, "kill -%d %d", signum, pid);
// 调用system函数执行构建的命令
system(cmd);
// 打印发送信号成功的消息
printf("send signal ok");
return 0;
}
例子6 通过信号量控制父子进程的执行顺序
#include <stdio.h> // 包含标准输入输出函数库
#include <sys/types.h> // 包含数据类型定义,如pid_t
#include <sys/ipc.h> // 包含IPC(进程间通信)相关的头文件
#include <sys/sem.h> // 包含System V信号量相关的函数声明和数据结构
// 用于semctl系统调用的联合体定义
union semun {
int val; // Value for SETVAL
struct semid_ds *buf; // Buffer for IPC_STAT, IPC_SET
unsigned short *array; // Array for GETALL, SETALL
};
// P操作 (等待/锁定资源)
void Phandle(int id)
{
struct sembuf sops;
sops.sem_num = 0; // 操作信号量的编号
sops.sem_op = -1; // 信号量减1操作,阻塞等待信号量变为0
sops.sem_flg = SEM_UNDO; // 确保如果进程异常退出,信号量操作会被撤销
semop(id, &sops, 1); // 执行P操作
}
// V操作 (释放/解锁资源)
void Vhandle(int id)
{
struct sembuf sops;
sops.sem_num = 0; // 操作信号量的编号
sops.sem_op = 1; // 信号量加1操作
sops.sem_flg = SEM_UNDO; // 确保如果进程异常退出,信号量操作会被撤销
semop(id, &sops, 1); // 执行V操作
}
int main()
{
key_t key;
key = ftok(".",1); // 生成一个基于文件的唯一键
// 创建或获取信号量集,只包含一个信号量
int semid = semget(key, 1, IPC_CREAT|0666);
// 初始化信号量的值为0
union semun setVal;
setVal.val = 0;
semctl(semid, 0, SETVAL, setVal);
int pid = fork(); // 创建子进程
if(pid > 0){
// 父进程
Phandle(semid); // 等待信号量变为0
printf("this is father\n");
Vhandle(semid); // 信号量值加1
semctl(semid, 0, IPC_RMID, NULL); // 删除信号量集
} else if(pid == 0){
// 子进程
printf("this is child\n");
Vhandle(semid); // 信号量值加1
} else {
// fork失败
printf("fork error\n");
}
return 0;
}
例子 7 信号量的创建、初始化、基本操作(P 和 V 操作)
#include <stdio.h> // 包含标准输入输出函数库
#include <stdlib.h> // 包含标准库函数,如exit()
#include <sys/sem.h> // 包含System V信号量相关的函数声明和数据结构
#include <unistd.h> // 提供access()函数和其他有用的POSIX函数和符号
// 初始化信号量
int init_sem(int sem_id, int value)
{
union semun sem_union; // 必须定义一个联合体用于semctl操作
sem_union.val = value; // 设置信号量的值
if (semctl(sem_id, 0, SETVAL, sem_union) == -1) // 初始化信号量
{
perror("Init Semaphore Error");
return -1;
}
return 0;
}
// P操作 (信号量减1,即等待操作)
int sem_p(int sem_id)
{
struct sembuf sbuf;
sbuf.sem_num = 0; // 信号量编号
sbuf.sem_op = -1; // P操作,等待并减小信号量的值
sbuf.sem_flg = SEM_UNDO; // 在进程终止时撤销当前操作,防止死锁
if (semop(sem_id, &sbuf, 1) == -1) // 执行P操作
{
perror("P operation Error");
return -1;
}
return 0;
}
// V操作 (信号量加1,即释放操作)
int sem_v(int sem_id)
{
struct sembuf sbuf;
sbuf.sem_num = 0; // 信号量编号
sbuf.sem_op = 1; // V操作,释放信号量,增加其值
sbuf.sem_flg = SEM_UNDO; // 在进程终止时撤销当前操作,防止死锁
if (semop(sem_id, &sbuf, 1) == -1) // 执行V操作
{
perror("V operation Error");
return -1;
}
return 0;
}
// 删除信号量集
int del_sem(int sem_id)
{
if (semctl(sem_id, 0, IPC_RMID) == -1) // 删除信号量集
{
perror("Delete Semaphore Error");
return -1;
}
return 0;
}
int main()
{
int sem_id; // 信号量集ID
key_t key; // IPC键
pid_t pid; // 进程ID
// 获取key值,用于信号量的唯一标识
if ((key = ftok(".", 'z')) < 0)
{
perror("ftok error");
exit(1);
}
// 创建信号量集,其中只有一个信号量
if ((sem_id = semget(key, 1, IPC_CREAT | 0666)) == -1)
{
perror("semget error");
exit(1);
}
// 初始化信号量:设置初值为0,表示资源被占用
init_sem(sem_id, 0);
// 创建子进程
if ((pid = fork()) == -1)
perror("Fork Error");
else if (pid == 0) /* 子进程 */
{
sleep(2); // 等待确保父进程先行
printf("Process child: pid=%d\n", getpid());
sem_v(sem_id); // 释放资源
}
else /* 父进程 */
{
sem_p(sem_id); // 等待资源,此处将阻塞直到子进程释放信号量
printf("Process father: pid=%d\n", getpid());
sem_v(sem_id); // 再次释放资源
del_sem(sem_id); // 删除信号量集
}
return 0;
}