赞
踩
使用fork()函数创建的子进程,其中包含的程序代码完全相同,只能根据fork()函数的返回值,执行不同的代码分支。
由exec函数族中的函数,则可以根据指定的文件名或路径,找到可执行文件。
exec函数族中包含6个函数,分别为:
在这里插入代码#include <unistd.h>
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[]);
(1)当参数是path,传入的为路径名;当参数是file,传入的可执行文件名;
(2)可以将exec函数族分为execl和execv两类:
在 Linux 系统中,环境变量是用来定义系统运行环境的一些参数,比如每个用户不同的家目录(HOME)、邮件存放位置(MAIL)等
[root@localhost ~]# env
ORBIT_SOCKETDIR=/tmp/orbit-root
HOSTNAME=livecd.centos
GIO_LAUNCHED_DESKTOP_FILE_PID=2065
TERM=xterm
SHELL=/bin/bash
提醒一下,有些参数是path有些参数又是file两个有一点细微的区别。当参数是path时,传入的为命令的路径名;当参数是file,传入的可执行文件名。
至于函数几个函数到底该怎么使用,看看后面的案例就会明白怎么使用、有什么作用了。
#include <stdio.h> #include <stdlib.h> #include <unistd.h> int main(){ pid_t tempPid; tempPid=fork(); if(tempPid == -1){ perror("fork error"); exit(1); } else if(tempPid > 0) { printf("parent process:pid=%d\n", getpid()); } else { printf("child process:pid=%d\n", getpid()); //execl("/bin/ls","-a","-l","./",NULL); //① //execlp("ls","-a","-l","./",NULL); //② char *arg[]={"-a","-l","./", NULL}; //③ execvp("ls", arg); perror("error exec\n"); printf("child process:pid=%d\n", getpid()); } //of if return 0; } //of main
以上即为大概格式,可以用ls命令查看文件夹中的文件、以路径查看文件等等。除了ls还可以用其它的命令,只要时bash能执行的在exec家族中也能。
#include <stdlib.h>
void exit(int status);
(1)status:表示进程的退出状态,0表示正常退出,非0表示异常退出,一般用-1或1表示;
(2)为了可读性,标准C定义了两个宏:EXIT_SUCCESS和EXIT_FAILURE
_exit()函数:
Linux系统中有一个与exit()函数非常相似的函数:_exit()
#include <unistd.h>
void _exit(int status);
区别:
一般的,当遇到一个函数是以下划线开头,那么这个函数会比没有下划线的函数更贴近于底层。例如:exit() 与 _exit() 相比功能一样,但增加了退出时的安全性条件的判断。
特殊进程的危害:
在多道程序环境下,进程是并发执行的,不同进程之间存在着不同的相互制约关系。不同进程存在着相互制约的关系。
#include <sys/wait.h>
pid_t wait(int *status);
【案例 1】若子进p1是其父进程p的先决进程,基于wait函数使得进程同步。
#include <stdio.h> #include <stdlib.h> #include <sys/wait.h> #include <unistd.h> int main(){ pid_t tempPid, tempW; tempPid = fork(); if(tempPid == -1){ perror("fork error"); exit(1); }else if(tempPid == 0){//child sleep(3); printf("Child process, pid = %d, ppid = %d\n", getpid(), getppid()); }else{//parent tempW = wait(NULL); printf("Catched a child process, tempW = %d, ppid = %d\n", tempW, getppid()); }//of if printf(">>>>>>>>finish<<<<<<<<\n"); return 0; }//of main
把父进程挂起就可以避免子进程变成孤儿进程,因为必须得子进程结束父进程的挂起才会结束。
若wait函数的参数不为空,可以获取子进程的退出状态,退出状态存放在参数status的低八位中。Linux定义了一组判断进程退出状态的宏函数,其中最基础的两个如下:
#include <sys/wait.h>
int WIFEXITED(int status);//判断子进程是否正常退出,若是,返回非0值,否则返回0
int WEXITSTATUS(int status);//和WIFEXITED配合使用,WIFEXITED返回非0值,则使用该宏提取子进程的返回值。
【案例 2】使用wait同步进程,并使用宏获取子进程的返回值。
test_wait2.c #include <stdio.h> #include <sys/wait.h> #include <stdlib.h> int main(){ int tempStatus; pid_t tempPid, tempW; tempPid = fork(); if(tempPid == -1){ perror("fork error"); exit(1); } else if(tempPid == 0){//子 sleep(3); printf("Child process: pid=%d\n",getpid()); exit(5); } else{//父 tempW = wait(&tempStatus); if(WIFEXITED(tempStatus)){ printf("Child process pid=%d exit normally.\n", tempW ); printf("Return Code:%d\n",WEXITSTATUS(tempStatus)); } else { printf("Child process pid=%d exit abnormally.\n", tempW); }//of if }//of if return 0; }//of main
wait虽然可用,但是也有很明显的缺点:如果存在多个子进程,那么只要其中一个子进程结束,那么父进程就解开了挂起状态,则后面的子进程都会成为孤儿进程。为了解决这个问题我们引用升级版wait函数waitpid()。
#include <sys/wait.h>
pid_t waitpid(pid_t pid, int *status, int options);
【案例 3】父进程等待进程组中指定子进程,该进程不退出,则父进程一直阻塞。
#include <stdio.h> #include <stdlib.h> #include <sys/wait.h> int main(){ pid_t tempPid, tempP, tempW; tempPid= fork(); //创建第一个子进程 if (tempPid == -1){ perror("fork1 error"); exit(1); } else if (tempPid == 0){ //子进程沉睡 sleep(5); printf("First child process:pid=%d\n", getpid()); } else { //父进程继续创建进程 int i; tempP = tempPid; for (i = 0; i < 3; i++){ //由父进程创建3个子进程 if ((tempPid = fork()) == 0){ break; }//of if }//of for i if (tempPid == -1){ //出错 perror("fork error"); exit(2); } else if (tempPid == 0){ //子进程 printf("Child process:pid=%d\n", getpid()); exit(0); } else { //父进程 tempW = waitpid(tempP, NULL, 0); //等待第一个子进程执行 if (tempW == tempP){ printf("Catch a child Process: pid=%d\n", tempW); }else{ printf("waitpid error\n"); }//of if }//of if }//of if return 0; }//of main
【案例 4】基于waitpid函数不断获取子进程的状态。
#include <stdio.h> #include <stdlib.h> #include <sys/wait.h> #include <unistd.h> int main() { pid_t tempPid, tempW; tempPid = fork(); if (tempPid == -1){ perror("fork error"); exit(1); } else if (tempPid == 0){ sleep(3); printf("Child process:pid=%d\n", getpid()); exit(0); } else { do{ tempW = waitpid(tempPid, NULL, WNOHANG); if (tempW == 0){ printf("No child exited\n"); sleep(1); }//of if } while (tempW == 0); if (tempW == tempPid){ printf("Catch a Child process:tempW=%d\n", tempW); }else{ printf("waitpid error\n"); }//of if }//of if return 0; }//of main
通信故名思意就是进程与进程之间的信息交互
我们通过一个缓冲区把信息从一个进程传到另一个进程,这个缓冲区我们把它称为管道。管道其实质是由内核管理的一个缓冲区,形象地认为管道的两端连接着两个进程:一个进程进行信息输出,将数据写入管道;另一个进程进行信息输入,从管道中读取信息。
写和读不能同时进行,也就是管道只能读或者写,如果两个进程同时对管道进行操作,则两个进程中必须有一个停止下来让另一个进行操作。
管道分为:
匿名管道的使用流程如下:
①在进程中创建匿名管道,pipe函数;
②关闭进程中不使用的管道端口,close函数;
③在待通信的进程中分别对管道的读、写端口进行操作,read/write函数;
④关闭管道,close函数。
#include <unistd.h>
int pipe(int pipefd[2]);
功能:
参数说明:
返回值说明:
【案例1】使用pipe()实现父子进程间通信,父进程作为读端,子进程作为写端。
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <sys/types.h> #include <sys/wait.h> int main(){ int tempFd[2];//定义文件描述符数组 int tempRet=pipe(tempFd);//创建管道 if(tempRet == -1){ perror("pipe"); exit(1); } pid_t tempPid=fork(); if(tempPid > 0){//父进程—读 close(tempFd[1]);//关闭写端 char tempBuf[64]={0}; tempRet = read(tempFd[0], tempBuf, sizeof(tempBuf));//读数据 close(tempFd[0]); write(STDOUT_FILENO, tempBuf, tempRet);//将读到的数据写到标准输出 wait(NULL); } else if(tempPid == 0){//子进程—写 close(tempFd[0]);//关闭读端 char *tempStr="hello,pipe\n"; write(tempFd[1], tempStr, strlen(tempStr)+1);//写数据 close(tempFd[1]); }//of if return 0; }//of main
分析如下:
pipe()创建管道后读端的文件描述符为fd[0],写端的文件描述符为fd[1];
调用fork后父子进程共享文件描述符,文件描述符与管道的关系如图所示:
父进程进行读操作,子进程进行写操作;使用close()函数关闭父进程的写端与子进程的读端。
#include <unistd.h>
int dup2(int oldfd, int newfd);
功能:
返回值说明:
【案例2】使用管道实现兄弟进程间通信,兄弟进程实现命令“ls | wc –l”的功能。
(在实现本案例时会用到重定向函数dup2:)
#include <stdio.h> #include <stdlib.h> #include <unistd.h> int main(){ int tempFd[2]; int tempRet = pipe(tempFd); if(tempRet == -1){ perror("pipe err"); exit(1); }//of if int i; pid_t tempPid, tempWpid; for(i=0; i<2; i++){//2个子 if((tempPid = fork()) == 0){ break; }//of if }//of if if(2 == i){//父进程,回收子进程 close(tempFd[0]); //关闭读 close(tempFd[1]); //关闭写 tempWpid = wait(NULL); printf("wait child 1 success,pid=%d\n", tempWpid ); tempPid = wait(NULL); printf("wait child 2 success,pid=%d\n", tempPid); } else if(0 == i){//子进程1—写 close(tempFd[0]); dup2(tempFd[1], STDOUT_FILENO);//定向到标准输出 execlp("ls", "ls", NULL); } else if(1 == i){//子进程2—读 close(tempFd[1]); dup2(tempFd[0], STDIN_FILENO); execlp("wc", "wc", "-l", NULL); }//of if return 0; }//of main
兄弟进程间通信,进程文件描述符与管道的关系如图所示:
实线所示的箭头为编程中需要保留的文件描述符
Linux标准I/O库两个函数popen()和pclose(),可完成管道通信的流程。
#include <stdio.h>
FILE *popen(const char *command, const char *type);
int pclose(FILE *stream);
功能:
popen函数的功能是:
pclose函数的功能:
参数说明:
popen函数的参数:
pclose函数的返回值:
【案例3】使用popen与pclose函数实现管道通信。
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main(){
FILE *tempRfp,*tempWfp;
char temBuf[100];
tempRfp = popen("ls","r"); //读取命令执行结果
tempWfp = popen("wc -l","w"); //将管道中的数据传递给进程
while(fgets(temBuf, sizeof(temBuf), tempRfp)!=NULL){
fputs(temBuf, tempWfp);
}//of while
pclose(tempRfp);
pclose(tempWfp);
return 0;
}//of main
#include <sys/type.h>
#include <sys/stat.h>
int mkfifo(const char *pathname, mode_t mode);
功能:
参数说明:
返回值说明:
【案例4】使用FIFO实现没有亲缘关系进程间的通信。没有亲缘关系的进程间通信,需要两段程序来实现:
fifo_write.c #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> int main(int paraArgc,char *paraArgv[]){ if(paraArgc < 2){ //判断是否传入文件名 printf("./a.out fifoname\n"); exit(1); }//of if int tempRet = access(paraArgv[1], F_OK); //判断fifo文件是否存在 if(tempRet == -1){ //若fifo不存在就创建fifo int tempFIFO = mkfifo(argv[1], 0664); if(tempFIFO == -1){ //判断文件是否创建成功 perror("mkfifo"); exit(1); } else{ printf("fifo creat success!\n"); }//of if }//of if int tempFd = open(argv[1], O_WRONLY); //读写方式打开 while(1){ //循环写入数据 char *tempStrp="hello,world!"; write(tempFd, tempStrp, strlen(tempStrp)+1); sleep(1); }//of while close(tempFd); return 0; }//of main fifo_read.c #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> int main(int paraArgc,char *paraArgv[]){ if(paraArgc < 2){ //判断是否传入文件名 printf("./a.out fifoname\n"); exit(1); }//of if int tempRet = access(argv[1], F_OK); //判断fifo文件是否存在 if(tempRet == -1){ //若fifo不存在就创建fifo int tempFIFO = mkfifo(argv[1], 0664); if(tempFIFO == -1){ //判断文件是否创建成功 perror("mkfifo"); exit(1); } else{ printf("fifo creat success!\n"); }//of if }//of if int tempFd = open(argv[1], O_RDONLY); //只读方式打开 if(tempFd == -1){ perror("open"); exit(1); }//of if while(1){ //不断读取fifo中的数据并打印 char temBuf[1024]={0}; read(tempFd, temBuf, sizeof(temBuf)); printf("buf=%s\n", temBuf); }//of while close(tempFd); //关闭文件 return 0; }//of main
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。