fork
fork创建的新进程被称为子进程(child process),fork函数被调用一次,返回两次,子进程的返回值是0代表成功,而父进程的返回值则是子进程的进程 id。
子进程完全拷贝父进程空间,共享代码空间,但数据空间是互相独立的,子进程数据空间中的内容是父进程的完整拷贝,指令指针也完全相同;子进程里面可以使用父进程的代码和变量,且内存地址相同,但是有自己的数据空间,其表现为对变量操作不影响父进程的值,是独立的。
父进程先运行,但运行后的顺序是不确定的,如果涉及同步问题可以使用进程间通信机制解决。可使用sleep,wait等待子进程先运行。
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
int gi = 0;
int main()
{
int li = 0;
static int si=0;
pid_t pid = fork();
if(0 == pid)
{
printf("child pid=%d,parent pid=%d\n", getpid(),getppid());
gi += 1;
li += 1;
si += 1;
printf("child process:%d - %d - %d\n", gi, li, si);//1 - 1 - 1
}
else if(0 < pid)
{
printf("child pid=%d\n", pid);
sleep(1);//wait son process
gi += 1;
li += 1;
si += 1;
printf("parent process:%d - %d - %d\n", gi, li, si);//1 - 1 - 1
}
else
{
perror("fork failed,exit -1!");
exit(-1);
}
exit(0);
}
vfork
vfork创建的子进程共享父进程的内存地址空间,即子进程完全运行在父进程的地址空间上,子进程对虚拟地址空间的修改同样为父进程所见,为了防止父进程重写子进程重要数据,阻塞父进程的执行,一直到子进程退出或执行一个新的程序为止。不能用sleep函数让父进程先运行。
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int gi = 0;
int main()
{
int li = 0;
static int si=0;
pid_t pid = vfork();
if(0 > pid)
{
perror("fork");
}
else if(0 == pid)
{
gi += 1;
li += 1;
si += 1;
printf("child process:%d - %d - %d\n", gi, li, si);//1 - 1 - 1
}
else
{
gi += 1;
li += 1;
si += 1;
printf("parent process:%d - %d - %d\n", gi, li, si);//2 - 2 - 2
}
exit (0);
}
exec函数族
在进程中启动一个程序,成功不返回,失败返回-1。
#include <stdio.h>
#include <unistd.h>
int main()
{
char *agv[] = {"ls", "-al", NULL};
char *env[] = {"PATH=.", NULL};
//if(0 > execl("/bin/ls", "ls", "-la", NULL))
//if(0 > execv("/bin/ls", agv))
//if(0 > execlp("ls", "-al", NULL))
if(0 > execle("b.out", "b.out", NULL, env))
{
perror("exec*");
return -1;
}
}
system
system会调用fork()产生子进程,由子进程来调用/bin/sh -c string来执行参数string字符串所代表的命令,命令执行完后随即返回原调用的进程。
=-1:出现错误
= 0:调用成功但是没有出现子进程
> 0:成功退出的子进程的id
#include <stdio.h>
#include <stdlib.h>
int main()
{
int ret = system("ls -al");
if(ret < 0)
perror("system");
else
printf("ret=%d\n",ret);//ret=0
return 0;
}
exit
登记函数atexit,程序退出时调用的函数,int atexit (void (*)(void)),exit调⽤终⽌处理函数的顺序和atexit登记的顺序相反。
_exit,直接使进程终止运行,清除其使用的空间,并销毁内核中各种数据结构。
exit,在调用exit系统函数之前,检查打开文件,把文件缓冲区的内容写回文件,清理I/O缓冲,并调用登记函数。
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
void e1(void)
{
printf("-------1--------\n");
}
void e2(void)
{
printf("-------2--------\n");
}
int main()
{
if(0 > atexit(e2))
{
perror("atexit");
return -1;
}
if(0 > atexit(e1))
{
perror("atexit");
return -1;
}
printf("main code\n");
int i = 0;
i++;
exit(-1);
//_exit(-1);
}
打印:
main code
-------1--------
-------2--------
Daemon守护进程
调用fork,创建子进程,而调用setsid函数之前,当前进程不允许是进程组的Leader,则需要将父进程退出;
调用setsid函数创建一个会话;
将当前工作目录更改为根目录其他目录;
umask(0):调用umask将文件模式创建屏蔽字设置为0. 原因:又继承来的文件模式创建屏蔽字可能会拒绝设置某种权限,而守护进程需要创建的文件要具有读写权限。
实例:创建守护进程a.out,在/tmp目录下创建文件demo.txt,每隔3秒写字符串"hello world\n"到文件。
#include <stdio.h>
#include <unistd.h>
int main()
{
pid_t pid = fork();
if(0 > pid)
{
perror("fork");
exit(-1);
}
if(0 < pid)
{
exit(-1);//parent process exit
}
setsid();
chdir("/tmp");
umask(0);
int fdsz = getdtablesize();
int i = 0;
for(i = 0; i < fdsz; i++)
close(i);
while(1)
{
FILE *fp = fopen("demo.txt", "a");
if(NULL == fp)
{
perror("fopen");
exit(-1);
}
char buf[] = "hello world\n";
int ret = fwrite(buf, sizeof(buf)-1, 1, fp);
if(0 > ret)
{
perror("fwrite");
}
fclose(fp);
sleep(3);
}
}