紧接着上一篇,我们来写写关于进程的编程。
获取ID
通过man getpid这个命令我们可以在Linux终端上获取到这个函数详细的讲解
#include
#include
pid_t getpid(void)
pid_t getppid(void)
下面通过一段代码来看看查找进程ID
int main()
{
printf ("当前进程id:%d\n",getpid());
printf ("当前父进程id:%d\n",getppid());
printf ("当前用户进程id:%d\n",getuid());
while (1);
return 0;
}
输出结果在不同终端上会有所不同。
进程创建fork()
#include
pid_t fork(void)
功能:创建子进程
fork()有一个很神奇的地方,就是在于它被调用一次,却返回两次,它可能有三个不同的返回值:
1.在父进程中,fork返回新创建的子进程的PID;
2.在子进程中,fork返回0;
3若出现错误,fork返回一个负值;
☆当fork()创建成功时,这时候会存在两个进程,每个进程都会fork()处开始执行。
☆两个进程执行相同的代码段(text),但是却各自的堆栈段(stack)、数据段(data)以及堆(heap)。
☆其中子进程的stack,data,heap都是从父进程拷贝过来的,两个进程只是共享代码段。
fork()之后,不能确定哪个进程先执行,会有什么隐患吗?
产生原因:
fork()之后,不能确定是父进程还是子进程获得CPU。
危害:
这种bugs很难被发现。
措施:
如果需要确保特定的执行顺序,需要采用某种同步(synchronization)技术(semaphores,file locks…)。
fork()代码:
#include
#include
#include
int main()
{
pid_t pid = fork();
if (pid == -1)
{
perror ("fork");
return -1;
}
if (pid > 0)
{
printf ("我是父进程,pid = %d\n",getpid());
}
if (pid == 0)
{
printf ("我是子进程,pid = %d\n",getpid());
}
return 0;
}
//那么问题来了
int main()
{
fork();
fork();
fork();
//创建了几个进程
return 0;
}
int main()
{
fork();
fork() && fork() || fork();
fork();
//这又创建了几个进程
return 0;
}
这里提一下fork()的兄弟vfork(),两者功能几乎一样,唯一不同的是vfork()需要用exit()来退出。
exec函数家族
execl()
int execl(const char * path, const char* arg1,...)
参数:
path : 被执行程序名(含完整路径)。
arg1 - argn: 被执行程序所需的命令行参数,含程序名。以空指针(NULL)结束。
execl代码:
#include
#include
int main1()
{
int ret = execl("/bin/ls", "ls", NULL);
if (ret == -1)
{
perror ("execl");
return 0;
}
return 0;
}
execlp()
execlp()与execl()唯一不同的是直接写执行文件在当前目录下没有找到会到环境变量中查找
Linux是在bin目录下
int main()
{
int ret = execlp("/mnt/hgfs/share/0809/file", "./file", NULL);
if (ret == -1)
{
perror ("execlp");
return 0;
}
return 0;
}
execv()
#include
int execv(const char * path, const char *argv[])
参数:
path : 被执行程序名(含完整路径)。
argv[]: 被执行程序所需的命令行参数数组。
代码:
int main()
{
char *a[100] = {"./file", NULL};
int ret = execv("/mnt/hgfs/share/0809/file", a);
if (ret == -1)
{
perror ("execlp");
return 0;
}
return 0;
}
system()
#include
int system(const char* string)
功能:
调用fork产生子进程,由子进程来调用 /bin/sh -c string来执行参数string所代表的命令
首先表示个人很喜欢这个函数,功能强大,使用起来不墨迹,平时喜欢用的system(“clear”)就是清屏,也可以在括号内填上执行文件的路径,我们甚至可以利用它来制造一个伪终端,但有个瑕疵,就是部分功能实现不了。
代码:
int main()
{
char str[100];
while (1)
{
printf ("[root@localhost 0809]# ");
fgets (str, 100, stdin);
system (str);
}
return 0;
}
执行结果
可以看出来进入伪终端后实行退出当前目录并没有成功。
进程的终止
这里用到exit,_exit来终止进程。两者有个明显的区别:
_exit:直接停止进程,清除其使用的内存,并清除缓冲区的内容。(像是一个冷酷的特工,只求结果不求过程)
exit:在停止进程之前,要检查文件的打开情况,并把文件缓冲区的内容写回到文件才停止进程(好人,帮人帮到底)
下一篇将会详细说明子父进程之间的恩怨情仇。