概念:信号是在软件层次上对中断机制的一种模拟,是一种异步通信方式
linux内核通过信号通知用户进程,不同的信号类型代表不同的事件
Linux对早期的unix信号机制进行了扩展
进程对信号有不同的响应方式
所有信号的产生及处理全部都是由内核完成的
信号名 | 含义 | 默认操作 |
---|---|---|
SIGHUP | 该信号在用户键入INTR字符(Ctrl-C)时产生,内核发送此信号送到当前终端的所有前台进程 | 终止 |
SIGINT | 该信号在用户终端关闭时产生,通常是发给和该终端关联的会话内的所有进程 | 终止 |
SIGQUIT | 该信号和SIGINT类似,但由QUIT字符(通常是Ctrl-)来产生 | 终止 |
SIGILL | 该信号在一个进程企图执行一条非法指令时产生 | 终止 |
SIGSEV | 该信号在非法访问内存时产生,如野指针、缓冲区溢出 | 终止 |
SIGPIPE | 当进程往一个没有读端的管道中写入时产生,代表“管道断裂” | 终止 |
SIGKILL | 该信号用来结束进程,并且不能被捕捉和忽略 | 终止 |
SIGSTOP | 该信号用于暂停进程,并且不能被捕捉和忽略 | 暂停进程 |
SIGTSTP | 该信号用于暂停进程,用户可键入SUSP字符(通常是Ctrl-Z)发出这个信号 | 暂停进程 |
SIGCONT | 该信号让进程进入运行态 | 继续运行 |
SIGALRM | 该信号用于通知进程定时器时间已到 | 终止 |
SIGUSR1/2 | 该信号保留给用户程序使用 | 终止 |
SIGCHLD | 是子进程状态改变发给父进程的。 | 忽略 |
kill命令:
默认情况下,kill命令发送的信号是终止信号(SIGTERM),通常会请求进程正常退出。
kill [-signal] pid
示例:
kill -9 12345 // 发送SIGKILL信号给进程ID为12345的进程,强制终止它
kill -l // 查看所有信号名 [signal]
killall命令:
killall命令用于根据进程名终止或发送信号给所有匹配的进程。你可以使用进程名、用户名、程序名等来匹配进程。
killall [-u user | prog]
示例:
killall firefox // 终止所有名为"firefox"的进程
killall -u Linux // 终止所有由"Linux"用户启动的进程
int kill(pid_t pid, int signum)
int raise(int sig); // 给自己发信号,等价于kill(getpid(), signo);
功能:发送信号
参数:
pid: > 0:发送信号给指定进程
= 0:发送信号给跟调用kill函数的那个进程处于同一进程组的进程。
< -1: 取绝对值,发送信号给该绝对值所对应的进程组的所有组员。
= -1:发送信号给,有权限发送的所有进程。
signum:待发送的信号
示例:
#include
#include
#include
int main() {
pid_t target_pid = 12345; // 将此处的 12345 替换为目标进程的实际进程ID
// 发送终止信号(SIGTERM)给目标进程
int result = kill(target_pid, SIGTERM);
if (result == 0) {
printf("终止信号已成功发送给进程 %d\n", target_pid);
} else {
perror("发送信号时出错");
}
return 0;
}
示例:
#include
#include
void signal_handler(int sig) {
printf("Received signal: %d\n", sig);
}
int main() {
signal(SIGINT, signal_handler); // 设置信号处理函数,捕获Ctrl+C
raise(SIGINT); // 发送信号给自己,触发信号处理函数
return 0;
}
unsigned int alarm(unsigned int seconds);
功能:用于设置定时器,以在指定的秒数后发送 SIGALRM 信号。
参数:
seconds:定时秒数,表示定时器的时间间隔,即多少秒后触发定时器。
返回值:返回之前定时器的剩余秒数,如果之前没有定时器,返回值为0。
注意事项:
alarm 函数不是线程安全的。在多线程环境中,如果不小心调用 alarm 函数,可能会影响其他线程。
alarm 函数是非阻塞的,它会设置定时器并立即返回。之后,定时器会在指定的秒数后触发 SIGALRM 信号。
在设置定时器时,如果之前已经存在定时器,调用 alarm 函数会取消之前的定时器。
示例:
#include
#include
#include
void alarm_handler(int signum) {
printf("定时器触发,收到 SIGALRM 信号!\n");
}
int main() {
signal(SIGALRM, alarm_handler);
unsigned int interval = 3; // 设置定时器触发的时间间隔(秒)
printf("等待 %u 秒...\n", interval);
unsigned int remaining_seconds = alarm(interval); // 设置定时器并获取之前的剩余秒数
// 在这里添加一些其他的操作
printf("定时器已触发,剩余 %u 秒\n", remaining_seconds);
while (1) {
// 等待
}
return 0;
}
useconds_t ualarm(useconds_t usecs, useconds_t interval);
eg:
value:参数指定了初始定时器的时间值(以微秒为单位)。
interval:参数指定了定时器到期后,如果它会被重置的间隔时间值(以微秒为单位)。
ualarm:函数的返回值是上一个定时器剩余的时间值。
例子:
#include
#include
#include
void handle_alarm(int signum) {
printf("Received SIGALRM signal.\n");
}
int main() {
if (signal(SIGALRM, handle_alarm) == SIG_ERR) {
perror("signal");
return 1;
}
useconds_t initial_value = 1000000; // 1秒
useconds_t interval = 1000000; // 1秒
useconds_t remaining_time = ualarm(initial_value, interval);
printf("Remaining time: %lu microseconds\n", remaining_time);
while (1) {
// 主循环保持进程运行
}
return 0;
}
在这个例子中,ualarm 函数会在初始时间(1秒)后触发 SIGALRM 信号,然后每隔1秒再次触发。handle_alarm 函数用于处理信号。
timer_create 函数是用于创建一个定时器的 POSIX 函数,它可以在一定时间间隔后触发一个信号。这个函数允许你在定时器超时时发送一个指定的信号给进程。
int timer_create(clockid_t clockid, struct sigevent *sevp, timer_t *timerid);
eg:
clockid:指定了定时器的时钟源,常用的有 CLOCK_REALTIME(实时时钟)和 CLOCK_MONOTONIC(单调时钟)。前者基于实际时间,后者基于系统启动时间,不受系统时间调整的影响。
sevp:一个指向 struct sigevent 结构的指针,用于指定定时器的事件通知方式。可以设置为 NULL,表示使用默认方式。
timerid:一个用于存储创建后的定时器标识符的变量。
struct sigevent 结构允许你指定定时器到期时如何通知进程,它包含了一个联合体来支持不同的通知方式,例如线程通知、信号通知等。
timer_t 是一个用于标识定时器的数据类型,实际上是一个不透明的指针类型。
struct sigevent结构体
struct sigevent {
int sigev_notify; // 通知方法(SIGEV_SIGNAL 或 SIGEV_THREAD)
int sigev_signo; // 发送的信号编号或线程函数(如果是 SIGEV_THREAD)
union sigval sigev_value; // 传递给信号处理函数或线程函数的值
void (*sigev_notify_function)(union sigval); // 线程启动函数(如果是 SIGEV_THREAD)
void *sigev_notify_attributes; // 线程属性(如果是 SIGEV_THREAD)
_Atomic void *sigev_notify_thread_id; // 线程 ID(如果是 SIGEV_THREAD_ID)
};
struct itimerspec结构体
struct itimerspec {
struct timespec it_interval; // 周期定时器的间隔时间
struct timespec it_value; // 初始超时时间
};
struct sigaction结构体
struct sigaction {
void (*sa_handler)(int); // 信号处理函数
void (*sa_sigaction)(int, siginfo_t *, void *); // 带有附加信息的信号处理函数
sigset_t sa_mask; // 用于阻塞的信号集
int sa_flags; // 信号处理标志
void (*sa_restorer)(void); // 未使用
};
示例:
#include
#include
#include
#include
void handle_timer(int signum, siginfo_t *info, void *context) {
printf("Timer expired.\n");
}
int main() {
timer_t timerid;
struct sigevent sev;
struct itimerspec its;
sev.sigev_notify = SIGEV_SIGNAL;
sev.sigev_signo = SIGALRM;
sev.sigev_value.sival_ptr = &timerid;
if (timer_create(CLOCK_REALTIME, &sev, &timerid) == -1) {
perror("timer_create");
return 1;
}
its.it_value.tv_sec = 1; // 初始时间:1秒
its.it_value.tv_nsec = 0;
its.it_interval.tv_sec = 1; // 间隔时间:1秒
its.it_interval.tv_nsec = 0;
if (timer_settime(timerid, 0, &its, NULL) == -1) {
perror("timer_settime");
return 1;
}
struct sigaction sa;
sa.sa_flags = SA_SIGINFO;
sa.sa_sigaction = handle_timer;
sigaction(SIGALRM, &sa, NULL);
while (1) {
// 主循环保持进程运行
}
return 0;
}
int setitimer(int which, const struct itimerval *new_value, struct itimerval *old_value);
功能:定时的发送alarm信号
eg:
which 参数指定了要设置的定时器类型。常用的取值有三种:
ITIMER_REAL:以实际时间为基准,发送 SIGALRM 信号。
ITIMER_VIRTUAL:以进程在用户态运行的时间为基准,发送 SIGVTALRM 信号。
ITIMER_PROF:以进程在用户态和内核态运行的总时间为基准,发送 SIGPROF 信号。
new_value 是一个指向 struct itimerval 结构的指针,用于设置新的定时器值。
old_value 是一个指向 struct itimerval 结构的指针,用于存储之前的定时器值(如果需要的话)。
struct itimerval 结构定义如下:
struct timeval {
time_t tv_sec; /* seconds */
suseconds_t tv_usec; /* microseconds */
};
struct timeval 结构体定义如下:
struct timeval {
time_t tv_sec; // 秒
suseconds_t tv_usec; // 微秒
};
例子:
#include
#include
#include
void handle_alarm(int signum) {
printf("Timer expired.\n");
}
int main() {
struct itimerval timer;
timer.it_value.tv_sec = 1; // 初始时间:1秒
timer.it_value.tv_usec = 0;
timer.it_interval.tv_sec = 2; // 间隔时间:2秒
timer.it_interval.tv_usec = 0;
if (signal(SIGALRM, handle_alarm) == SIG_ERR) {
perror("signal");
return 1;
}
if (setitimer(ITIMER_REAL, &timer, NULL) == -1) {
perror("setitimer");
return 1;
}
while (1) {
// 主循环保持进程运行
}
return 0;
}
signal函数:
typedef void (*sighandler_t)(int);
sighandler_t signal(int signum, sighandler_t handler);
功能:捕捉信号执行自定义函数
返回值:成功时返回原先的信号处理函数,失败时返回SIG_ERR
**参数:**
signo 要设置的信号类型
handler 指定的信号处理函数: SIG_DFL代表缺省方式; SIG_IGN 代表忽略信号;
系统建议使用sigaction函数,因为signal在不同类unix系统的行为不完全一样。
sigaction函数:
int sigaction(int signum, const struct sigaction *act,struct sigaction *oldact);
struct sigaction {
void (*sa_handler)(int);
void (*sa_sigaction)(int, siginfo_t *, void *);
sigset_t sa_mask;
int sa_flags;
void (*sa_restorer)(void);
}
**参数:**
signum:处理的信号
act,oldact: 处理信号的新行为和旧的行为,是一个sigaction结构体。
sigaction结构体成员定义如下:
sa_handler: 是一个函数指针,其含义与 signal 函数中的信号处理函数类似
sa_sigaction: 另一个信号处理函数,它有三个参数,可以获得关于信号的更详细的信息。
sa_flags参考值如下:
SA_SIGINFO:使用 sa_sigaction 成员而不是 sa_handler 作为信号处理函数
SA_RESTART:使被信号打断的系统调用自动重新发起。
SA_RESETHAND:信号处理之后重新设置为默认的处理方式。
SA_NODEFER:使对信号的屏蔽无效,即在信号处理函数执行期间仍能发出这个信号。
re_restorer:是一个已经废弃的数据域
例子:
#include
#include
#include
#include
#include
#include
typedef void (*sighandler_t)(int);
sighandler_t oldact;
void handle(int sig){
if(sig == SIGINT){
printf("I cath the SIGINT \n");
}else if (sig==SIGALRM){
printf("second timer \n");
alarm(1);
}
// signal(SIGINT,oldact);
}
void mytimer(int sig){
}
int main(){
struct sigaction act;
act.sa_handler = handle;
act.sa_flags = 0;
sigemptyset(&act.sa_mask);
sigaction(SIGINT,&act,NULL);
alarm(1);
sigaction(SIGALRM,&act,NULL);
// oldact = signal(SIGINT,handle);
while(1){
sleep(1);
}
}
SIGCHLD的产生条件
#include
#include
#include
#include
#include
void handle(int sig){
wait(NULL);
printf("Get sig =%d\n",sig);
}
int main(){
pid_t pid;
struct sigaction act;
act.sa_handler = handle;
act.sa_flags = 0;
sigemptyset(&act.sa_mask);
pid = fork();
if(pid>0){
//wait(NULL);
sigaction(SIGCHLD,&act,NULL);
while(1){
printf("this is father process\n");
sleep(1);
}
}else if(pid==0){
sleep(5);
exit(0);
}
}
有时候不希望在接到信号时就立即停止当前执行,去处理信号,同时也不希望忽略该信号,而是延时一段时间去调用信号处理函数。这种情况可以通过阻塞信号实现。
信号的阻塞概念:信号的”阻塞“是一个开关动作,指的是阻止信号被处理,但不是阻止信号产生。
信号的状态:
信号递达(Delivery ):实际信号执行的处理过程(3种状态:忽略,执行默认动作,捕获)
信号未决(Pending):从产生到递达之间的状态
sigset_t set; 自定义信号集。 是一个32bit 64bit 128bit的数组。
sigemptyset(sigset_t *set); 清空信号集
sigfillset(sigset_t *set); 全部置1
sigaddset(sigset_t *set, int signum); 将一个信号添加到集合中
sigdelset(sigset_t *set, int signum); 将一个信号从集合中移除
sigismember(const sigset_t *set,int signum); 判断一个信号是否在集合中。
设定对信号集内的信号的处理方式(阻塞或不阻塞)
#include
int sigprocmask( int how, const sigset_t *restrict set, sigset_t *restrict oset );
返回值:若成功则返回0,若出错则返回-1
首先,若oset是非空指针,那么进程的当前信号屏蔽字通过oset返回。
其次,若set是一个非空指针,则参数how指示如何修改当前信号屏蔽字。
how可选用的值:(注意,不能阻塞SIGKILL和SIGSTOP信号)
SIG_BLOCK : 把参数set中的信号添加到信号屏蔽字中
SIG_UNBLOCK: 从信号屏蔽字中删除参数set中的信号
SIG_SETMASK: 把信号屏蔽字设置为参数set中的信号
int pause(void);
进程一直阻塞,直到被信号中断,返回值:-1 并设置errno为EINTR
函数行为:
int sigsuspend(const sigset_t *sigmask);
功能:将进程的屏蔽字替换为由参数sigmask给出的信号集,然后挂起进程的执行
参数:
sigmask:希望屏蔽的信号
例子:
#include
#include
#include
void handle(int sig) {
printf("I get sig = %d\n", sig);
}
int main () {
struct sigaction act;
act.sa_handler = handle;
sigemptyset(&act.sa_mask);
act.sa_flags = 0;
sigaction(SIGINT, &act, NULL);
sigset_t set;
sigemptyset(&set);
sigaddset(&set, SIGINT);
sigprocmask(SIG_BLOCK, &set, NULL);
sleep(5);
sigprocmask(SIG_UNBLOCK, &set, NULL);
while (1) {
sleep(1);
}
}
例子:
#include
#include
#include
void handle(int sig) {
printf("I get sig = %d\n", sig);
}
void Mytask() {
printf("My task start\n");
sleep(3);
struct sigaction act;
act.sa_handler = handle;
sigemptyset(&act.sa_mask);
act.sa_flags = 0;
sigaction(SIGINT, &act, NULL);
sigaction(SIGHUP, &act, NULL);
sigset_t set, set2;
sigemptyset(&set2);
sigaddset(&set, SIGHUP);
sigaddset(&set, SIGINT);
pause();
while(1) {
sigprocmask(SIG_BLOCK, &set, NULL);
Mytask();
// sigprocmask(SIG_UNBLOCK, &set, NULL);
// pause();
sigsuspend(&set2);
}
printf("After pause \n");
while (1) {
sleep(1);
}
} printf("My task end\n");
}
int main () {
struct sigaction act;
act.sa_handler = handle;
sigemptyset(&act.sa_mask);
act.sa_flags = 0;
sigaction(SIGINT, &act, NULL);
sigaction(SIGHUP, &act, NULL);
sigset_t set, set2;
sigemptyset(&set2);
sigaddset(&set, SIGHUP);
sigaddset(&set, SIGINT);
pause();
while(1) {
sigprocmask(SIG_BLOCK, &set, NULL);
Mytask();
// sigprocmask(SIG_UNBLOCK, &set, NULL);
// pause();
sigsuspend(&set2);
}
printf("After pause \n");
while (1) {
sleep(1);
}
}
发送端:
接收端:
#include
#include
int msgget(key_t key, int msgflg);
成功时返回消息队列的id,失败时返回EOF
key 和消息队列关联的key IPC_PRIVATE 或 ftok
msgflg 标志位 IPC_CREAT|0666 IPC_CREAT:没有创建,有则打开。
发送消息
#include
#include
int msgsnd(int msgid, const void *msgp, size_t size,
int msgflg);
成功时返回0,失败时返回-1
msgid 消息队列id
msgp 消息缓冲区地址
size 消息正文长度
msgflg 标志位 0 或 IPC_NOWAIT
msgflg:
0:当消息队列满时,msgsnd将会阻塞,直到消息能写进消息队列
IPC_NOWAIT:当消息队列已满的时候,msgsnd函数不等待立即返回
消息格式:
typedef struct{
long msg_type;
char buf[128];
}msgT;
注意:
#include
#include
int msgrcv(int msgid, void *msgp, size_t size, long msgtype,
int msgflg);
成功时返回收到的消息长度,失败时返回-1
msgid 消息队列id
msgp 消息缓冲区地址
size 指定接收的消息长度
msgtype 指定接收的消息类型
msgflg 标志位
msgtype:
msgtype=0:收到的第一条消息,任意类型。
msgtype>0:收到的第一条 msg_type类型的消息。
msgtype<0:接收类型等于或者小于msgtype绝对值的第一个消息。
例子:如果msgtype=-4,只接受类型是1、2、3、4的消息
msgflg:
0:阻塞式接收消息
IPC_NOWAIT:如果没有返回条件的消息调用立即返回,此时错误码为ENOMSG
MSG_EXCEPT:与msgtype配合使用返回队列中第一个类型不为msgtype的消息
#include
#include
int msgctl(int msgid, int cmd, struct msqid_ds *buf);
成功时返回0,失败时返回-1
msgid 消息队列id
cmd 要执行的操作 IPC_STAT / IPC_SET / IPC_RMID(删除)
buf 存放消息队列属性的地址
例子:
msgsnd.c
#include
#include
#include
#include
// 定义消息结构体
typedef struct {
long msg_type; // 消息类型,必须为长整型
char buf[128]; // 消息内容,最大长度为 128 字节
} msgT;
#define MSGLEN (sizeof(msgT) - sizeof(long)) // 消息长度
int main() {
key_t key;
int msgid;
int ret;
msgT msg;
// 使用 ftok 函数生成一个唯一的键值
key = ftok(".", 100);
if (key < 0) {
perror("ftok");
return 0;
}
// 使用 msgget 函数创建或获取一个消息队列
msgid = msgget(key, IPC_CREAT | 0666);
if (msgid < 0) {
perror("msgget");
return 0;
}
// 设置消息类型为 1,并发送消息
msg.msg_type = 1;
strcpy(msg.buf, "this msg type 1");
ret = msgsnd(msgid, &msg, MSGLEN, 0);
if (ret < 0) {
perror("msgsnd");
return 0;
}
// 以类似的方式发送其他类型的消息
msg.msg_type = 2;
strcpy(msg.buf, "this msg type 2");
ret = msgsnd(msgid, &msg, MSGLEN, 0);
if (ret < 0) {
perror("msgsnd");
return 0;
}
msg.msg_type = 3;
strcpy(msg.buf, "this msg type 3");
ret = msgsnd(msgid, &msg, MSGLEN, 0);
if (ret < 0) {
perror("msgsnd");
return 0;
}
msg.msg_type = 4;
strcpy(msg.buf, "this msg type 4");
ret = msgsnd(msgid, &msg, MSGLEN, 0);
if (ret < 0) {
perror("msgsnd");
return 0;
}
msg.msg_type = 5;
strcpy(msg.buf, "this msg type 5");
ret = msgsnd(msgid, &msg, MSGLEN, 0);
if (ret < 0) {
perror("msgsnd");
return 0;
}
// 消息发送完毕,程序结束
return 0;
}
msgrcv.c
#include
#include
#include
#include
// 定义消息结构体
typedef struct {
long msg_type; // 消息类型,必须为长整型
char buf[128]; // 消息内容,最大长度为 128 字节
} msgT;
#define MSGLEN (sizeof(msgT) - sizeof(long)) // 消息长度
int main() {
int msgid;
key_t key;
msgT msg;
int ret;
// 使用 ftok 函数生成一个唯一的键值,要与发送消息的程序使用相同的 key
key = ftok(".", 100);
if (key < 0) {
perror("ftok");
return 0;
}
// 使用 msgget 函数创建或获取一个消息队列,要与发送消息的程序使用相同的 key
msgid = msgget(key, IPC_CREAT | 0666);
if (msgid < 0) {
perror("msgget");
return 0;
}
int count = 0;
while (1) {
// 使用 msgrcv 函数接收消息,第二个参数是消息缓冲区,第三个参数是消息长度,第四个参数是消息类型
ret = msgrcv(msgid, &msg, MSGLEN, 0, 0);
if (ret < 0) {
perror("msgrcv");
return 0;
}
count++;
// 打印接收到的消息类型和内容
printf("Received msg type=%ld, buf=%s\n", msg.msg_type, msg.buf);
if (count > 3) {
break; // 接收到足够数量的消息后退出循环
}
}
// 使用 msgctl 函数删除消息队列
ret = msgctl(msgid, IPC_RMID, NULL);
if (ret < 0) {
perror("msgctl");
return 0;
}
return 0;
}
信号灯/信号量(semaphore)
概念:是不同进程间或一个给定进程内部不同线程间同步的机制。类似我们的
PV操作概念:
生产者和消费者场景
P(S) 含义如下:
if (信号量的值大于0) {
申请资源的任务继续运行;
信号量的值减一;
} else {
申请资源的任务阻塞;
}
V(S) 含义如下:
信号量的值加一;
if (有任务在等待资源) {
唤醒等待的任务,让其继续运行
}
Posix 有名信号灯
Posix 无名信号灯 (linux只支持线程同步)
System V 信号灯
有名信号灯打开:
sem_t *sem_open(const char *name, int oflag);
sem_t *sem_open(const char *name, int oflag,mode_t mode, unsigned int value);
参数:
name:name是给信号灯起的名字
oflag:打开方式,常用O_CREAT
mode:文件权限。常用0666
value:信号量值。二元信号灯值为1,普通表示资源数目
信号灯文件位置:/dev/shm
有名信号灯关闭
int sem_close(sem_t *sem);
有名信号灯的删除
int sem_unlink(const char* name);
例子:
sem_w.c
#include /* For O_* constants */
#include /* For mode constants */
#include
#include
#include
#include
#include
#include
#include
void delsemfile(int sig){
sem_unlink("mysem_w");
exit(0);
}
int main(){
sem_t *sem_r,*sem_w;
key_t key;
int shmid;
char *shmaddr;
struct sigaction act;
act.sa_handler = delsemfile;
act.sa_flags = 0;
sigemptyset(&act.sa_mask);
sigaction(SIGINT,&act,NULL);
key = ftok(".",100);
if(key<0){
perror("ftok");
return 0;
}
shmid = shmget(key,500,0666|IPC_CREAT);
if(shmid<0){
perror("shmget");
return 0;
}
shmaddr = shmat(shmid,NULL,0);
sem_r = sem_open("mysem_r",O_CREAT|O_RDWR,0666,0);
sem_w = sem_open("mysem_w",O_CREAT|O_RDWR,0666,1);
while(1){
sem_wait(sem_w);
printf(">");
fgets(shmaddr,500,stdin);
sem_post(sem_r);
}
}
sem_r.c
#include /* For O_* constants */
#include /* For mode constants */
#include
#include
#include
#include
#include
#include
#include
void delsemfile(int sig){
sem_unlink("mysem_r");
exit(0);
}
int main(){
sem_t *sem_r,*sem_w;
key_t key;
int shmid;
char *shmaddr;
struct sigaction act;
act.sa_handler = delsemfile;
act.sa_flags = 0;
sigemptyset(&act.sa_mask);
sigaction(SIGINT,&act,NULL);
key = ftok(".",100);
if(key<0){
perror("ftok");
return 0;
}
shmid = shmget(key,500,0666|IPC_CREAT);
if(shmid<0){
perror("shmget");
return 0;
}
shmaddr = shmat(shmid,NULL,0);
sem_r = sem_open("mysem_r",O_CREAT|O_RDWR,0666,0);
sem_w = sem_open("mysem_w",O_CREAT|O_RDWR,0666,1);
while(1){
sem_wait(sem_r);
printf("%s\n",shmaddr);
sem_post(sem_w);
}
}
无名信号灯初始化
int sem_init(sem_t *sem, int shared, unsigned int value);
参数:
sem:需要初始化的信号灯变量
shared: shared指定为0,表示信号量只能由初始化这个信号量的进程使用,不能在进程间使用,linux 不支持进程间同步。
Value:信号量的值
无名信号灯销毁
int sem_destroy(sem_t* sem);
信号灯P操作
int sem_wait(sem_t *sem);
获取资源,如果信号量为0,表示这时没有相应资源空闲,那么调用线程就将挂起,直到有空闲资源可以获取
信号灯V操作
int sem_post(sem_t *sem);
释放资源,如果没有线程阻塞在该sem上,表示没有线程等待该资源,这时该函数就对信号量的值进行增1操作,表示同类资源多增加了一个。如果至少有一个线程阻塞在该sem上,表示有线程等待资源,信号量为0,这时该函数保持信号量为0不变,并使某个阻塞在该sem上的线程从sem_wait函数中返回
注意:编译posix信号灯需要加pthread动态库。
例子:
sem_thread.c
#include /* For O_* constants */
#include /* For mode constants */
#include
#include
#include
#include
#include
#include
#include
#include
sem_t sem_r,sem_w;
char *shmaddr;
void destroysem(int sig){
// sem_unlink("mysem_w");
sem_destroy(&sem_r);
sem_destroy(&sem_w);
exit(0);
}
void *readmem(void *arg){
while(1){
sem_wait(&sem_r);
printf("%s\n",shmaddr);
sem_post(&sem_w);
}
}
int main(){
key_t key;
int shmid;
struct sigaction act;
act.sa_handler = destroysem;
act.sa_flags = 0;
sigemptyset(&act.sa_mask);
sigaction(SIGINT,&act,NULL);
key = ftok(".",100);
if(key<0){
perror("ftok");
return 0;
}
shmid = shmget(key,500,0666|IPC_CREAT);
if(shmid<0){
perror("shmget");
return 0;
}
shmaddr = shmat(shmid,NULL,0);
sem_init(&sem_r,0,0);
sem_init(&sem_w,0,1);
pthread_t tid;
pthread_create(&tid,NULL,readmem,NULL);
while(1){
sem_wait(&sem_w);
printf(">");
fgets(shmaddr,500,stdin);
sem_post(&sem_r);
}
}
System V 信号灯使用:
int semget(key_t key, int nsems, int semflg);
功能:创建/打开信号灯
参数:key:ftok产生的key值(和信号灯关联的key值)
nsems:信号灯集中包含的信号灯数目
semflg:信号灯集的访问权限,通常为IPC_CREAT |0666
返回值:成功:信号灯集ID ; 失败:-1
int semop ( int semid, struct sembuf *opsptr, size_t nops);
功能:对信号灯集合中的信号量进行P - V操作
参数:semid:信号灯集ID
struct sembuf {
short sem_num; // 要操作的信号灯的编号
short sem_op; // 1 : 释放资源,V操作
// -1 : 分配资源,P操作
short sem_flg; // 0(阻塞),IPC_NOWAIT, SEM_UNDO
};//对某一个信号灯的操作,如果同时对多个操作,则需要定义这种结构体数组
nops: 要操作的信号灯的个数 ,1个
返回值:成功 :0 ; 失败:-1
int semctl ( int semid, int semnum, int cmd…/union semun arg/);
功能:信号灯集合的控制(初始化/删除)
参数:semid:信号灯集ID
semnum: 要操作的集合中的信号灯编号
cmd:
GETVAL:获取信号灯的值,返回值是获得值
SETVAL:设置信号灯的值,需要用到第四个参数:共用体
IPC_RMID:从系统中删除信号灯集合
返回值:成功 0 ; 失败 -1
例子:
sysvsem.c
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define SEM_READ 0
#define SEM_WRITE 1
union semun {
int val;
};
void Poperation(int semid,int semindex){
struct sembuf sbuf;
sbuf.sem_num = semindex;
sbuf.sem_op = -1;
sbuf.sem_flg = 0;
semop(semid,&sbuf,1);
}
void Voperation(int semid,int semindex){
struct sembuf sbuf;
sbuf.sem_num = semindex;
sbuf.sem_op = 1;
sbuf.sem_flg = 0;
semop(semid,&sbuf,1);
}
int main(){
key_t key;
char *shmaddr;
int semid,shmid;
key = ftok(".",100);
if(key<0){
perror("ftok");
return 0;
}
semid = semget(key,2,IPC_CREAT |0666);
if(semid<0){
perror("semget");
return 0;
}
shmid = shmget(key,500,IPC_CREAT |0666);
shmaddr = shmat(shmid,NULL,0);
union semun mysem;
mysem.val = 0;
semctl(semid,SEM_READ,SETVAL,mysem);
mysem.val = 1;
semctl(semid,SEM_WRITE,SETVAL,mysem);
pid_t pid;
pid = fork();
if(pid<0){
perror("fork");
shmctl(shmid,IPC_RMID,NULL);
semctl(semid,0,IPC_RMID);
exit(-1);
}else if(pid == 0){
while(1){
Poperation(semid,SEM_READ);
printf("%s\n",shmaddr);
Voperation(semid,SEM_WRITE);
}
}else{
while(1){
Poperation(semid,SEM_WRITE);
printf(">");
fgets(shmaddr,32,stdin);
Voperation(semid,SEM_READ);
}
}
}