进程可以简单的看做是正在运行的程序
UNIX允许多个用户同时访问系统
每个用户可以同时运行多个程序
每个程序可以同时运行多个实例
进程之间共享程序代码和系统函数库,所以任何时候内存中只有一份代码的拷贝
启动进程并等待它们结束的能力是整个系统的基础
启动新的进程
用库函数system完成
1 #include <stdlin.h> 2 int system(const char * string);
system函数的作用是运行以字符串参数的形式传递的命令并等待该命令的完成
1 #include <stdlib.h> 2 #include <stdio.h> 3 4 int main() 5 { 6 printf("Running ps with system "); 7 system("ps -ax"); 8 printf("Done! "); 9 exit(0); 10 }
结果是执行这个打印
要是把上面的
system("ps -ax");
换成
system("ps -ax &");
让打印进入后台,这样先退出程序,打印可能还会继续输出。
为此我们需要更加精确的控制函数
启动新的进程
由exec函数完成,这个系列的函数把当前的进程换成一个新的进程,新的进程由path 或者file参数指定
1 #include <unistd.h> 2 char * * environ 3 int execl(const char * path, const char * arg0,... (char *)0); 4 int execlp(const char * file, const char * arg0,... (char *)0); 5 int execle(const char * path,const char * arg0,... (char *)0, char * const envp[]); 6 //上面的三个参数是可变的,以一个空指针结束 7 int execv(const char * file, char * argv[]); 8 int execvp(const char * file,char * argv[]); 9 int execve(const char * path, char * const argv[], char * const envp[]);
下面使用exec函数启动ps程序可用的参考片段:
1 #include <unistd.h> 2 3 char * const ps_argv[]={"ps", "-ax", 0}; 4 char * const ps_envp[]={"PATH=/bin:/usr/bin","TERM=console",0}; 5 6 execl("/bin/ps","ps","-ax",0); 7 execlp("ps","ps","-ax",0); 8 execle("/bin/ps","ps","-ax",0,ps_envp); 9 10 execv("/bin/ps",ps_argv); 11 execvp("ps",ps_argv); 12 execve("/bin/ps",ps_argv,ps_envp);
下面是一个实际的例程
1 #include <unistd.h> 2 #include <stdio.h> 3 4 int main() 5 { 6 printf("Running ps with execlp "); 7 execlp("ps", "ps", "-ax", 0); 8 printf("Done. "); 9 exit(0); 10 }
//程序要是正常执行的话done是不会输出的,因为ps结束之后,没有返回execlp程序中,而是出现新的shell
复制进程映像
1 #include <sys/types.h> 2 #include <unistd.h> 3 pid_t fork(void);
父进程的fork返回新的子进程的PID
子进程中的fork返回的是0
父进程可以通过这个性质判断谁是父进程谁是子进程
下面是具体的实现:
1 #include <sys/types.h> 2 #include <unistd.h> 3 #include <stdio.h> 4 5 int main() 6 { 7 pid_t pid; 8 char *message; 9 int n; 10 11 printf("fork program starting "); 12 pid = fork(); 13 switch(pid) 14 { 15 case -1: 16 perror("fork failed"); 17 exit(1); 18 case 0: 19 message = "This is the child"; 20 n = 5; 21 break; 22 default: 23 message = "This is the parent"; 24 n = 3; 25 break; 26 } 27 28 for(; n > 0; n--) { 29 puts(message); 30 sleep(1); 31 } 32 exit(0); 33 }//创建子进程执行五次打印,父进程执行三次打印
输出的结果是这样的:
1 jason@t61:~/c_program/544977-blp3e/chapter11$ ./fork1 2 fork program starting 3 This is the parent 4 This is the child 5 This is the parent 6 This is the child 7 This is the parent 8 This is the child 9 This is the child 10 jason@t61:~/c_program/544977-blp3e/chapter11$ This is the child 11 //这里出现了一个问题就是父进程退出之后子进程还在打印
等待一个进程
1 #include <sys/types.h> 2 #include <sys/wait.h> 3 pid_t wait(int * stat_loc)
这个系统调用返回子进程的pid通常是已经结束运行的子进程的pid
要是stat_loc不是空指针,状态信息将被写入它指向的位置
测试程序:
1 #include <sys/types.h> 2 #include <sys/wait.h> 3 #include <unistd.h> 4 #include <stdio.h> 5 6 int main() 7 { 8 pid_t pid; 9 char *message; 10 int n; 11 int exit_code; 12 13 printf("fork program starting "); 14 pid = fork();//父进程获得一个非零的返回值 15 switch(pid) 16 { 17 case -1: 18 exit(1); 19 case 0: 20 message = "This is the child"; 21 n = 5; 22 exit_code = 37; 23 break; 24 default: 25 message = "This is the parent"; 26 n = 3; 27 exit_code = 0; 28 break; 29 } 30 31 for(; n > 0; n--) { 32 puts(message); 33 sleep(1); 34 } 35 36 /* This section of the program waits for the child process to finish. */ 37 38 if(pid) 39 { 40 int stat_val; 41 pid_t child_pid; 42 43 child_pid = wait(&stat_val);//父进程用wait将自己的执行挂起。返回子进程的pid 44 45 printf("Child has finished: PID = %d ", child_pid);//wait结束执行打印 46 if(WIFEXITED(stat_val))//这个宏表示子程序正常结束的话,就返回一个非零值 47 printf("Child exited with code %d ", WEXITSTATUS(stat_val));/*如果WIFEXITED非零, WEXITSTATUS(stat_val)返回子进程的退出码*/ 48 else 49 printf("Child terminated abnormally "); 50 } 51 exit (exit_code); 52 }
执行效果:
1 jason@t61:~/c_program/544977-blp3e/chapter11$ ./wait 2 fork program starting 3 This is the parent 4 This is the child 5 This is the parent 6 This is the child 7 This is the parent 8 This is the child 9 This is the child 10 This is the child 11 Child has finished: PID = 3264 12 Child exited with code 37 13 jason@t61:~/c_program/544977-blp3e/chapter11$ 14 //这样可以保证子进程在父进程之前先结束
僵尸进程
1 jason@t61:~/c_program/544977-blp3e/chapter11$ cat fork2.c 2 #include <sys/types.h> 3 #include <unistd.h> 4 #include <stdio.h> 5 6 int main() 7 { 8 pid_t pid; 9 char *message; 10 int n; 11 12 printf("fork program starting "); 13 pid = fork(); 14 switch(pid) 15 { 16 case -1: 17 perror("fork failed"); 18 exit(1); 19 case 0: 20 message = "This is the child"; 21 n = 3; 22 break; 23 default: 24 message = "This is the parent"; 25 n = 50; 26 break; 27 } 28 29 for(; n > 0; n--) { 30 puts(message); 31 sleep(1); 32 } 33 exit(0); 34 }
在子进程结束之后父进程结束之前调用pa -al可以看到下面的输出情况:
1 F S UID PID PPID C PRI NI ADDR SZ WCHAN TTY TIME CMD 2 0 S 1000 3362 2710 0 80 0 - 523 hrtime pts/6 00:00:00 fork2 3 1 Z 1000 3363 3362 0 80 0 - 0 exit pts/6 00:00:00 fork <defunct>
中断父进程的执行会得到子进程为僵尸进程,这个进程会被init进程收养,在init清理之前,一致消耗系统资源
输入和输出的重定向
1 #include <stdio.h> 2 #include <ctype.h> 3 4 int main() 5 { 6 int ch; 7 while((ch = getchar()) != EOF) 8 { 9 putchar(toupper(ch)); 10 } 11 exit(0); 12 }//读取输入并将输入字符转换成大写
执行的效果:
1 jason@t61:~/c_program/544977-blp3e/chapter11$ ./upper 2 hello WORLD 3 HELLO WORLD 4 HI linux 5 HI LINUX 6 ^C 7 jason@t61:~/c_program/544977-blp3e/chapter11$
重定向可以实现:
1 jason@t61:~/c_program/544977-blp3e/chapter11$ cat file.txt 2 this is the file, file.txt; it is all lower case. 3 jason@t61:~/c_program/544977-blp3e/chapter11$ ./upper <file.txt 4 THIS IS THE FILE, FILE.TXT; IT IS ALL LOWER CASE.
下面用程序实现重定向的过滤
1 jason@t61:~/c_program/544977-blp3e/chapter11$ cat useupper.c 2 /* This code, useupper.c, accepts a file name as an argument 3 and will respond with an error if called incorrectly. */ 4 5 #include <unistd.h> 6 #include <stdio.h> 7 8 int main(int argc, char *argv[]) 9 { 10 char *filename; 11 12 if(argc != 2) //要是输入不是一个参数的话,报错退出 13 { 14 fprintf(stderr, "usage: useupper file "); 15 exit(1); 16 } 17 18 filename = argv[1];//将第一个参数赋值给fielname 19 20 /* That done, we reopen the standard input, again checking for any errors as we do so, 21 and then use execl to call upper. */ 22 23 if(!freopen(filename, "r", stdin)) //关闭标准输入,把文件流stdin和给定的文件名关联起来 24 {//失败处理 25 fprintf(stderr, "could not redirect stdin to file %s ", filename); 26 exit(2); 27 } 28 29 execl("./upper", "upper", 0);//打开成功,执行execl用upper代替正在运行的程序代码 30 31 /* Don't forget that execl replaces the current process; 32 provided there is no error, the remaining lines are not executed. */ 33 34 fprintf(stderr, "could not exec upper! ");//不会执行这句 35 exit(3); 36 }
执行效果:
1 jason@t61:~/c_program/544977-blp3e/chapter11$ ./useupper file.txt 2 THIS IS THE FILE, FILE.TXT; IT IS ALL LOWER CASE. 3 jason@t61:~/c_program/544977-blp3e/chapter11$