(转)I/O多路复用详解(三)
(转)并发网络编程学习之路(二):多进程与进程池(续)

(整理)并发编程之一:多进程

孔令春 posted @ 2009年10月18日 21:37 in 网络安全 with tags 多进程编程 , 5037 阅读

        多进程编程的主要内容包括进程控制和进程间通信:

一、Linux下进程控制

        在传统的Unix环境下,有两个基本的操作用于创建和修改进程:

         fork():用来创建一个新的进程,该进程几乎是当前进程的一个完全拷贝;

         exec函数族:用来启动外部程序以取代当前的进程(由于此类函数并不是只有一个,而是六个,所以统称exec函数族)。

下面分别介绍一下:

1、 fork函数

        fork在英文中是“分叉”的意思。为什么取这个名字呢?因为一个进程在运行中,如果使用了fork,就产生了另一个进程,于是进程就“分叉”了,所以这个名字取得很形象。下面来看一下它的格式:

#include <sys/types.h>
#include <unistd.h>
pid_t fork(void);
    正确返回:在父进程中返回子进程的进程号,在子进程中返回0
    错误返回:-1

      使用fork函数后,系统就会在此时新建一个进程(称为子进程)并且拷贝原有进程(称为父进程)的上下文和数据(子进程和父进程使用相同的代码段;子进程复制父进程的堆栈段和数据段。),并且利用fork函数的不同返回值(>0表示父进程,0表示子进程,-1表示出错)来标识。这样一来,子进程就会和父进程彼此独立运行,互不干扰。下面就看看如何具体使用fork,这段程序演示了使用fork的基本框架:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>

int main(void)
{
        pid_t pid=fork();
        if(pid==0)
        {
                int j ;
                for(j=0;j<10;j++)
                {
                        printf("child: %d\n",j);
                        sleep(1);
                }
        }
        else if (pid>0)
        {
                int i;
                for(i=0;i<10;i++)
                {
                        printf("parent: %d\n",i);
                        sleep(1);
                }
        }
        else
        {
                fprintf(stderr,"can't fork ,error %d\n",errno);
                exit(1);
        }
        printf("This is the end !\n");
        return 0;
}

     运行了这段代码,我想应该可以明白fork了吧。运行的时候可以查看进程(ps -aux),会发现有两个一样的进程,运行结束后最后一句printf会运行两次,因为每个进程都会运行一次.中间的交替就是进程的调度了。

 

 2、exec函数族

       如果想运行外部程序就要用到exec函数族,这种函数一共有六个,它们分别是:

/*
 * 版权声明:转载时请以超链接形式标明文章原始出处和作者信息及本声明
 * http://tuhao.blogbus.com/logs/22833492.html
*/

extern char **environ;

int execl(const char* fullpath, const char* arg, ...);
int execlp(const char* file, const char* arg, ...);
int execle(const char* fullpath, const char* arg , ..., char* const envp[]);
int execv(const char* fullpath, char* const argv[]);
int execvp(const char* file, char* const argv[]);
int execve(const char* fullpath, const char* arg[] , char* const envp[]);

int execl(const char* fullpath, const char* arg, ....)
使用范例:execl(“/bin/ls”, ”ls”, ”-al”, NULL)

int execlp(const char* file, const char* arg, ....)
使用范例:execlp(“ls”, ”ls”, ”-al”, NULL)

int execle(const char* fullpath, const char* arg, ...., char* const envp[])
使用范例:execle(“/bin/ls”, ”ls”, ”-al”, NULL, environ)

int execv(const char * fullpath, char* const argv[])
使用范例:execle(“/bin/mkdir”, argv) // int main(int argc, char* argv[])
或
char* const p[] = {"a.out", "testDir", NULL};
execv("/bin/mkdir", p);

int execvp(const char* file, const char* arg, ....)
使用范例:execlp(“ls”, argv) // int main(int argc, char* argv[])
或
char* const p[] = {"a.out", "testDir", NULL};
execvp("mkdir", p);

