进程同步的两种实现

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/rikeyone/article/details/88977157

在前文《Linux – 进程控制》博客中,我曾提到过父子进程之间的同步有两种方法:分别是基于管道和信号实现。
为什么需要进程的同步,当我们创建一个新进程时,为了保证父子进程的运行按照我们预期的时序进行,所以需要加入同步机制。下面直接以代码示例来实现两种同步方式。

基于管道实现的进程同步

#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include "include/debug.h"

static int	pfd1[2], pfd2[2];

void initialize(void)
{
	if (pipe(pfd1) < 0 || pipe(pfd2) < 0)
		err_exit("pipe error");
}

void wake_parent(void)
{
	if (write(pfd2[1], "c", 1) != 1)
		err_exit("write error");
}

void wait_parent(void)
{
	char	c;

	if (read(pfd1[0], &c, 1) != 1)
		err_exit("read error");

	if (c != 'p')
		err_exit("WAIT_PARENT: incorrect data");
}

void wake_child(void)
{
	if (write(pfd1[1], "p", 1) != 1)
		err_exit("write error");
}

void wait_child(void)
{
	char	c;

	if (read(pfd2[0], &c, 1) != 1)
		err_exit("read error");

	if (c != 'c')
		err_exit("WAIT_CHILD: incorrect data");
}


int main (int argc, char *argv[])
{
	pid_t pid;
	int ret;

	initialize();
	if ((pid = fork()) < 0) {
		err_exit("fork() error\n");
	} else if (pid > 0) { //parent
		wait_child();
		pr_info("parent waken up\n");
		pr_info("parent running\n");
		pr_info("wake up child\n");
		wake_child();
		wait(NULL);
	} else {
		pr_info("child running\n");
		pr_info("wake up parent\n");
		wake_parent();
		wait_parent();
		pr_info("child waken up\n");
	}
	return 0;
}

基于信号SIGUSR1和SIGUSR2实现的同步

#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <signal.h>
#include "include/debug.h"

static volatile sig_atomic_t sigflag; /* set nonzero by sig handler */
static sigset_t mask, omask, zeromask;

static void sig_usr(int signo)	/* one signal handler for SIGUSR1 and SIGUSR2 */
{
	pr_info("catch signal:%d\n", signo);
	sigflag = 1;
}

void initialize(void)
{
	if (signal(SIGUSR1, sig_usr) == SIG_ERR)
		err_exit("signal(SIGUSR1) error");
	if (signal(SIGUSR2, sig_usr) == SIG_ERR)
		err_exit("signal(SIGUSR2) error");

	sigemptyset(&zeromask);
	sigemptyset(&mask);
	sigaddset(&mask, SIGUSR1);
	sigaddset(&mask, SIGUSR2);

	if (sigprocmask(SIG_BLOCK, &mask, &omask) < 0)
		err_exit("SIG_BLOCK error");
}

void wake_parent(pid_t pid)
{
	kill(pid, SIGUSR2);		/* tell parent we're done */
}

void wait_parent(void)
{
	while (sigflag == 0)
		sigsuspend(&zeromask);	/* and wait for parent */
	sigflag = 0;

	/* Reset signal mask to original value */
	if (sigprocmask(SIG_SETMASK, &omask, NULL) < 0)
		err_exit("SIG_SETMASK error");
}

void wake_child(pid_t pid)
{
	kill(pid, SIGUSR1);			/* tell child we're done */
}

void wait_child(void)
{
	while (sigflag == 0)
		sigsuspend(&zeromask);
	sigflag = 0;

	/* Reset signal mask to original value */
	if (sigprocmask(SIG_SETMASK, &omask, NULL) < 0)
		err_exit("SIG_SETMASK error");
}

int main (int argc, char *argv[])
{
	pid_t pid;
	int ret;

	initialize();
	if ((pid = fork()) < 0) {
		err_exit("fork() error\n");
	} else if (pid > 0) { //parent
		wait_child();
		pr_info("parent waken up\n");
		pr_info("parent running\n");
		pr_info("wake up child\n");
		wake_child(pid);
		wait(NULL);
	} else {
		pr_info("child running\n");
		pr_info("wake up parent\n");
		wake_parent(getppid());
		wait_parent();
		pr_info("child waken up\n");
	}
	return 0;
}

其他方式

system v IPC、posix IPC这两种当然也可以实现进程同步,但是不在本文讲解。

猜你喜欢

转载自blog.csdn.net/rikeyone/article/details/88977157