【Linux编程】竞争条件
2014-06-10 11:20
441 查看
当多个进程都企图对共享数据进行某种处理,而最后的结果又取决于进程进行的顺序时,则我们认为这发生了竞争条件。一个例子是在fork出子进程之后,父、子进程的执行顺序是不确定的,这种不确定决定了后面程序的结果,那么这便产生了一个竞争条件。
如果一个进程希望等待一个子进程终止,则它必须调用wait或waitpid函数。如果一个进程要等待其父进程终止,则可使用如下的轮询方式:
这样的轮询需要休眠、唤醒等工作,很显然浪费了CPU资源。
如果要消除竞争条件而又不想使用轮询,则有如下两种方法:
信号机制
进程间通信(IPC)
这两种机制以后再讲,先来看看一个包含竞争条件的程序:
上述程序由fork产生了一个竞争条件。把标准输出的缓冲区大小设为了0,每输入一个字符就调用一次write系统调用,增大了进程运行时间,为的是让两个进程尽可能的切换。
运行结果:
可以看到,父、子进程交替输出结果。
解决办法1:使用信号
这里使用到了信号集的概念,把若干个信号放入一个以sigset_t类型表示的信号集中,然后调用相关接口实现了父、子进程的同步。运行结果如下:
解决办法2:使用管道
上述程序会建立如下进程关系:
处于等待的子进程调用read函数并阻塞,等待父进程发送字符。运行结果如下:
参考:
《unix环境高级编程》 P185-P188、P256-P273、P398-P403.
如果一个进程希望等待一个子进程终止,则它必须调用wait或waitpid函数。如果一个进程要等待其父进程终止,则可使用如下的轮询方式:
while (getppid() != 1) sleep(1);
这样的轮询需要休眠、唤醒等工作,很显然浪费了CPU资源。
如果要消除竞争条件而又不想使用轮询,则有如下两种方法:
信号机制
进程间通信(IPC)
这两种机制以后再讲,先来看看一个包含竞争条件的程序:
#include <stdio.h> #include <unistd.h> #include <stdlib.h> static void charatatime(char *str) { char *ptr; int c; setbuf(stdout, NULL); /* 标准输出设置为不带缓冲 */ for (ptr = str; (c = *ptr++) != 0; ) putc(c, stdout); } int main(void) { pid_t pid; pid = fork(); if (pid == 0) charatatime("output from child\n"); else charatatime("output from parent\n"); return 0; }
上述程序由fork产生了一个竞争条件。把标准输出的缓冲区大小设为了0,每输入一个字符就调用一次write系统调用,增大了进程运行时间,为的是让两个进程尽可能的切换。
运行结果:
可以看到,父、子进程交替输出结果。
解决办法1:使用信号
#include <signal.h> #include <stdio.h> #include <unistd.h> #include <stdlib.h> static volatile sig_atomic_t sigflag; static sigset_t newmask, oldmask, zeromask; /* 定义三个信号集 */ static void sig_usr(int signo) { sigflag = 1; } void TELL_WAIT(void) { if (signal(SIGUSR1, sig_usr) == SIG_ERR) exit(-1); if (signal(SIGUSR2, sig_usr) == SIG_ERR) exit(-1); sigemptyset(&zeromask); /* 初始化信号集 */ sigemptyset(&newmask); /* 初始化信号集 */ sigaddset(&newmask, SIGUSR1); /* 向信号集中添加信号 */ sigaddset(&newmask, SIGUSR2); /* 向信号集中添加信号 */ /* 更改信号屏蔽字,阻塞SIGUSR1和SIGUSR2两个信号 * SIG_BLOCK表示新的信号屏蔽字是当前信号屏蔽字和第二个参数newmask的并集 * oldmask保存当前的信号屏蔽字,以便稍后恢复用 */ if (sigprocmask(SIG_BLOCK, &newmask, &oldmask) < 0) exit(-1); } void TELL_PARENT(pid_t pid) { kill(pid, SIGUSR2); } void WAIT_PARENT(void) { while (sigflag == 0) sigsuspend(&zeromask); /* 将信号屏蔽字设置为zeromask并在捕捉到信号之前挂起进程 */ sigflag = 0; /* 恢复信号屏蔽字 * SIG_SETMASK表示新的信号屏蔽字为由oldmask参数提供 */ if (sigprocmask(SIG_SETMASK, &oldmask, NULL) < 0) exit(-1); } void TELL_CHILD(pid_t pid) { kill(pid, SIGUSR1); } void WAIT_CHILD(void) { while (sigflag == 0) sigsuspend(&zeromask); sigflag = 0; if (sigprocmask(SIG_SETMASK, &oldmask, NULL) < 0) exit(-1); } static void charatatime(char *str) { char *ptr; int c; setbuf(stdout, NULL); /* 标准输出设置为不带缓冲 */ for (ptr = str; (c = *ptr++) != 0; ) putc(c, stdout); } int main(void) { pid_t pid; TELL_WAIT(); pid = fork(); if (pid == 0) { WAIT_PARENT(); charatatime("output from child\n"); } else { charatatime("output from parent\n"); TELL_CHILD(pid); waitpid(pid, NULL, 0); } return 0; }
这里使用到了信号集的概念,把若干个信号放入一个以sigset_t类型表示的信号集中,然后调用相关接口实现了父、子进程的同步。运行结果如下:
解决办法2:使用管道
#include <stdio.h> #include <unistd.h> #include <stdlib.h> static int pfd1[2], pfd2[2]; void TELL_WAIT(void) { pipe(pfd1); /* 建立管道1 */ pipe(pfd2); /* 建立管道2 */ } void TELL_PARENT(pid_t pid) { if (write(pfd2[1], "c", 1) != 1) /* 写输出描述符 */ exit(-1); } void WAIT_PARENT(void) { char c; if (read(pfd1[0], &c, 1) != 1) /* 读输入描述符 */ exit(-1); if (c != 'p') exit(-1); } void TELL_CHILD(pid_t pid) { if (write(pfd1[1], "p", 1) != 1) /* 写输出描述符 */ exit(-1); } void WAIT_CHILD(void) { char c; if (read(pfd2[0], &c, 1) != 1) /* 读输入描述符 */ exit(-1); if (c != 'c') exit(-1); } static void charatatime(char *str) { char *ptr; int c; setbuf(stdout, NULL); /* 标准输出设置为不带缓冲 */ for (ptr = str; (c = *ptr++) != 0; ) putc(c, stdout); } int main(void) { pid_t pid; TELL_WAIT(); pid = fork(); if (pid == 0) { WAIT_PARENT(); charatatime("output from child\n"); } else { charatatime("output from parent\n"); TELL_CHILD(pid); waitpid(pid, NULL, 0); } return 0; }
上述程序会建立如下进程关系:
处于等待的子进程调用read函数并阻塞,等待父进程发送字符。运行结果如下:
参考:
《unix环境高级编程》 P185-P188、P256-P273、P398-P403.
相关文章推荐
- Linux安全编程:避免竞争条件
- 安全编程: 避免竞争条件
- 关于linux下c编程利用条件编译打印信息调试
- 【Linux C 多线程编程】互斥锁与条件变量
- linux脚本编程之条件判断
- Linux C 多线程编程----互斥锁与条件变量-转
- 安全编程: 避免竞争条件
- linux的条件变量和时间编程
- Linux编程学习之互斥锁和条件变量
- UNIX环境高级编程8.9竞争条件
- Linux下的C语言编程——查找介于n1与n2(0<n1<n2<32768)之间所有满足下列条件的整数
- Linux脚本(shell)编程(四) 判断条件
- Linux kernel ptrace功能竞争条件漏洞
- Linux脚本(shell)编程(四) 判断条件
- Linux高级编程复习 第十二章 多线程概念_互斥锁--_多线程同步_信号_条件量_信号量
- Linux脚本(shell)编程(四) 判断条件
- 安全编程: 避免竞争条件--资源争用可能对您不利
- Unix/Linux Shell 编程笔记——条件测试
- linux中避免竞争条件的途径
- linux笔记 第六天 bash编程入门之变量、条件判断、条件测试和for循环等