int execve(const char* fullpath, const char* arg, ...., char* const envp[])
使用范例:execve(“/bin/ls”, argv, environ)
或
char* const p[] = {"a.out", "testDir", NULL};
execve("/bin/mkdir", p);

 下面一段代码显示如何配合fork函数启动运行其它程序:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>

char command[256];
int main()
{
   int rtn; /*子进程的返回数值*/
   while(1) 
   {
        /* 从终端读取要执行的命令 */
		printf( ">" );
		fgets( command, 256, stdin );
		command[strlen(command)-1] = 0;
		if ( fork() == 0 ) {
			/* 子进程执行此命令 */
			execlp( command, command, NULL);
			/* 如果exec函数返回,表明没有正常执行命令,打印错误信息*/
			perror( command );
			exit(errno);
		}
		else {
			/* 父进程, 等待子进程结束,并打印子进程的返回值 */
			wait ( &rtn );
			printf( " child process return %d\n",rtn );
		}
	}
} 

 

 二、Linux下的进程间通信

      创建一个新进程后,两个进程就会彼此独立运行,互不干扰,那么如果需要两个进程之间通信,该怎么办呢?在Unix/Linux下常用的进程间通信的方法有很多种,如管道、消息队列、共享内存、信号量、套接口等。下面我们以管道为例来介绍:

     管道是进程间通信中最古老的方式,它包括无名管道和有名管道两种,前者用于父进程和子进程间的通信,后者用于运行于同一台机器上的任意两个进程间的通信。

1、(无名)管道

无名管道由pipe函数创建:

       #include <unistd.h>

       int pipe(int filedis[2]);

      参数filedis返回两个文件描述符:filedes[0]为读而打开;filedes[1]为写而打开。filedes[1]的输出是filedes[0]的输入。下面的例子示范了如何在父进程和子进程间实现通信。

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>

#define INPUT 0
#define OUTPUT 1

int main() 
{
	int file_descriptors[2];
	/*定义子进程号 */
	pid_t pid;
	char buf[256];
	int returned_count;

	/*创建无名管道*/
	pipe(file_descriptors);
	
	/*创建子进程*/
	if((pid = fork()) == -1) 
	{
		printf("Error in fork\n");
		exit(1);
	}

	/*执行子进程*/
	if(pid == 0) 
	{
		printf("in the spawned (child) process...\n");

		/*关闭管道的读端,并且将数据"test data"从写端写入*/
		close(file_descriptors[INPUT]);
		write(file_descriptors[OUTPUT], "test data", strlen("test data"));
		exit(0);
	}
	else 
	{
		/*执行父进程*/
		printf("in the spawning (parent) process...\n");

		/*关闭管道的写端,父进程从管道读取子进程传来的数据*/
		close(file_descriptors[OUTPUT]);
		returned_count = read(file_descriptors[INPUT], buf, sizeof(buf));

		printf("%d bytes of data received from spawned process: %s\n",
			returned_count, buf);
	}
}

2、有名管道     

在Linux系统下,有名管道可由两种方式创建:命令行方式mknod和函数mkfifo。下面的两种途径者在当前目录下生成了一个名为myfifo的有名管道:

              方式一:mknod myfifo p          //p-表示创建FIFO特殊文件

              方式二:mkfifo("myfifo", "rw")  //rw表示读、写两种权限

      生成了有名管道后,就可以使用一般的文件I/O函数如open、close、read、write等来对它进行操作。下面是一个简单的例子:

/*
 * fifo_read.c
 * 管道通信:有名管道
 * 无名管道只能用于具有亲缘关系的进程之间,而有名管道可以在互不相关的两个进程间
 * 实现彼此通信。要注意,FIFO严格按照先进先出的规则,对管道及FIFO的读总是从开始
 * 处返回数据,对它们的写则把数据添加到末尾,不支持lseek等文件定位操作。
 *
 * 有名管道的创建使用mkfifo()。创建成功后就可以使用open、read、write这些函数了。
 * 读管道部分
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

/*在这里设置打开管道文件的mode为只读形式*/
#define FIFOMODE (O_CREAT | O_RDWR | O_NONBLOCK)
#define OPENMODE (O_RDONLY | O_NONBLOCK)
#define FIFO_SERVER "myfifo"

