fork用于创建进程。
set follow-fork-mode child命令设置gdb在fork之后跟踪子进程(set follow-fork-mode parent则是跟踪父进程),然后用run命令。fork在子进程中返回0,子进程仍可以调用getpid函数得到自己的进程id,也可以调用getppid函数得到父进程的id。在父进程中用getpid可以得到自己的进程id,然而要想得到子进程的id,只有将fork的返回值记录下来。
原型:
#include <sys/types.h>
#include <unistd.h>
pid_t fork(void);
fork调用失败则返回-1
///
Exec函数族
#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[]);
不带字母p(表示path)的exec函数第一个参数必须是程序的相对路径或绝对路径,例如"/bin/ls"或"./a.out",而不能是"ls"或"a.out"。对于带字母p的函数:
如果参数中包含/,则将其视为路径名。
否则视为不带路径的程序名,在PATH环境变量的目录列表中搜索这个程序。
带有字母l(表示list)的exec函数要求将新程序的每个命令行参数都当作一个参数传给它,命令行参数的个数是可变的,因此函数原型中有...,...中的最后一个可变参数应该是NULL,起sentinel的作用。对于带有字母v(表示vector)的函数,则应该先构造一个指向各参数的指针数组,然后将该数组的首地址当作参数传给它,数组中的最后一个指针也应该是NULL,就像main函数的argv参数或者环境变量表一样。
对于以e(表示environment)结尾的exec函数,可以把一份新的环境变量表传给它,其他exec函数仍使用当前的环境变量表执行新程序。
例程:
char *const ps_argv[] ={"ps", "-o", "pid,ppid,pgrp,session,tpgid,comm", NULL};
char *const ps_envp[] ={"PATH=/bin:/usr/bin", "TERM=console", NULL};
execl("/bin/ps", "ps", "-o", "pid,ppid,pgrp,session,tpgid,comm", NULL);
execv("/bin/ps", ps_argv);
execle("/bin/ps", "ps", "-o", "pid,ppid,pgrp,session,tpgid,comm", NULL, ps_envp);
execve("/bin/ps", ps_argv, ps_envp);
execlp("ps", "ps", "-o", "pid,ppid,pgrp,session,tpgid,comm", NULL);
execvp("ps", ps_argv);
/
主要还是fork吧,exec用得不多!
这个进程的父进程可以调用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);
如果父进程的所有子进程都还在运行,调用wait将使父进程阻塞,而调用waitpid时如果在options参数中指定WNOHANG可以使父进程不阻塞而立即返回0。
wait等待第一个终止的子进程,而waitpid可以通过pid参数指定等待哪一个子进程。
进程间通信:
管道是一种最基本的IPC机制,由pipe函数创建:
#include <unistd.h>
int pipe(int filedes[2]);
它有一个读端一个写端,然后通过filedes参数传出给用户程序两个文件描述符,filedes[0]指向管道的读端,filedes[1]指向管道的写端(很好记,就像0是标准输入1是标准输出一样)。所以管道在用户程序看起来就像一个打开的文件,通过read(filedes[0]);或者write(filedes[1]);向这个文件读写数据其实是在读写内核缓冲区。pipe函数调用成功返回0,调用失败返回-1。
Fork pipe综合练习代码:
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#define MAXLINE 80
int main(void)
{
int n;
int fd[2];
pid_t pid;
char line[MAXLINE];
if (pipe(fd) < 0) { //管道
perror("pipe");
exit(1);
}
if ((pid = fork()) < 0) {
perror("fork");
exit(1);
}
if (pid > 0) { /* parent 写数据 */
printf("this is parent pid=%d\n", getpid());
close(fd[0]);
printf("清输入数据:");
char arry[1024] = " ";
scanf("%s", arry);
write(fd[1], arry, sizeof(arry));
wait(NULL);
} else { /* child 读数据 */
printf("this is child pid=%d\n", getpid());
close(fd[1]);
n = read(fd[0], line, MAXLINE);
write(STDOUT_FILENO, line, n); //打印到屏幕
}
return 0;
}
//注:Linux下 indent -kr -i8 fork.c 命令可以自动把代码格式化成某种风格。(-kr选项表示K&R风格,-i8表示缩进8个空格的长度)
编译结果:
[huangbin@localhost shujia]$ ./a.out
this is child pid=26328
this is parent pid=26327
清输入数据:你好
你好
[huangbin@localhost shujia]$ ./a.out
this is child pid=26332
this is parent pid=26331
清输入数据:你好 大家好
你好
[huangbin@localhost shujia]$ ./a.out
this is parent pid=26333
清输入数据:this is child pid=26334
你好大家好
你好大家好
[huangbin@localhost shujia]$
管道的读端读到空格就停???没注意过以前。现在没网查不了。。。。
以为是write的原因,加上printf("%s\n",line);
结果:
[huangbin@localhost shujia]$ ./a.out
this is child pid=26350
this is parent pid=26349
清输入数据:hello world
hello
hello
[huangbin@localhost shujia]$ ./a.out
this is child pid=26352
this is parent pid=26351
清输入数据:hello,world!
hello,world!
hello,world!
一样的问题。以后解决!
解决办法:
用fprintf(stdin,"%[^\n]",str);代替scanf("%s",str);
或者用gets(),也可以。因为scanf这个函数遇到空格就会断,后面的就没有再读进缓冲去了。