linux多进程编程

创建进程

替换进程映像

exec系列函数可以创建(启动)新进程,并替换原进程的进程映像。exec系列函数如下所示:

#include <unistd.h>
int execl(const char *path, const char *arg, ...,(char  *) NULL);
int execlp(const char *file, const char *arg, ...,(char  *) NULL);
int execle(const char *path, const char *arg, ..., (char *) NULL, char * const envp[]);
int execv(const char *path, char *const argv[]);
int execvp(const char *file, char *const argv[]);
int execvpe(const char *file, char *const argv[],char *const envp[]);

这些函数按参数个数是否可变,可以分为两类。
①execl()、execlp()、execle(),他们的特点是:
1)参数个数可变;
2)最后一个arg参数为(char *) NULL。
3)函数名中带字母“l”。

②execv()、execp()、execvp(),他们的特点是:
1)参数个数不可变;
2)第二个参数是一个字符串数组,数组的最后一个元素需是(char *) NULL。
3)函数名中带字母“v”。

按第一个参数的类型,可以分为两类。
①execlp()、execvp()、execvpe(),他们的特点是:
1)第一个参数名为“file”;
2)第一个参数为被调用(启动)的可执行文件名,函数会通过PATH环境变量搜索该可执行文件,如果该文件不在PATH定义的路径中,则需要使用全路径名。
3)函数名中带字母“p”,表是可以通过PATH环境变量搜索。

②execl()、execle()、execv(),他们的特点是:
1)第一个参数名为“path”;
2)第一个参数为被调用(启动)的可执行文件的路径名,无论PATH环境变量是否包含该可执行文件的路径。
3)函数名中不带字母“p”,表是不可以通过PATH环境变量搜索

示例1:

#include <stdio.h>
#include <unistd.h>
void exec1()
{	
	printf("enter exec1\n");
	const char *path = "/bin/ps";//此处需要使用路径名
	const char *arg = "ps";
	const char *arg1 = "";
	execl(path,arg, (char	*) NULL);
	printf("leave exec1\n");
}
void main()
{
	printf("enter main\n");
	exec1();
	printf("leave main\n");
}

打印结果:

enter main
enter exec1
   PID TTY          TIME CMD
 10305 pts/18   00:00:00 bash
 11156 pts/18   00:00:00 ps

分析:函数execl执行成功之后原进程映像被“ps”程序替换,故原进程中的

printf("leave exec1\n");
printf("leave main\n");

这两句为执行。

示例2:

#include <stdio.h>
#include <unistd.h>
void exec2()
{	
	printf("enter exec2\n");
	const char *file = "ps";//此处使用可执行文件名
	const char *arg = "ps";
	const char *arg1 = "";
	execlp(file,arg, (char	*) NULL);
	printf("leave exec2\n");
}
void main()
{
	printf("enter main\n");
	exec2();
	printf("leave main\n");
}

打印结果:

enter main
enter exec2
   PID TTY          TIME CMD
 10305 pts/18   00:00:00 bash
 11202 pts/18   00:00:00 ps

分析:函数execlp执行成功之后原进程映像被“ps”程序替换,故原进程中的

printf("leave exec2\n");
printf("leave main\n");

这两句为执行。

示例3:

#include <stdio.h>
#include <unistd.h>
void exec3()
{	
	printf("enter exec3\n");
	const char *path = "/bin/ps";
	const char *arg = "ps";
	const char *arg1 = "";
	char * const envp[] = {"/bin:/usr/bin",NULL};//此处环境变量参数可有可无。
	execle(path,arg, (char	*) NULL,NULL);
	printf("leave exec3\n");
}
void main()
{
	printf("enter main\n");
	exec3();
	printf("leave main\n");
}

打印结果:

enter main
enter exec3
   PID TTY          TIME CMD
 10305 pts/18   00:00:00 bash
 11216 pts/18   00:00:00 ps

分析:函数execle执行成功之后原进程映像被“ps”程序替换,故原进程中的

printf("leave exec3\n");
printf("leave main\n");

这两句为执行。

示例4:

#include <stdio.h>
#include <unistd.h>
void exec4()
{	
	printf("enter exec4\n");
	const char *path = "ps";//"/bin/ps"
	char *const argv[] = {"ps",NULL};
	execv(path,argv);
	printf("leave exec4\n");
}
void main()
{
	printf("enter main\n");
	exec4();
	printf("leave main\n");
}

打印结果:

enter main
enter exec4
leave exec4
leave main

分析:由于函数execv第一个参数未使用路径名,故execv执行失败,创开启新进程。

示例5:

#include <stdio.h>
#include <unistd.h>
void exec5()
{	
	printf("enter exec5\n");
	const char *file = "ps";
	char *const argv[] = {"ps",NULL};
	execvp(file,argv);
	printf("leave exec5\n");
}
void main()
{
	printf("enter main\n");
	exec5();
	printf("leave main\n");
}

打印结果:

enter main
enter exec5
   PID TTY          TIME CMD
 10305 pts/18   00:00:00 bash
 11246 pts/18   00:00:00 ps

分析:函数execvp执行成功之后原进程映像被“ps”程序替换,故原进程中的

printf("leave exec5\n");
printf("leave main\n");

这两句为执行。

示例6:

