拥有梦想是一种智力,实现梦想是一种能力。
并发程序是应用开发中非常重要的一部分内容,如何实现程序的并发?包括多进程编程、进程间通信机制、多线程编程、线程间同步和异步机制等等。本次介绍多进程编程:
系统调用fork允许一个进程(父进程)创建一个新进程(子进程)。通过fork,子进程几乎是父进程的复制版本,子进程获得父进程的栈、数据段、堆和执行文本段的拷贝。通常,调用fork产生子进程后,子进程随便会调用execve函数簇执行新的任务,随后执行exit相关函数退出。而父进程则通常会调用wait函数等待子进程终止。此外,我会通过程序的方向来介绍,如何让一个子进程脱离它原有的终端成为一个守护进程运行。
pid_t fork(void);
- 创建新的进程,失败时返回-1
- 成功时父进程返回子进程的进程号,子进程返回0
- 通过fork的返回值区分父进程和子进程
#include
#include
#include
int main()
{
pid_t pid;
if ((pid = fork()) < 0) {
perror(“fork”);
return -1;
}
else if (pid == 0) {//子进程执行
printf(“child process : my pid is %d\n”, getpid());
}
else {//父进程执行
printf(“parent process : my pid is %d\n”, getpid());
}
return 0;
}
此时,子进程fork()函数之后,而不是程序的开始处进行。由于子进程继承了父进程的所有内容,所以两个进程的执行没有差分。
void exit(int status);
void _exit(int status);
- 结束当前的进程并将status返回
exit与_exit的区别是,exit结束进程时会刷新(流)缓冲区。
#include
#include
int main(void)
{
printf("this process will exit");//
exit(0);
printf("never be displayed");
}
由于shell终端是行缓冲输出,printf输出的字符串又没有换行,所以 "this process will exit" 会驻留在缓冲区。exit()操作会刷新缓冲区,终端显示 "this process will exit"。
#include
int execl(const char *path, const char *arg, …);
int execlp(const char *file, const char *arg, …);
- 成功时执行指定的程序;失败时返回EOF
- path 执行的程序名称,包含路径
- arg… 传递给执行的程序的参数列表
- file 执行的程序的名称,在PATH中查找
进程调用exec函数族执行某个程序,进程当前内容被指定的程序替换。从而实现让父子进程执行不同的程序。
#include
#include
int main()
{
if (execl(“/bin/ls”, “ls”, “-a”, “-l”, “/etc”, NULL) < 0)
{
perror(“execl”);
}
return 0;
}
问:如何让父子进程执行不同程序?
答:fork()创建一个子进程,然后exec()让进程执行其他程序,这样就达到了父子进程执行不同程序目的。
如果子进程结束了但是没有被回收的话,此时进程进入死亡态,叫做僵尸进程。僵尸进程的进程控制块依然存在(PCB),会占用资源,务必对其进行回收。通过父进程对其回收。
#include
pid_t wait(int *status);
- 成功时返回回收的子进程的进程号;失败时返回EOF
- 若子进程没有结束,父进程一直阻塞
- 若有多个子进程,哪个先结束就先回收
- status 指定保存子进程返回值和结束方式的地址
- status为NULL表示直接释放子进程PCB,不接收返回值
#include
#include
#include
#include
int main()
{
int status;
pid_t pid;
if ((pid = fork()) < 0) {
perror(“fork”);
exit(-1);
}
else if (pid == 0) {
sleep(1);
exit(2);
}
else {
wait(&status);
printf(“%x\n”, status);
}
return 0;
}
wait()函数被父进程执行,父进程便进入阻塞态(一直等待),直到子进程结束。status保存子进程的返回值和结束方式地址。
守护进程通常在系统启动时运行,系统关闭时结束。Linux很多服务程序以守护进程形式运行。其特点
Linux以会话(session)、进程组的方式管理进程。例如,我在打开shell(一个会话建立),运行一个脚本程序,然后关闭sell终端(会话关闭),程序也随之结束。而守护进程是不会随着终端关闭而结束的!!!
#include
#include
#include
#include
int main() {
pid_t pid;
FILE *fp;
time_t t;
int i;
if ((pid = fork()) < 0)
{
perror(“fork”);
exit(-1);
}
else if (pid > 0)
{
exit(0);
}
setsid();
umask(0);
chdir(“/tmp”);
for (i=0; i< getdtablesize(); i++)
{
close(i);
}
if ((fp = fopen(“time.log”, “a”)) == NULL)
{
perror(“fopen”);
exit(-1);
}
while ( 1 )
{
time(&t);
fprintf(fp, “%s”, ctime(&t));
fflush(fp);
sleep(1);
}
return 0;
}
if ((pid = fork()) < 0)
{
perror(¡°fork¡±);
exit(-1);
}
else if (pid > 0)
{
exit(0);
}
setsid();
umask(0);
chdir()
for (i=0; i< getdtablesize(); i++)
{
close(i);
}