多进程开发

进程:
Linux系统至少有一个进程,一个程序可以对应多个进程,一个进程只能对应一个程序。
由于历史原因,大多数UNIX系统的main()函数定义为:
int main(int argc, char *argv[ ], char *envp[ ]);
argc表示参数argv有多少个字符串,argv表示一个不定长的字符串数组,envp以name=value的形式存放了一组进程中会用到的环境变量。

环境变量:
读取环境变量:getenv();
设定环境变量:putenv();
char *getenv(const char *name); //name是要获取的环境变量名字

POSIX定义的环境变量及其含义:
HOME:起始目录
LANG:本地名(本地语言类型)
LC_ALL:本地名
LC_COLLATE:本地排序名等等

在程序中获得环境变量:gcc path.c -o path

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

int main()
{
        char *env_path = "PATH";	
        char *env_value = NULL;		//环境变量值

        env_value = getenv(env_path);	//使用系统函数获取指定环境变量
        if(NULL==env_value)		//检查是否获取到变量的值
                printf("Not found!\n");
        printf("Get Env PATH:\n%s\n",env_value);	//输出PATH环境变量的值
        return 0;
}

【运行结果】
lwb@ubuntu:~/mmanager$ ./path
Get Env PATH:
/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/snap/bin:/home/lwb/SYSTEM/tools-master/arm-bcm2708/gcc-linaro-arm-linux-gnueabihf-raspbian-x64/bin

进程建立:
一个进程的基本属性主要包括:进程号,父进程号,进程组号,真实用户号,真实组号,有效用户号,有效组号。

fork()函数创建进程成功后,在此之后运行的已经是两个进程。子进程和父进程都可以得到fork()函数的返回值,对于子进程,返回值是0,对于父进程,返回值是子进程的进程号。如果创建子进程失败了,fork()函数会给父进程返回-1;

fork()函数创建子进程后,会复制父进程的数据段,代码段和堆栈空间等到子进程的空间。子进程可以共享父进程中打开的文件,即父进程已经打开的文件,在子进程中可以直接操作。

【fork()函数创建子进程】

#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>

int main()
{
        pid_t pid;
        pid=fork();				//创建进程
        if(pid==-1)				//创建进程失败
        {
        printf("creat child process is faild\n");
        return 0;
        }
        else if (pid==0)			//子进程
        {
        printf("child process is sucess--id:%d\n",pid);
        }
        else					//父进程
        {
        printf("Parent process! child process id:%d\n",pid);
        }

        return 0;
}

【运行结果】
lwb@ubuntu:~/mmanager$ gcc process.c -o process
lwb@ubuntu:~/mmanager$ ./process
Parent process! child process id:4524
lwb@ubuntu:~/mmanager$ child process is sucess–id:0

所有的进程都是从init进程创建来的,当进程结束的时候它的父进程会收回子进程的资源。但是父进程和子进程是无序进行的,此时需要调用waitpid()函数,等待一个进程结束。
pid_t waitpid(pid_t pid, int *status, int options);

【waitpid()函数等待子进程】

#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>

int main()
{
        pid_t pid, pid_wait;
        int status;

        pid=fork();					//创建子进程
        if(pid==-1)					//检查是否创建成功
        {
        printf("error to create new process!\n");	
        return 0;
        }
        else if(pid==0)					//子进程
        {
        printf("child process!\n");
        }
        else						//父进程
        {
        printf("parent process! chile process ID:%d\n",pid);
        pid_wait=waitpid(pid, &status, 0);			//等待指定号的子进程
        printf("child process %d returned!\n",pid_wait);
        }

        return 0;
}

【运行结果】
lwb@ubuntu:~/mmanager$ gcc wait.c -o wait
lwb@ubuntu:~/mmanager$ ./wait
parent process! chile process ID:5122
child process!
child process 5122 returned!

退出进程函数:exit() _exit() atexit() on_exit()。eixt()函数的作用是退出当前进程,并且尽可能释放当前进程占用的资源。
int atexit(void (*function)(void));
int on_exit(void (*function)(int , void *), void *arg);
void exit(int status);
void _exit(int status);

【atexit()函数退出回调函数例程】

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

void bye(void)
{
        printf("thid was all, folks\n");
}

void bye1(void)
{
        printf("this should called first!\n");
}