#include <stdio.h>
#include <unistd.h>
void exec6()
{	
	printf("enter exec6\n");
	const char *file = "ps";
	char *const argv[] = {"ps",NULL};
	char * const envp[] = {"/bin:/usr/bin",NULL};
	execvpe(file,argv,envp);
	printf("leave exec6\n");
}
void main()
{
	printf("enter main\n");
	exec6();
	printf("leave main\n");
}

打印结果:

enter main
enter exec6
   PID TTY          TIME CMD
 10305 pts/18   00:00:00 bash
 11258 pts/18   00:00:00 ps

分析:函数execvpe执行成功之后原进程映像被“ps”程序替换,故原进程中的

printf("leave exec6\n");
printf("leave main\n");

这两句为执行。

复制进程映像

如果想要进程同时执行多个函数,可以调用fork函数创建一个与原进程完全分离的进程。这个系统调用复制当前进程,并在进程表中创建一个新的表项,该表项与原进程表项基本相同。新进程与原进程几乎一模一样,执行的代码也完全相同,但新进程有自己独立的数据空间、环境和文件描述符。fork函数的部分manual如下所示:

NAME
       fork - create a child process
SYNOPSIS
       #include <unistd.h>
       pid_t fork(void);
RETURN VALUE
       On success, the PID of the child process is returned in the parent, and
       0 is returned in the child.  On failure, -1 is returned in the  parent,
       no child process is created, and errno is set appropriately.

fork用法介绍

  • fork函数执行成功后会复制一份当前进程(父进程)的进程映像,即子进程。此时子进程的PID会返回给父进程,“0”会返回给子进程。
  • fork函数执行失败则返回“-1”给父进程。
    用法示例
#include <stdio.h>
#include <unistd.h>

int main()
{
	pid_t pid;
	pid = fork();
	if(pid == -1){
		printf("fork failed\n");
		return 1;
	}else if(pid){
		printf("The father return value is %d\n",pid);
		printf("The father pid is %d\n",getpid());		//获取进程id
		printf("The father ppid is %d\n",getppid());	//获取父进程id
		while(1);
	}else{
		printf("The child return value is %d\n",pid);
		printf("The child pid is %d\n",getpid());	//获取进程id
		printf("The child ppid is %d\n",getppid());	//获取父进程id
		while(1);
	}
	return 0;
}
The father return value is 12002
The father pid is 12001
The father ppid is 10305
The child return value is 0
The child pid is 12002
The child ppid is 12001

等待进程

系统调用wait可以挂起父进程,等待子进程结束。

NAME
       wait, waitpid, waitid - wait for process to change state
SYNOPSIS
       #include <sys/types.h>
       #include <sys/wait.h>
       pid_t wait(int *status);
       pid_t waitpid(pid_t pid, int *status, int options);
       int waitid(idtype_t idtype, id_t id, siginfo_t *infop, int options);
                       /* This is the glibc and POSIX interface; see
                          NOTES for information on the raw system call. */

使用示例

#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
int main()
{
	pid_t pid;
	int status = 0;
	pid = fork();
	if(pid == -1){
		printf("fork failed\n");
		return 1;
	}else if(pid){
		printf("The father return value is %d\n",pid);
		wait(&status);
		printf("The father pid is %d\n",status);
		printf("The father pid is %d\n",getpid());
		printf("The father ppid is %d\n",getppid());
		//while(1);
	}else{
		printf("The child return value is %d\n",pid);
		printf("The child pid is %d\n",getpid());
		printf("The child ppid is %d\n",getppid());
		//while(1);
	}
	return 0;
}
The father return value is 12118
The child return value is 0
The child pid is 12118
The child ppid is 12117
The father pid is 0
The father pid is 12117
The father ppid is 10305

进程间通信

管道

  • 创建管道
#include <unistd.h>
int pipe(int pipefd[2]);

数组pipefd用于返回指向管道末端的两个文件描述符。pipefd[0]为管道的读出端,pipefd[1]为管道的写入端。
进程写入到管道写入端的数据由内核缓冲,直到另一进程从管道的读出端读取。

  • 应用示例
#include <sys/types.h>
#include <sys/wait.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>

int main(int argc, char *argv[])
{
    int pipefd[2];
    pid_t cpid;
    char buf;
    if (argc != 2) {
        fprintf(stderr, "Usage: %s <string>\n", argv[0]);
        exit(EXIT_FAILURE);
    }
    if (pipe(pipefd) == -1) {
        perror("pipe");
        exit(EXIT_FAILURE);
    }
    cpid = fork();
    if (cpid == -1) {
        perror("fork");
        exit(EXIT_FAILURE);
    }
    if (cpid == 0) {    /* Child reads from pipe */
        close(pipefd[1]);          /* Close unused write end */
        while (read(pipefd[0], &buf, 1) > 0)
            write(STDOUT_FILENO, &buf, 1);
        write(STDOUT_FILENO, "\n", 1);
        close(pipefd[0]);
        _exit(EXIT_SUCCESS);
    } else {            /* Parent writes argv[1] to pipe */
        close(pipefd[0]);          /* Close unused read end */
        write(pipefd[1], argv[1], strlen(argv[1]));
        close(pipefd[1]);          /* Reader will see EOF */
        wait(NULL);                /* Wait for child */
        exit(EXIT_SUCCESS);
    }
}

信号量

消息队列

共享内存

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值