linux应用编程笔记(8)多进程程序设计
2015-10-13 16:19
281 查看
摘要: 总结了进程控制理论,getpid的用法,多进程程序设计中创建进程,等待进程,退出进程,fork和vfork的区别,exec函数族的用法,每个总结给出一个实例加深理解。
一、进程控制理论
关于进程的互斥,同步,竞争,死锁,调度策略,优先级等,之前有一篇帖子已经介绍了,链接地址如下:
http://blog.csdn.net/deep_l_zh/article/details/48346287
这里介绍一个函数,就是getpid:
函数原型:pid_t getpid(void)
函数作用:返回调用该函数的进程的id
头文件:include <sys/types.h> include <unistd.h>
参数:无
返回值:调用该函数的进程的id
例子:example1.c
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
pid_t pid1,pid2;
void fun(void)
{
pid2=getpid();
printf("process2 id is:%d\n",pid2);
}
int main(void)
{
pid1=getpid();
printf("process1 id is:%d\n",pid1);
fun();
return0;
}
得到的结果如下:
#process1 id is:29045
#process2 id is:29045
可以看到我们得到了进程的pid号,虽然是在不同的函数里面调用的,为什么打印的pid号是一样的,这是因为他们属于同一个进程!
二、多进程程序设计
1.创建进程fork
函数原型:pid_t fork(void)
函数作用:创建一个子进程
头文件:include <unistd.h>
参数:无
返回值:成功:父进程中返回子进程的pid
子进程中返回0
失败:父进程中返回-1.子进程不会被创建
<span style="font-size:18px;">例程:myfork.c
#include <stdio.h>
#include <unistd.h>
int main(void)
{
pid_tpid;
pid=fork();
if(pid>0)
{
printf("thisis father process!%d\n",pid);
}
else
{
printf("thisis child process!%d\n",pid);
exit(0);
}
return0;
}
输出结果如下:
#thisis father process!30339
#thisis child process!0</span>
可以看到打印出来了父进程和子进程的pid号,这里fork之后,子进程从该处开始和父进程共用代码段,也就是为什么fork之后,紧接着一个printf会打印两条相同的语句,一个是父进程执行的,一个是子进程执行的。这里我们通过判断pid的大小来判断是父进程还是子进程,虽然是if…else,但是两个分支里面的语句都被执行了,这说明子进程在运行。
2.创建进程vfork
函数原型:pid_t vfork
函数作用:创建一个子进程并且阻塞父进程
头文件:include <sys/types.h> include <unistd.h>
参数:无
返回值:成功:父进程中返回子进程的pid
子进程中返回0
失败:父进程中返回-1.子进程不会被创建
例程:myvfork.c
#include <stdio.h>
#include <unistd.h>
int main(void)
{
pid_t pid;
pid=vfork();
if(pid>0)
{
printf("this is father process!%d\n",pid);
}
else
{
printf("this is child process!%d\n",pid);
sleep(2);
exit(0);
}
return0;
}
运行结果如下:
#this is child process!0
#this is father process!3717
但是这里有一个过程需要注意,红色字体是标出来需要注意的,虽然程序和上一个很类似,我们在子进程当中让程序睡眠了2S钟,所以在运行的时候,子进程先打印,然后睡眠2S,这时候父进程才打印出了相关信息,这是vfork和fork一个不同之处:子进程先执行,在子进程执行结束之前,父进程是阻塞的,子进程退出了,父进程在继续执行!
三、for和vfork的不同之处
1.fork:子进程拥有独立的数据段,堆栈。
vfork:子进程与父进程共享数据段,堆栈。
2.fork:父、子进程的执行次序不确定。
vfork:子进程先运行,父进程后运行。
<span style="font-size:18px;">例程:dif.c
#include <stdio.h>
#include <unistd.h>
int main(void)
{
intcount=0;
pid_tpid;
pid=fork();//vfork()
count++;
printf("%d\n",count);
exit(0);
return0;
}</span>
当红色字体部分为fork时候,输出两个1,当为vfork的时候,输出了1和2,这就是因为当我们fork的时候,子进程复制了父进程的数据段和堆栈,因此他们是各自在count=0的基础上+1,而vfork创建的子进程和父进程共同数据段和堆栈,所以count被加了两次输出1和2。
三、进程退出
1.一般正常情况下退出:exit(0),异常情况退出exit(1).
2.父进程可以使用return 0和exit(0)来退出,子进程只能使用exit(0),不能使用return 0来退出。
四、进程等待
函数原型:pid_t wait(int *status)
函数功能:挂起调用它的进程,直到其子进程运行结束
头文件:include <sys/type.s> include <unistd.h>
返回值:成功:返回被终止的子进程的id
失败:返回-1
参数说明:status记录子进程退出时的状态,可以设置为NULL。
例子:mywait.c
#include <stdio.h>
#include <unistd.h>
#include <sys/wait.h>
int main(void)
{
pid_t pid;
pid=fork();
if(pid>0)
{
wait(NULL);
printf("this is father process!%d\n",pid);
}
else
{
printf("this is child process!%d\n",pid);
exit(0);
}
return0;
}
程序运行结果如下:
#this is child process!0
#this is father process!9802
可以看到父进程等待子进程退出之后才继续执行。
五、exec函数族
之前的fork,它在创建子进程之后,子进程和父进程共用数据段和堆栈,得到的是一个父进程的拷贝,那么我们fork有什么意义呢?这时候可以配合exec函数族,在fork后的子进程中使用exec函数族,可以装入和运行其它程序,这样子进程替换原有进程,和父进程做不同的事。
1.exec在替换了原有的进程之后,还是会使用调用它的子进程的pid。
该函数族如下:
(1)int execl(const char*path, const char *arg, ......);
(2)intexecle(const char *path, const char *arg, ...... , char * const envp[]);
(3)intexecv(const char *path, char *const argv[]);
(4)intexecve(const char *filename, char *const argv[], char *const envp[]);
(5)intexecvp(const char *file, char * const argv[]);
(6)intexeclp(const char *file, const char *arg, ......);
exec函数族装入并运行程序path/file,并将参数arg0(arg1, arg2,argv[],
envp[])传递给子程序,出错返回-1.
在exec函数族中,后缀l、v、p、e指定函数将具有某种操作能力:
2. 换句话说,exec函数族的作用是根据指定的文件名找到可执行文件,并用它来取代调用进程的内容,换句话说,就是在调用进程内部执行一个可执行文件。这里的可执行文件既可以是二进制文件,也可以是任何Linux下可执行的脚本文件。
3.与一般情况不同,exec函数族的函数执行成功后不会返回,因为调用进程的实体,包括代码段,数据段和堆栈等都已经被新的内容取代,只留下进程ID等一些表面上的信息仍保持原样,只有调用失败了,它们才会返回一个-1,从原程序的调用点接着往下执行。
4.在平时的编程中,如果用到了exec函数族,一定记得要加错误判断语句。先判断execl的返回值,如果出错,可以用perror( )函数打印出错误信息。
例程:myexec.c
#include <stdio.h>
#include <unistd.h>
int main(void)
{
pid_t pid;
pid=fork();
if(pid>0)
{
printf("thisis father process!%d\n",pid);
}
else
{
if(execl("/bin/ls","ls","-a",NULL)==-1)
{
perror("execlerror!");
exit(1);
}
printf("thisis child process!%d\n",pid);
exit(0);
}
return0;
}
运行结果如下:
其余的就不一一做实验了,这篇帖子就总结到这里吧,如有不正确的地方还请指出,大家共同进步!
一、进程控制理论
关于进程的互斥,同步,竞争,死锁,调度策略,优先级等,之前有一篇帖子已经介绍了,链接地址如下:
http://blog.csdn.net/deep_l_zh/article/details/48346287
这里介绍一个函数,就是getpid:
函数原型:pid_t getpid(void)
函数作用:返回调用该函数的进程的id
头文件:include <sys/types.h> include <unistd.h>
参数:无
返回值:调用该函数的进程的id
例子:example1.c
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
pid_t pid1,pid2;
void fun(void)
{
pid2=getpid();
printf("process2 id is:%d\n",pid2);
}
int main(void)
{
pid1=getpid();
printf("process1 id is:%d\n",pid1);
fun();
return0;
}
得到的结果如下:
#process1 id is:29045
#process2 id is:29045
可以看到我们得到了进程的pid号,虽然是在不同的函数里面调用的,为什么打印的pid号是一样的,这是因为他们属于同一个进程!
二、多进程程序设计
1.创建进程fork
函数原型:pid_t fork(void)
函数作用:创建一个子进程
头文件:include <unistd.h>
参数:无
返回值:成功:父进程中返回子进程的pid
子进程中返回0
失败:父进程中返回-1.子进程不会被创建
<span style="font-size:18px;">例程:myfork.c
#include <stdio.h>
#include <unistd.h>
int main(void)
{
pid_tpid;
pid=fork();
if(pid>0)
{
printf("thisis father process!%d\n",pid);
}
else
{
printf("thisis child process!%d\n",pid);
exit(0);
}
return0;
}
输出结果如下:
#thisis father process!30339
#thisis child process!0</span>
可以看到打印出来了父进程和子进程的pid号,这里fork之后,子进程从该处开始和父进程共用代码段,也就是为什么fork之后,紧接着一个printf会打印两条相同的语句,一个是父进程执行的,一个是子进程执行的。这里我们通过判断pid的大小来判断是父进程还是子进程,虽然是if…else,但是两个分支里面的语句都被执行了,这说明子进程在运行。
2.创建进程vfork
函数原型:pid_t vfork
函数作用:创建一个子进程并且阻塞父进程
头文件:include <sys/types.h> include <unistd.h>
参数:无
返回值:成功:父进程中返回子进程的pid
子进程中返回0
失败:父进程中返回-1.子进程不会被创建
例程:myvfork.c
#include <stdio.h>
#include <unistd.h>
int main(void)
{
pid_t pid;
pid=vfork();
if(pid>0)
{
printf("this is father process!%d\n",pid);
}
else
{
printf("this is child process!%d\n",pid);
sleep(2);
exit(0);
}
return0;
}
运行结果如下:
#this is child process!0
#this is father process!3717
但是这里有一个过程需要注意,红色字体是标出来需要注意的,虽然程序和上一个很类似,我们在子进程当中让程序睡眠了2S钟,所以在运行的时候,子进程先打印,然后睡眠2S,这时候父进程才打印出了相关信息,这是vfork和fork一个不同之处:子进程先执行,在子进程执行结束之前,父进程是阻塞的,子进程退出了,父进程在继续执行!
三、for和vfork的不同之处
1.fork:子进程拥有独立的数据段,堆栈。
vfork:子进程与父进程共享数据段,堆栈。
2.fork:父、子进程的执行次序不确定。
vfork:子进程先运行,父进程后运行。
<span style="font-size:18px;">例程:dif.c
#include <stdio.h>
#include <unistd.h>
int main(void)
{
intcount=0;
pid_tpid;
pid=fork();//vfork()
count++;
printf("%d\n",count);
exit(0);
return0;
}</span>
当红色字体部分为fork时候,输出两个1,当为vfork的时候,输出了1和2,这就是因为当我们fork的时候,子进程复制了父进程的数据段和堆栈,因此他们是各自在count=0的基础上+1,而vfork创建的子进程和父进程共同数据段和堆栈,所以count被加了两次输出1和2。
三、进程退出
1.一般正常情况下退出:exit(0),异常情况退出exit(1).
2.父进程可以使用return 0和exit(0)来退出,子进程只能使用exit(0),不能使用return 0来退出。
四、进程等待
函数原型:pid_t wait(int *status)
函数功能:挂起调用它的进程,直到其子进程运行结束
头文件:include <sys/type.s> include <unistd.h>
返回值:成功:返回被终止的子进程的id
失败:返回-1
参数说明:status记录子进程退出时的状态,可以设置为NULL。
例子:mywait.c
#include <stdio.h>
#include <unistd.h>
#include <sys/wait.h>
int main(void)
{
pid_t pid;
pid=fork();
if(pid>0)
{
wait(NULL);
printf("this is father process!%d\n",pid);
}
else
{
printf("this is child process!%d\n",pid);
exit(0);
}
return0;
}
程序运行结果如下:
#this is child process!0
#this is father process!9802
可以看到父进程等待子进程退出之后才继续执行。
五、exec函数族
之前的fork,它在创建子进程之后,子进程和父进程共用数据段和堆栈,得到的是一个父进程的拷贝,那么我们fork有什么意义呢?这时候可以配合exec函数族,在fork后的子进程中使用exec函数族,可以装入和运行其它程序,这样子进程替换原有进程,和父进程做不同的事。
1.exec在替换了原有的进程之后,还是会使用调用它的子进程的pid。
该函数族如下:
(1)int execl(const char*path, const char *arg, ......);
(2)intexecle(const char *path, const char *arg, ...... , char * const envp[]);
(3)intexecv(const char *path, char *const argv[]);
(4)intexecve(const char *filename, char *const argv[], char *const envp[]);
(5)intexecvp(const char *file, char * const argv[]);
(6)intexeclp(const char *file, const char *arg, ......);
exec函数族装入并运行程序path/file,并将参数arg0(arg1, arg2,argv[],
envp[])传递给子程序,出错返回-1.
在exec函数族中,后缀l、v、p、e指定函数将具有某种操作能力:
后缀 | 操作能力 |
l | 希望接收以逗号分隔的参数列表,列表以NULL指针作为结束标志 |
v | 希望接收到一个以NULL结尾的字符串数组的指针 |
p | 是一个以NULL结尾的字符串数组指针,函数可以DOS的PATH变量查找子程序文件 |
e | 函数传递指定参数envp,允许改变子进程的环境,无后缀e时,子进程使用当前程序的环境 |
3.与一般情况不同,exec函数族的函数执行成功后不会返回,因为调用进程的实体,包括代码段,数据段和堆栈等都已经被新的内容取代,只留下进程ID等一些表面上的信息仍保持原样,只有调用失败了,它们才会返回一个-1,从原程序的调用点接着往下执行。
4.在平时的编程中,如果用到了exec函数族,一定记得要加错误判断语句。先判断execl的返回值,如果出错,可以用perror( )函数打印出错误信息。
例程:myexec.c
#include <stdio.h>
#include <unistd.h>
int main(void)
{
pid_t pid;
pid=fork();
if(pid>0)
{
printf("thisis father process!%d\n",pid);
}
else
{
if(execl("/bin/ls","ls","-a",NULL)==-1)
{
perror("execlerror!");
exit(1);
}
printf("thisis child process!%d\n",pid);
exit(0);
}
return0;
}
运行结果如下:
其余的就不一一做实验了,这篇帖子就总结到这里吧,如有不正确的地方还请指出,大家共同进步!
相关文章推荐
- Linux socket 初步
- linux lsof详解
- linux 文件权限
- Linux 执行数学运算
- 10 篇对初学者和专家都有用的 Linux 命令教程
- Linux 与 Windows 对UNICODE 的处理方式
- Ubuntu12.04下QQ完美走起啊!走起啊!有木有啊!
- 解決Linux下Android开发真机调试设备不被识别问题
- 运维入门
- 运维提升
- Linux 自检和 SystemTap
- Ubuntu Linux使用体验
- c语言实现hashmap(转载)
- Linux 信号signal处理机制
- linux下mysql添加用户
- Scientific Linux 5.5 图形安装教程
- 基于 Linux 集群环境上 GPFS 的问题诊断
- 谁是桌面王者?Win PK Linux三大镇山之宝
- vivi下重新调整分区