int main()
{
        long a;
        int i;

        i=atexit(bye);
        if(i!=0)
        {
        fprintf(stderr,"cannot set exit function bye\n");
        return EXIT_FAILURE;
        }

        i=atexit(bye1);
        if(i!=0)
        {
        fprintf(stderr,"cannot set exit function bye1\n");
        return EXIT_FAILURE;
        }
        return EXIT_SUCCESS;
}

【运行结果】
lwb@ubuntu:~/mmanager$ gcc exit.c -o exit
lwb@ubuntu:~/mmanager$ ./exit
this should called first!
thid was all, folks

不同的进程间需要传递数据时,会采取进程间通信,常见的包括管道,FIFO,消息队列,信号量,共享存储以及socket等。

管道通信:
管道通信有两个限制,一是管道通信是半双工的,二是管道只能在有共同父进程的进程间使用。通常,管道由一个进程创建,之后进程调用fork()创建新的进程,父进程和子进程之间就可以使用管道通信了。
int pipe(int filedes[2]); //参数fieldes返回两个文件描述符,filedes[0]为读打开,filedes[1]为写打开。当创建成功时pipe()返回0,如果创建失败则会返回-1。

【pipe()函数管道通信例程】

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

int main()
{
        int fd[2];
        pid_t pid;
        char buf[64]="I am parent process!\n";         //父进程要写入管道的信息
        char line[64];

        if(pipe(fd)!=0)                                 //创建管道并检查结果
        {
        fprintf(stderr,"fail to creat pipe!\n");
        return 0;
        }


        pid=fork();                                     //创建进程
        if(pid<0)
        {
        fprintf(stderr,"fail to creat process!\n");
        return 0;
        }
        else if(pid==0)                                 //父进程
        {
        close(fd[0]);                                   //关闭读管道,使得父进程只能向管道写入数据
        write(fd[1], buf, strlen(buf));                 //写数据到管道
        close(fd[1]);                                   //关闭写管道
        }
        else                                            //子进程
        {
        close(fd[1]);                                   //关闭写管道,使得子进程只能从管道读取数据
        read(fd[0], line, 64);                          //从管道读取数据
        printf("DATA from parent: %s", line);
        close(fd[0]);                                   //关闭读管道
        }

        return 0;
}

【运行结果】
lwb@ubuntu:~/mmanager$ vi pipe.c
lwb@ubuntu:~/mmanager$ gcc pipe.c -o pipe
lwb@ubuntu:~/mmanager$ ./pipe
DATA from parent: I am parent process! //子进程打印出了父进程通过管道发送来的字符串

共享内存:
共享内存是在内存中开辟一段空间,供不同的进程访问,即多个不同进程(非父子进程)间共享数据,比管道传送更大量的数据。
共享内存在使用之前需要先创建,之后获得共享内存的入口地址就可以对共享内存进行操作了。如果不需要使用共享内存的时候,还可以在程序中分离共享内存。
int shmget(key_t key, size_t size, int shmflg);
void *shmat(int shmid, const void *shmaddr, int shmflg);
int shmdt(const void *shmaddr);

shmget()函数用来创建共享内存。
shmat()函数是获得一个共享内存ID对应的内存起始地址。
shmdt()函数从程序中分离一块共享内存。

【shm_write.c写入共享内存操作】

#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/types.h>
#include <unistd.h>
#include <string.h>
#include <stdio.h>

int main()
{
        int shmid;              					//定义共享内存ID
        char *ptr;
        char *shm_str = "string in a share memory";
        shmid = shmget(0x90,1024,SHM_W|SHM_R|IPC_CREAT|IPC_EXCL);       //创建共享内存

        if(shmid==-1)
                perror("create share memory");

        ptr = (char *)shmat(shmid, 0, 0);                                    	//通过共享内存ID获得共享内存地址
        if((void *)-1==ptr)	
                perror("get share memory");

        strcpy(ptr, shm_str);                                         	  //把字符串写入共享内存
        shmdt(ptr);

        return 0;
}

【shm_read.c读取共享内存操作】

#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/types.h>
#include <unistd.h>
#include <string.h>
#include <stdio.h>

