Linux进程以及进程间管道通信

进程相关概念

程序和进程

程序:死的。只占用磁盘空间。
进程;活的。运行起来的程序。占用内存、cpu 等系统资源。

并发和并行

并发和并行:并行是宏观上并发,微观上串行
并发:是同一个cpu,执行不同的进程,使用不同的 时间片,只是有一个进程在执行
并行:是不同的cpu,使得多个进程同时的执行

进程控制块

struct task_struct {
	volatile long state;	/* -1 unrunnable, 0 runnable, >0 stopped */
	void *stack;
	atomic_t usage;
	unsigned int flags;	/* per process flags, defined below */
	unsigned int ptrace;

	//........

	struct pipe_inode_info *splice_pipe;
#ifdef	CONFIG_TASK_DELAY_ACCT
	struct task_delay_info *delays;
#endif
#ifdef CONFIG_FAULT_INJECTION
	int make_it_fail;
#endif
};

// /usr/src/linux-headers-3.16.0-30/include/linux/sched.h文件中可以查看struct task_struct 结构体定义。其内部成员有很多
/* 
    * 进程id。系统中每个进程有唯一的id,在C语言中用pid_t类型表示,其实就是一个非负整数。
    * 进程的状态,有就绪、运行、挂起、停止等状态。
    * 进程切换时需要保存和恢复的一些CPU寄存器。
    * 描述虚拟地址空间的信息。
    * 描述控制终端的信息。
    * 当前工作目录(Current Working Directory)。
    * umask掩码。
    * 文件描述符表,包含很多指向file结构体的指针。
    * 和信号相关的信息。
    * 用户id和组id。
    * 会话(Session)和进程组。
    * 进程可以使用的资源上限(Resource Limit)。
*/
  • 进程状态

