Linux 进程及进程之间的通信机制——管道

参考: LInux C编程从初学到精通 电子工业出版社

Linux 进程

Linux 进程简介

Linux是一个多用户多任务的操作系统,多用户是指多个用户可以在同一时间使用同一台计算机系统;多用户是指Linux可以同时执行几个任务,它可以在还未执行完一个任务时又执行另一个任务,操作系统管理着多个用户的请求和多个任务。

Linux系统中所有运行的东西都可以称之为一个进程。每个用户任务、每个系统管理,都可以称之为进程,Linux用分时管理方法使所有的任务共同分享系统资源。

归结起来,进程具有以下4个要素:

  1. 要有一段程序供该进程运行。
  2. 进程专用的系统堆栈空间。
  3. 进程控制块(PCB),在Linux中的具体实现是task_struct结构。
  4. 有独立的存储空间。

Linux操作系统包括三种不同类型的进程,每种进程都有自己的特点和属性:

  1. 交互进程:由一个shell启动的进程。交互进程既可以在前台运行,也可以在后台运行。
  2. 批处理进程:这种进程和终端没有联系,是一个进程序列
  3. 监控进程(守护进程):Linux系统启动时启动的进程,并在后台运行。

进程相关的主要函数

fork() 创建一个子进程,若成功,父进程中返回子进程id,子进程中返回0;若出错则返回-1;

#include<sys/types.h>
#include<unistd.h>
pid_t fork(void);

需要注意:

  1. fork()系统调用的作用是复制一个进程。当一个进程调用它,完成后就会出现两个几乎一摸一样的进程。
  2. fork()函数被调用一次,但是返回两次。在子进程中返回的是0,在父进程中返回的是子进程id。

getpid() 返回进程的进程id

#include<sys/types.h>
#include<unistd.h>
pid_t getpid(void);

exec函数族

#include<unistd.h>
int execl(const char*pathname, const char *arg, ...);
int execlp(const char*filename, const char *arg, ...);
int execle(const char*pathname, const char*arg, ..., char *const envp[]);
int execv(const char*pathname, char*const argv[]);
int execvp(const char*filename, char*const argv[]);
int execve(const char*pathname, char*const argv[], char*cons envp[]);

6 个函数若成功则无返回值,若出错返回-1;

需要注意:

  1. 函数名中有字母 ‘l’ 的,其参数个数不定,参数由所调用程序的命令行参数列表组成,最后一个NULL表示结束。
  2. 函数名中有字母 ‘v’ 的,则是使用一个字符串数组指针argv指向参数列表,这一字符串数组和含有字母 ‘l’ 的函数中的参数列表完全相同,也同样以NULL结束。
  3. 函数名中有字母 ‘p’ 的,可以自动在环境变量PATH指定的路径中搜索要执行的程序,因此它的第一个参数为filename,表示可执行程序的文件名。而其他函数则需要用户在参数中指定该程序,所以其第一个参数为pathname。
  4. 函数名中含有 ’e’ 的,比其他函数多含有一个参数envp,这是一个字符串数组指针,用于指定环境变量。调用这样的函数(execle和execve)时,可以由用户自行设定子进程的环境变量,存放在参数的envp所指向的字符串数组中。这个字符串数组也必须由NULL结束。其他函数则是接受当前的环境变量。

简单示例:

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

int main(){
	//char *envp[]={"THIS=test",NULL};
	//char *argv[]={"echo","this is a test",NULL};
	//char *argve[]={"env",NULL};
	//execl("/usr/bin/ls","ls","-a",NULL);
	//execlp("echo","echo","this is a test",NULL);	
	//execle("/usr/bin/env","env",NULL,envp);
	//execv("/bin/echo",argv);
	//execvp("echo",argv);
	//execve("/usr/bin/env",argve,envp);
	return 0;
}

普遍情况下,如果一个进程想要执行另一个程序,它可以fork一个新进程,然后调用任何一个exec(使用system函数更简单)。

**exit(int) 终止一个进程 **

#include<stdlib.h>
void exit(int status);

在一个进程调用了exit之后,该进程并非马上就消失掉,而是留下一个僵尸进程的数据结构。

wait和waitpid函数:进程等待的系统调用

#include<sys/types.h>
#include<sys/wait.h>
pid_t wait(int *status);
pid_t waitpid(pid_t pid, int *status, int options);

两函数:若成功则返回进程id,若出错则返回-1;
对于waitpid如果设置了WNOHANG选项,返回0表示没有已退出的子进程可收集。

需要注意:

  1. 系统一旦调用了wait,就立即阻塞自己,由wait自动分析是否当前进程中的某个子进程已经退出,如果让它找到了这样一个已经变成僵尸的子进程,wait就会手机这个子进程的信息,并把它彻底销毁后返回,如果没有找到这样一个子进程,wait就会一直阻塞在这里。
  2. 从本质上讲waitpid和wait的作用是完全相同的,但waitpid多了两个可由用户控制的参数pid和options,从而为用户变成提供了一种更为灵活的方式。

pid取值及其含义:
在这里插入图片描述options选项:
WNOHANG:即使没有子进程退出,它也会立即返回
WUNTRRACED:极少用到

进程之间的通信

管道

Linux环境下使用pipe函数创建一个匿名管道,其函数原型如下:

#include<unistd.h>
int pipe(int fd[2]);

参数fd[2]是一个长度为2的文件描述符数组,fd[0]是读出端的文件描述符,fd[1]是写入端的文件描述符。

管道的读写
可以使用read和write函数对管道进行读写操作,需要注意的是,管道的两端是固定了任务的,即管道的读写端智能用于读取数据,管道的写入端则只能用于写入数据。如果试图从管道写端读取数据,或者和向管道读端写入数据都将导致错误发生。

父子进程之间通信:

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

int main(){	

	int fd[2];
	char inbuf[256]="this is a test!\n";
	char outbuf[256]={0};
	pid_t pid;
	int len;
	if(pipe(fd)<0){
		printf("create the pipe failed!\n");
		exit(1);
	}
	
	if((pid=fork())<0){
		printf("fork failed!\n");
		exit(1);
	}
	else if(pid>0){
		close(fd[0]);
		write(fd[1],inbuf,16);
		exit(0);
	}
	else{
		close(fd[1]);
		len=read(fd[0],outbuf,256);
		if(len<0){
			printf("read pipe failed\n");
			exit(1);
		}
		printf("%s",outbuf);
	}

	return 0;
}   

兄弟进程之间通信

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

int main(){
	int fd[2];
	char inbuf[256]="this is a test!\n";
	char outbuf[256]={0};
	int len;
	pid_t pid;

	if(pipe(fd)<0){
		printf("create pipe failed!\n");
		exit(1);
	}
	if((pid=fork())<0){
		printf("fork failed!\n");
		exit(1);
	}
	else if(pid==0){
		close(fd[0]);
		write(fd[1],inbuf,16);
		exit(0);
	}
	if((pid=fork())<0){
		printf("fork failed!\n");
		exit(1);
	}
	else if(pid==0){
		close(fd[1]);
		len=read(fd[0],outbuf,256);
		printf("%s",outbuf);
	}

	close(fd[0]);close(fd[1]);
	return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值