int main(void)
{
        char buf[100];
        int fd;
        int readnum;

        /*创建有名管道,设置为可读写,无阻塞,如果不存在则按照指定权限创建*/
        if ((mkfifo(FIFO_SERVER, FIFOMODE) < 0) && (errno != EEXIST)) {
                printf("cannot create fifoserver\n");
                exit(1);
        }

        printf("Preparing for reading bytes... ...\n");

        /*打开有名管道,并设置非阻塞标志*/
        if ((fd = open(FIFO_SERVER, OPENMODE)) < 0) {
                perror("open");
                exit(1);
        }

        while (1) {
                /*初始化缓冲区*/
                bzero(buf, sizeof(buf));
                /*读取管道数据*/
                if ((readnum = read(fd, buf, sizeof(buf))) < 0) {
                        if (errno == EAGAIN) {
                                printf("no data yet\n");
                        }
                }
                /*如果读到数据则打印出来,如果没有数据,则忽略*/
                if (readnum != 0) {
                        buf[readnum] = '\0';
                        printf("read %s from FIFO_SERVER\n", buf);
                }
                sleep(1);
        }

        return 0;
}
/*
 * fifo_wirte.c
 * 管道通信:有名管道
 * 无名管道只能用于具有亲缘关系的进程之间,而有名管道可以在互不相关的两个进程间
 * 实现彼此通信。要注意,FIFO严格按照先进先出的规则,对管道及FIFO的读总是从开始
 * 处返回数据,对它们的写则把数据添加到末尾,不支持lseek等文件定位操作。
 *
 * 有名管道的创建使用mkfifo()。创建成功后就可以使用open、read、write这些函数了。
 * 写管道部分
 */

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>

/*特别注意写管道时,设置打开管道文件的格式必须为可写*/
#define FIFO_SERVER "myfifo"
#define OPENMODE (O_WRONLY | O_NONBLOCK)

int main(int argc, char **argv)
{
        int fd;
        int nwrite;

        /*打开管道文件,可写非阻塞*/
        if ((fd = open(FIFO_SERVER, OPENMODE)) < 0) {
                perror("open");
                exit(1);
        }

        /*如果没有在命令行中写入参数,那么要重新运行程序*/
        if (argc == 1) {
                printf("Please send something\n");
                exit(1);
        }

        /*向管道文件中写入数据,在这里要用strlen,如果用sizeof,则只是4个字节的指针长度*/
        if ((nwrite = write(fd, argv[1], strlen(argv[1]))) < 0) {
                if (errno == EAGAIN) {
                        printf("The FIFO has not been read yet.Please try later\n");
                }
        }
        else {
                printf("write %s to FIFO\n", argv[1]);
        }

        return 0;
}

       测试过程,开两个窗口,先打开fifo_read,然后打开fifo_write写入数据,观察情况。

参考网址:http://www.linuxdiyf.com/viewarticle.php?id=6195

Avatar_small
LIC Premium Payment 说:
2022年8月06日 23:33

Life Insurance Corporation was established in 1956 and Is the oldest insurance company in India, and it has been providing millions of individuals with their variety of insurance schemes along with giving a good service to the business groups. To have a successful policy on your name, it is mandatory to make the LIC online premium payment or offline in time, LIC Premium Payment Online PayTm and timely paying the installments of LIC will keep your policy active and that will be helpful in your emergency.LIC does provide various methods to make the premium which can be office and as well online whichever is convenient to the customer, and it is also mandatory to check the payment dates, as it depends on the customer whether they have chosen for monthly, half-yearly or yearly.


登录 *


loading captcha image...
(输入验证码)
or Ctrl+Enter