进程基本的状态有5种。分别为初始态,就绪态,运行态 , 挂起态与终止态。
其中初始态为进程准备阶段,常与就绪态结合来看。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-iRlWn3RV-1671507698300)(file:///C:\Users\dongfang\AppData\Local\Temp\ksohtml9364\wps2.jpg)]

环境变量

常见的环境变量

1、PATH:用来记录文件的可执行路径,echo $PATH
2、SHELL:用来记录当前Shell,它的值通常是/bin/bash。echo $SHELL
3、TERM:当前终端类型,在图形界面终端下它的值通常是xterm,echo $TERM
4、LANG:语言和locale,决定了字符编码以及时间、货币等信息的显示格式。
5、HOME:当前用户主目录的路径,很多程序需要在主目录下保存配置文件,使得每个用户在运行该程序时都有自己的一套配置。

常用函数

1、getenv函数:获取环境变量值
    char *getenv(const char *name); 	
	成功:返回环境变量的值;失败:NULL (name不存在)
2、setenv函数:设置环境变量的值	
    int setenv(const char *name, const char *value, int overwrite);  	
	成功:0;失败:-1
	参数overwrite:若存在是否覆盖	
        1:覆盖原环境变量 
        若存在且overwrite == 0 则忽略value
		0:不覆盖。(该参数常用于设置新环境变量,如:ABC = haha-day-night)
3、unsetenv函数:删除环境变量name的定义
    int unsetenv(const char *name); 	
	成功:0;失败:-1 
	注意事项:name不存在仍返回0(成功),当name命名为"ABC="时则会出错。

查看当前进程所有环境变量

#include <stdio.h>

extern char **environ;//eg : environ[1] = "SHLVL=1"

int main(void){
	int i;
	for(i = 0; environ[i] != NULL; i++){
		printf("%s\n", environ[i]);
	}
	return 0;
}

获取name对应环境变量

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

extern char **environ;

char *my_getenv(const char *name){
	char *p = NULL;
	int i, len;

	for(i = 0; environ[i] != NULL; i++){
		p = strstr(environ[i], "=");//获取“=”所在位置
		len = p - environ[i];//获取环境变量name的长度
		if(strncmp(name, environ[i], len) == 0){//对比是否相等
			return p+1;//返回对应的环境变量
		}
	}
	return NULL;
}

int main(int argc, char *argv[]){
	char *p = NULL;

	p = getenv(argv[1]);
	//p = my_getenv(argv[1]);
    
	if (p == NULL)
		printf("there is no match\n");
	else
		printf("%s\n", p);
	return 0;
}
/*执行结果
ubuntu@ubuntu:~/LearnCPP/linux_test/path_test$ ./getenv PATH
/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/snap/bin
*/

setenv和unsetenv例子

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

int main(void)
{
	char *val;
	const char *name = "ABD";

	val = getenv(name);
	printf("1, %s = %s\n", name, val);

	setenv(name, "haha-day-and-night", 1);//设置覆盖环境变量值为haha-day-and-night

	val = getenv(name);
	printf("2, %s = %s\n", name, val);

#if 0
	unsetenv("ABD");//删除环境变量ABD
	val = getenv(name);
	printf("3, %s = %s\n", name, val);
#else
	int ret = unsetenv("ABD=");  //name=value:value
	printf("ret = %d\n", ret);

	val = getenv(name);
	printf("3, %s = %s\n", name, val);
#endif

	return 0;
}
/*执行结果
ubuntu@ubuntu:~/LearnCPP/linux_test/path_test$ ./setenv 
1, ABD = 123
2, ABD = haha-day-and-night
ret = -1
3, ABD = haha-day-and-night
正确删除环境变量
ubuntu@ubuntu:~/LearnCPP/linux_test/path_test$ ./setenv 
1, ABD = 123
2, ABD = haha-day-and-night
3, ABD = (null)
*/

进程控制

fork()getpid()getppid()

pid_t fork(void);
失败返回-1;成功返回① 父进程返回子进程的ID(非负) ②子进程返回 0 。
创建一个子线程。pid_t类型表示进程ID,但为了表示-1,它是有符号整型。
(0不是有效进程ID,init最小,为1)。不是fork函数能返回两个值,而是fork后,fork函数变为两
个进程,每个进程各自返回一个。

pid_t getpid(void);
获取当前进程id

pid_t getppid(void);
获取当前进程的父进程id

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

int main(int argc,char* argv[]){

	printf("before fork1\n");
	printf("before fork2\n");
	printf("before fork3\n");
	printf("before fork4\n");

	pid_t pid = fork();

	if(pid == -1){
		perror("fork err");
		exit(1);
	}else if(pid == 0){
		printf("children pid is create  -- pid = %d \n,parent pid = %d",getpid(),getppid());
	}else if(pid >0){
		printf("parent pid = %d create children pid is %d\n,parent parent pid = %d \n",getpid(),pid,getppid());
	}
	
	printf("-----------end of file\n");
	return 0;
}
/*
ubuntu@ubuntu:~/LearnCPP/linux_test/pcb_test$ ./pcb_test 
before fork1
before fork2
before fork3
before fork4
parent pid = 7083 create children pid is 7084
,parent parent pid = 7077 
-----------end of file
children pid is create  -- pid = 7084 
,parent pid = 7083-----------end of file
*/

循环创建多个子进程

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

int main(int argc,char* argv[]){
	
	int i =0;
	pid_t pid;
	for(i =0;i<5;i++){
		pid = fork();
		if(pid == 0){
			break;
		}
	}
	if(i == 5){
		sleep(5);
		printf("parent process\n");
	}else{
		sleep(i);//让进程按照顺序打印
	    printf("I am %dth chilren\n",i+1);
	}
	printf("-----------end of file\n");
	return 0;
}
/*
ubuntu@ubuntu:~/LearnCPP/linux_test/pcb_test$ ./fork_loop 
I am 1th chilren
-----------end of file
I am 2th chilren
-----------end of file
I am 3th chilren
-----------end of file
I am 4th chilren
-----------end of file
I am 5th chilren
-----------end of file
parent process
-----------end of file
*/

getuid()getgid()geteuid()getegid()

获取当前进程实际用户ID
uid_t getuid(void);
获取当前进程有效用户ID
uid_t geteuid(void);

获取当前进程使用用户组ID
gid_t getgid(void);
获取当前进程有效用户组ID
gid_t getegid(void);

进程共享

  • 父子进程相同:

    fork 后。 data 段、text 段、堆、栈、环境变量、全局变量、宿主目录位置、进程工作目录位

置、信号处理方式

  • 父子进程不同:

    进程 id、返回值、各自的父进程、进程创建时间、闹钟、未决信号集

  • 父子进程共享:

    读时共享、写时复制。———————— 全局变量。

共享内容:1、文件描述符 2. mmap 映射区。

父子进程GDB调试

gdb 调试:

  • 设置父进程调试路径:set follow-fork-mode parent (默认)

  • 设置子进程调试路径:set follow-fork-mode child

注意,一定要在fork函数调用之前设置才有效。

exec函数族

当进程调用一种exec函数时,该进程的用户空间代码和数据完全被新程序替换,从新程序的启动例程开始执行。将当前进程的.text.data替换为所要加载的程序的.text.data,然后让进程从新的.text第一条指令开始执行,但进程ID不变。

exec函数族函数

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

exec函数一般规律

exec 函数族一般规律:
exec 函数一旦调用成功,即执行新的程序,不返回。只有失败才返回,错误值-1,所以通常我们
直接在 exec 函数调用后直接调用 perror(),和 exit(),无需 if 判断。

  • l(list) :命令行参数列表
  • p(path) :搜索 file 时使用 path 变量
  • v(vector) :使用命令行参数数组
  • e(environment) :使用环境变量数组,不适用进程原有的环境变量,设置新加载程序运行的

环境变量事实上,只有 execve 是真正的系统调用,其他 5 个函数最终都调用 execve,是库函数,所以 execve man 手册第二节,其它函数在 man 手册第 3 节。

execl执行test程序

  • test程序
#include <stdio.h>

int main(){
	printf("hello my name is child process\n");

	return 0;
}
  • execl程序
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <pthread.h>

int main(int argc,char* argv[]){

	pid_t pid = fork();

	if(pid == -1){
		perror("fork error");
		exit(1);
	}else if(pid == 0){
		execl("./test","./test",NULL);//NULL哨兵	
		perror("exec error");
		exit(1);
	}else if(pid >0){
		sleep(1);
		printf("i am parent : %d \n",getpid());		
	}

	return 0;
}

/*
ubuntu@ubuntu:~/LearnCPP/linux_test/exec_test$ ./exec_test2 
hello my name is child process
i am parent : 9095 
*/

execl执行ls

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

int main(int argc,char* argv[]){

	pid_t pid = fork();

	if(pid == -1){
		perror("fork error");
		exit(1);
	}else if(pid == 0){
		execl("/bin/ls","ls","-l",NULL);//NULL哨兵	
		perror("exec error");
		exit(1);
	}else if(pid >0){
		sleep(1);
		printf("i am parent : %d \n",getpid());		
	}

	return 0;
}
/*
ubuntu@ubuntu:~/LearnCPP/linux_test/exec_test$ ./exec_test4 
总用量 132
-rwxrwxr-x 1 ubuntu ubuntu  8912 9月   7 16:25 exec_ps
-rw-rw-r-- 1 ubuntu ubuntu   403 9月   7 16:25 exec_ps.c
-rwxrwxr-x 1 ubuntu ubuntu  8920 9月   7 15:18 exec_test
-rwxrwxr-x 1 ubuntu ubuntu  8920 9月   7 15:24 exec_test2
-rw-rw-r-- 1 ubuntu ubuntu   402 9月   7 15:54 exec_test2.c
-rwxrwxr-x 1 ubuntu ubuntu  8920 9月   7 15:28 exec_test3
-rw-rw-r-- 1 ubuntu ubuntu   399 9月   7 15:28 exec_test3.c
-rwxrwxr-x 1 ubuntu ubuntu  8920 9月   7 15:56 exec_test4
-rw-rw-r-- 1 ubuntu ubuntu   404 9月   7 15:56 exec_test4.c
-rw-rw-r-- 1 ubuntu ubuntu   400 9月   7 15:18 exec_test.c
-rw-rw-r-- 1 ubuntu ubuntu 34074 9月   7 16:25 ps.out
-rwxrwxr-x 1 ubuntu ubuntu  8600 9月   7 15:24 test
-rw-rw-r-- 1 ubuntu ubuntu    91 9月   7 15:22 test.c
i am parent : 9208 
*/

execlp函数执行ls

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

int main(int argc,char* argv[]){

	pid_t pid = fork();

	if(pid == -1){
		perror("fork error");
		exit(1);
	}else if(pid == 0){
		execlp("ls","ls","-l",NULL);//NULL哨兵	
		perror("exec error");
		exit(1);
	}else if(pid >0){
		sleep(1);
		printf("i am parent : %d \n",getpid());		
	}

	return 0;
}
/*
ubuntu@ubuntu:~/LearnCPP/linux_test/exec_test$ ./exec_test
总用量 132
-rwxrwxr-x 1 ubuntu ubuntu  8912 9月   7 16:25 exec_ps
-rw-rw-r-- 1 ubuntu ubuntu   403 9月   7 16:25 exec_ps.c
-rwxrwxr-x 1 ubuntu ubuntu  8920 9月   7 15:18 exec_test
-rwxrwxr-x 1 ubuntu ubuntu  8920 9月   7 15:24 exec_test2
-rw-rw-r-- 1 ubuntu ubuntu   402 9月   7 15:54 exec_test2.c
-rwxrwxr-x 1 ubuntu ubuntu  8920 9月   7 15:28 exec_test3
-rw-rw-r-- 1 ubuntu ubuntu   399 9月   7 15:28 exec_test3.c
-rwxrwxr-x 1 ubuntu ubuntu  8920 9月   7 15:56 exec_test4
-rw-rw-r-- 1 ubuntu ubuntu   404 9月   7 15:56 exec_test4.c
-rw-rw-r-- 1 ubuntu ubuntu   400 9月   7 15:18 exec_test.c
-rw-rw-r-- 1 ubuntu ubuntu 34074 9月   7 16:25 ps.out
-rwxrwxr-x 1 ubuntu ubuntu  8600 9月   7 15:24 test
-rw-rw-r-- 1 ubuntu ubuntu    91 9月   7 15:22 test.c
i am parent : 9235 
*/

execlp执行ps aux命令,并存入文件

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

int main(int argc,char* argv[]){

	int fd = open("ps.out",O_RDWR|O_CREAT|O_TRUNC,0664);
	if(fd == -1){
		perror("open error");
		exit(1);
	}

	dup2(fd,STDOUT_FILENO);

	pid_t pid = fork();

	if(pid == 0){
		execlp("ps","ps","-aux",NULL);
		close(fd);
		perror("exec error");
		exit(1);
	}

	return 0;
}

执行后的结果

在这里插入图片描述

孤儿进程和僵尸进程

孤儿进程:

​ 父进程先于子进终止,子进程沦为“孤儿进程”,会被 init 进程领养。

僵尸进程:

​ 子进程终止,父进程尚未对子进程进行回收,在此期间,子进程为“僵尸进程”。 kill 对其

无效。这里要注意,每个进程结束后都必然会经历僵尸态,时间长短的差别而已。

​ 子进程终止时,子进程残留资源 PCB 存放于内核中,PCB 记录了进程结束原因,进程回收就是回

收 PCB。回收僵尸进程,得 kill 它的父进程,让孤儿院去回收它。

回收子进程

wait()函数

回收子进程退出资源, 阻塞回收任意一个。
pid_t wait(int *status)

参数:(传出) 回收进程的状态。
返回值:成功: 回收进程的 pid
失败: -1, errno

wait阻塞回收单个子进程

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

int main(int argc,char* argv[]){

	pid_t pid , wpid;
	int status;

	pid = fork();
	if(pid == 0){
		printf("child my parent pid = %d,going to sleep 5s\n",getppid());
		sleep(5);
		printf("------child die------\n");
	}else if(pid >0){
			wpid = wait(&status);
			if(wpid == -1){
				perror("wait error");
				exit(1);
			}
			printf("I am parent ,pid = %d,myson = %d\n",getpid(),pid);
			printf("parent finish\n");
	}else{
		perror("fork");
		return 1;
	}
	
	return 0;
}
/*
ubuntu@ubuntu:~/LearnCPP/linux_test/wait_test$ ./wait_test
child my parent pid = 9511,going to sleep 5s
------child die------
I am parent ,pid = 9511,myson = 9512
parent finish
*/

wait获取子进程退出值和异常终止信号

  • 获取子进程正常终止值:

    WIFEXITED(status) --》 为真 --》调用WEXITSTATUS(status)–》 得到 子进程 退出值。

  • 获取导致子进程异常终止信号:

    WIFSIGNALED(status) --》 为真 --》调用 WTERMSIG(status) --》 得到 导致子进程异常

终止的信号编号。

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

int main(int argc,char* argv[]){

    pid_t pid, wpid;
    int status;

    pid = fork();

    //一次wait和waitpid一次只能回收一个子进程
    //wait来说是随机的,那个先,就先回收那个子进程

    if(pid == 0 ){//子进程
        printf("this is child process: child id = %d,sleep 10s\n",getpid());
        sleep(10);
        printf("-------child die--------\n");
        return 87;
    }else if(pid > 0){//父进程
        wpid = wait(&status);

        if(wpid == -1){
            perror("wait error");
            exit(1);
        }else{
            if(WIFEXITED(status)){//正常结束
                printf("child process exit : %d\n",WEXITSTATUS(status));
            }else if(WIFSIGNALED(status)){//被信号kill
                printf("child process kill by singal:%d",WTERMSIG(status));
            }else if(WIFSTOPPED(status)){//被信号stop
                printf("child process stop by singal:%d",WSTOPSIG(status));
            }
        }
    }else{
        perror("fork error");
        return -1;
    }

    return 0;
}
/*
//正常终止
ubuntu@ubuntu:~/LearnCPP/linux_test/wait_test$ ./wait_test3
this is child process: child id = 9589,sleep 10s
-------child die--------
child process exit : 87
*/

waitpid()函数

pid_t waitpid(pid_t pid, int *status, int options)
waitpid(-1, &status, 0)相当于wait(&status);
参数:

  • pid:指定回收某一个子进程 pid

    ​ > 0: 待回收的子进程 pid
    ​ -1:任意子进程
    ​ 0:同组的子进程。

  • status:(传出) 回收进程的状态。

  • optionsWNOHANG 指定回收方式为,非阻塞。如果是0表示阻塞

返回值:

​ > 0 : 表成功回收的子进程 pid
​ 0 : 函数调用时, 参 3 指定了 WNOHANG, 并且,没有子进程结束。
​ -1: 失败。errno

回收多个子进程

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/wait.h>

int main(int argc, char *argv[]){

    int n = 5, i;
    pid_t pid, wpid;

    for (i = 0; i < 5; i++){
        pid = fork();
        if (pid == 0)
        {
            break;
        }
    }
    if (n == i){ //父进程
        // sleep(n);
        // printf("I am parent , pid = %d",getpid());
        // while ((wpid = waitpid(-1, NULL, 0))) { //阻塞的回收
        //     printf("wait child %d\n", wpid);
        //     if (wpid == -1){
        //         break;
        //     }
        // }
        while((wpid = waitpid(-1,NULL,WNOHANG) != -1)){//非阻塞的回收
            sleep(1);
            if(wpid > 0){
                printf("wait child %d\n",wpid);
            }else if (wpid == 0){
                sleep(1);
                printf("wait child finish");
                continue;
            }
        }
    }
    else{
        sleep(i);
        printf("I am %dth child ,pid = %d\n", i + 1, getpid());
    }

    return 0;
}
/*
ubuntu@ubuntu:~/LearnCPP/linux_test/waitpid_test$ ./waitpid_test
I am 1th child ,pid = 9749
wait child 1
I am 2th child ,pid = 9750
I am 3th child ,pid = 9751
wait child 1
I am 4th child ,pid = 9752
wait child 1
I am 5th child ,pid = 9753
wait child 1
wait child 1
wait child 1
*/

回收单个进程

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

int main(int argc,char* argv[]){

    pid_t pid, wpid;
    int i = 0;

    for (i = 0; i < 5;i++){//循环产生5个子进程
        pid = fork();//父进程执行
        if(pid == 0){//子进程不在衍生,子进程执行
            break;
        }
        if(i == 2){//第三个子进程
            wpid = pid;//父进程执行
        }
    }

    if( 5 == i){//父进程
        sleep(1);
        pid_t p = waitpid(wpid, NULL, 0);//阻塞回收wpid进程
        if(p > 0){
            printf("wait pid = %d finish\n",wpid);
        }
    }
    else { //子进程
        //
        sleep(i);
        printf("I am %dth child , my pid = %d\n",i+1,getpid());
    }

    return 0;
}

进程间通信

常见的进程间通信

进程间通信的常用方式:

  • 管道:简单

  • 信号:开销小

  • mmap 映射:非血缘关系进程间

  • socket(本地套接字):稳定

管道pipe

调用pipe系统函数即可创建一个管道。有如下特质:

  • 其本质是一个伪文件(实为内核缓冲区)
  • 由两个文件描述符引用,一个表示读端,一个表示写端。
  • 规定数据从管道的写端流入管道,从读端流出。

管道的原理: 管道实为内核使用环形队列机制,借助内核缓冲区(4k)实现。
管道的局限性:

​ ① 数据不能进程自己写,自己读。
​ ② 管道中数据不可反复读取。一旦读走,管道中不再存在。
​ ③ 采用半双工通信方式,数据只能在单方向上流动。
​ ④ 只能在有公共祖先的进程间使用管道。

int pipe(int pipefd[2]); 成功:0;失败:-1,设置errno
函数调用成功返回r/w两个文件描述符。无需open,但需手动close。规定:fd[0] → rfd[1] → w

用于有血缘关系进程之间的通信

pipe通信demo

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/wait.h>
#include <string.h>

//管道命令pipe test
int main(int argc,char* argv[]){

    int fd[2];//管道
    int ret;
    pid_t pid;
    char *buf = "hello world\n";
    char readBuf[1024];//接收管道数据buf

    ret = pipe(fd);//创建管道并打开

    if(ret == -1){
        perror("pipe error");
        exit(1);
    }

    pid = fork();
    if (pid > 0){//父进程
        close(fd[0]);//关闭读段
        write(fd[1],buf,strlen(buf));//向写管道中写入
        sleep(1);//保证子进程先于父进程结束
        close(fd[1]);  //关闭写管道
    }else if(pid == 0){//子进程
        close(fd[1]);//关闭写管道
        ret = read(fd[0],readBuf,sizeof(readBuf));
        printf("child read %d char\n",ret);
        write(STDOUT_FILENO, readBuf, ret);
        close(fd[0]);
    }

    return 0;
}
/*
ubuntu@ubuntu:~/LearnCPP/linux_test/pipe_test$ ./pipe_test 
child read 12 char
hello world
*/

pipe实现ls | wc -l

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>
#include <string.h>
#include <fcntl.h>

//利用pipe管道通信实现ls | wc -l
//使用dup2 fork pipe等函数

int main(int argc,char* argv[]){

    int fd[2];
    int ret;
    pid_t pid;

    ret = pipe(fd);
    if(ret == -1){
        perror("pipe error");
        exit(1);
    }

    pid = fork();
    if(pid == -1){
        perror("fork error");
        exit(1);
    }else if(pid == 0){//子程序进行写端,将STDOUT重定向到fd[1]中
        close(fd[0]);//关闭读端
        dup2(fd[1],STDOUT_FILENO);//重定向
        execlp("ls","ls",NULL);//执行ls命令
        close(fd[1]);
        perror("execlp error");
        exit(1);
    }
    else if (pid > 0)
    { //父程序进行读端,将STDIN重定向到fd[1]中
        close(fd[1]);
        dup2(fd[0],STDIN_FILENO);//重定向
        execlp("wc","wc","-l",NULL);
        close(fd[0]);
        perror("execlp error");
        exit(1);
    }

    return 0;
}
/*
ubuntu@ubuntu:~/LearnCPP/linux_test/pipe_test$ ./pipe1 
10
*/

fifo命名管道

int mkfifo(const char *pathname, mode_t mode); 成功:0; 失败:-1

** 可以应用于无血缘关系进程之间的通信

创建fifo命名管道

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

void sys_errno(char* str){	
	perror("str");
	exit(1);
}

int main(int argc,char** argv){
	
	int ret = mkfifo("myfifo",0664);
	if(ret == -1){	
		sys_errno("fifo err");	
	}
	return 0;
}

程序1写fifo

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

int main(int argc,char* argv[]){	
	int fd,i;
	char buf[4096];

	fd = open(argv[1],O_WRONLY);
	if(fd<0){
		perror("open err");
		exit(1);
	}
	i = 0;

	while(1){	
		sprintf(buf, "hello itcast %d\n",i++);
		write(fd,buf,strlen(buf));
		sleep(1);	
	}	
	close(fd);
	return 0;
}

程序2读取fifo

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

void sys_errno(char* str){
	
	perror("str");
	exit(1);
}

int main(int argc,char* argv[]){
	
	int fd,i,len;
	char buf[4096];

	fd = open(argv[1],O_RDONLY);
	if(fd<0){
		perror("open err");
		exit(1);
	}
	i = 0;
	
//	write(fd,"nihao\n",strlen("nihao\n"));
	while(1){
		len = read(fd,buf,strlen(buf));		
		write(STDOUT_FILENO,buf,len);
		sleep(1);	
	}	
	close(fd);
	return 0;
}

fifo执行结果

在这里插入图片描述

s

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值