int main()
{
        int shmid;                                    				  //定义共享内存ID
        char *ptr;

        shmid = shmget(0x90, 1024, SHM_W|SHM_R|IPC_EXCL);     //根据key获得共享内存ID

        if(shmid==-1)
                perror("creare share memory");

        ptr = shmat(shmid, 0, 0);                             		  //通过共享内存ID获得共享内存地址
        if((void *)-1==ptr)
                perror("get share memory");

        printf("string in share memory: %s\n", ptr);       	     //打印共享内存中的内容

        shmdt(ptr);
        return 0;
}

【运行结果】
lwb@ubuntu:~/sharemem$ gcc shm_write.c -o w
lwb@ubuntu:~/sharemem$ gcc shm_read.c -o r
lwb@ubuntu:~/sharemem$ ./w
lwb@ubuntu:~/sharemem$ ./r
string in share memory: string in a share memory
lwb@ubuntu:~/sharemem$ ./w
create share memory: File exists
get share memory: Invalid argument
Segmentation fault (core dumped) //出错原因在于第二次写入时,已经有相同的key值的共享内存了,实例中退出的时候没有删除共享。
lwb@ubuntu:~/sharemem$ ipcs //查看目前共享资源情况
在这里插入图片描述
lwb@ubuntu:~/sharemem$ ipcrm -m 27 //成功删除共享内存

【进程编程实例】
实例中,父进程和子进程之间通过管道进行传输数据,父进程向子进程发送字符串exit表示子进程退出,并且等待子进程返回,子进程查询管道,当从管道读出字符串exit的时候结束。

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

int main()
{
	pid_t pid, pid_wait;
	int fd[2];
	char buff[64], *cmd="exit";

	if(pipe(fd))			//创建管道
	{
	perror("create pipe fail!");
	return 0;
	}
	
	pid = fork();
	if(pid==-1)
	{
	perror("create process fail!");
	return 0;
	}
	else if(pid==0)				//子进程
	{
	close(fd[1]);				//关闭写操作
	printf("wait command from parent!\n");
		while(1)
		{
		read(fd[0], buff, 64);
		if(strcmp(buff,cmd)==0)
		{
		printf("recv command ok!\n");
		close(fd[0]);
		exit(0);	
		}
		}
	}
	else					//父进程
	{
	printf("Parent process! child process id: %d\n", pid);
	close(fd[0]);				//关闭读操作
	sleep(2);
	printf("Send command to child process\n");
	write(fd[1], cmd, strlen(cmd)+1);	//写入命令
	close(fd[1]);
	}

	return 0;
}

【运行结果】
lwb@ubuntu:~/process$ gcc process.c -o process
lwb@ubuntu:~/process$ ./process
Parent process! child process id: 5762
wait command from parent!
Send command to child process
lwb@ubuntu:~/process$ recv command ok!

摘自:弓雷 ARM嵌入式Linux系统开发详解

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
在Windows系统中,多进程开发可以使用Windows API或者第三方库实现。以下是一个简单的多进程示例代码: ```c++ #include <windows.h> #include <iostream> using namespace std; int main() { STARTUPINFO si; PROCESS_INFORMATION pi; ZeroMemory(&si, sizeof(si)); si.cb = sizeof(si); ZeroMemory(&pi, sizeof(pi)); // 创建子进程 if (!CreateProcess(TEXT("child.exe"), NULL, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi)) { cerr << "Failed to create process." << endl; return 1; } // 等待子进程结束 WaitForSingleObject(pi.hProcess, INFINITE); // 关闭句柄 CloseHandle(pi.hProcess); CloseHandle(pi.hThread); return 0; } ``` 在这个示例中,我们使用了Windows API中的CreateProcess函数创建了一个子进程,并使用WaitForSingleObject函数等待子进程结束。需要注意的是,在使用CreateProcess函数时需要填写STARTUPINFO和PROCESS_INFORMATION结构体,其中STARTUPINFO结构体用来指定子进程的一些属性,如标准输入输出句柄,PROCESS_INFORMATION结构体用来接收子进程的句柄和线程句柄。 在子进程中,我们可以使用类似的方式创建进程: ```c++ #include <windows.h> #include <iostream> using namespace std; int main() { cout << "Child process is running." << endl; return 0; } ``` 这是一个简单的示例,实际应用中可能需要更加复杂的进程间通信和同步机制。可以使用Windows API中的管道、共享内存等机制实现。同时,也可以使用第三方库如Boost和Qt等来简化多进程开发

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值