基础操作系统-进程-并发程序设计
这学期感觉最有难度的就是操作系统这门课了,以前Linux接触的不多,双学位跳级学习课程暂时还没有学完其中的基础课。所以可能错误多一些。
1、 相关头文件
<unistd.h> <sys/types.h> <sys/wait.h> <stdio.h> <stdlib.h>
2、 函数说明
l fork(建立一个新的进程)
定义函数 pid_t fork(void);
函数说明 fork()会产生一个新的子进程,其子进程会复制父进程的数据与堆栈空间,并继承父进程的用户代码,组代码,环境变量、已打开的文件代码、工作目录和资源限制等。
返回值 如果fork()成功则在父进程会返回新建立的子进程代码(PID),而在新建立的子进程中则返回0。如果fork 失败则直接返回-1,失败原因存于errno中。
l waitpid(等待子进程中断或结束)
定义函数 pid_t waitpid(pid_t pid,int * status,int options);
函数调用 waitpid(pid, NULL, 0);
函数说明 waitpid()会暂时停止目前进程的执行,直到有信号来到或子进程结束。如果在调用waitpid()时子进程已经结束,则wait()会立即返回子进程结束状态值。子进程的结束状态值会由参数status返回,而子进程的进程识别码也会一快返回。如果不在意结束状态值,则参数status可以设成NULL。参数pid为欲等待的子进程识别码,其他数值意义如下:
pid<-1 等待进程组识别码为pid绝对值的任何子进程。
pid=-1 等待任何子进程,相当于wait()。
pid=0 等待进程组识别码与目前进程相同的任何子进程。
pid>0 等待任何子进程识别码为pid的子进程。
返回值 如果执行成功则返回子进程识别码(PID),如果有错误发生则返回-1。失败原因存于errno
l getpid(取得进程识别码)
定义函数 pid_t getpid(void);
函数说明 getpid()用来取得目前进程的进程识别码,许多程序利用取到的此值来建立临时文件,以避免临时文件相同带来的问题。
返回值 目前进程的进程识别码
l exit(正常结束进程)
定义函数 void exit(int status);
函数说明 exit()用来正常终结目前进程的执行,并把参数status返回给父进程,而进程所有的缓冲区数据会自动写回并关闭未关闭的文件。
l execl(执行文件)
定义函数 int execl(const char * path,const char * arg,....);
函数说明 execl()用来执行参数path字符串所代表的文件路径,接下来的参数代表执行该文件时传递过去的argv(0)、argv[1]……,最后一个参数必须用空指针(NULL)作结束。
返回值 如果执行成功则函数不会返回,执行失败则直接返回-1,失败原因存于errno中。
调用ls命令范例: execl("/bin/ls", "/bin/ls", "-l" , "/etc", NULL);
安装VMware Workstation Pro,下载Fedora Linux镜像文件,安装成功后如下图。打开终端,输入命令su进入管理员账户,输入yum install gcc 安装gcc编译器,测试编译环境正常;
单个子进程的创建分析:通过gcc first.c进行编译,在.a/.out执行输出,例程刚开始输出父进程pid 2218
#include<unistd.h>
#include<stdarg.h>
#include<time.h>
#include<sys/types.h>
#include<sys/wait.h>
#include<stdio.h>
#include<stdlib.h>
int tprintf (const char*fmt, ...);
int main(void)
{
int i=0 ,j=0;
pid_t pid;
printf("Hello from Parent Process, PID is %d.\n", getpid());
- fork()会产生一个新的子进程,其子进程会复制父进程的数据与堆栈空间,并继承父进程的用户代码,组代码,环境变量、已打开的文件代码、工作目录和资源限制等。因为父与子进程的pid值不一样,可以分别对父进程和子进程做操作,当pid为0时可以对子进程做操作,当pid>0时对父进程做操作,当pid为-1时创建进程失败输出提示信息;
pid = fork();
if(pid == 0) //child process
{
sleep(1);
for(i=0;i<3;i++)
{
printf("Hello from Child Process %d. %d times\n", getpid(), i+1);
sleep(1);
}
}
else if(pid != -1) //parent process
{
tprintf("Parent forked one child process--%d.\n",pid);
tprintf("Parent is waiting for child to exit.\n");
waitpid(pid,NULL,0);
tprintf("Child Process has exited.\n");
tprintf("Parent had exited.\n");
}
else
{
tprintf("Everything was done without error.\n");
}
return 0;
}
waitpid()会暂时停止目前进程的执行,直到有信号来到或子进程结束。如果在调用waitpid()时子进程已经结束,则wait()会立即返回子进程结束状态值。子进程的结束状态值会由参数status返回,而子进程的进程识别码也会一快返回。如果不在意结束状态值,则参数status可以设成NULL。
int tprintf(const char*fmt, ...)
{
va_list args;
struct tm *tstruct;
time_t tsec;
tsec = time(NULL);
tstruct = localtime(&tsec);
printf("%02d:%02d:%02d %5d|", tstruct->tm_hour,tstruct->tm_min,tstruct->tm_sec, getpid());
va_start(args,fmt);
return vprintf(fmt,args);
}
当pid=-1时创建进程失败输出错误时间、PID等错误信息。最后运行结果如下:
在子进程中调用外部命令;
#include<unistd.h>
#include<stdarg.h>
#include<time.h>
#include<sys/types.h>
#include<sys/wait.h>
#include<stdio.h>
#include<stdlib.h>
int tprintf (const char*fmt, ...);
int main(void)
{
pid_t pid;
pid = fork();
if(pid == 0) //child process
{
sleep(5);
tprintf("Hello from Child Process!\n");
tprintf("I am calling exec.\n");
execl("/bin/ps","-a",NULL);
tprintf("You should never see this because the child is already gone.\n");
}
int execl(const char * path,const char * arg,....)用来执行参数path字符串所代表的文件路径,接下来的参数代表执行该文件时传递过去的argv(0)、argv[1]……,最后一个参数必须用空指针(NULL)作结束。返回值:如果执行成功则函数不会返回,执行失败则直接返回-1,失败原因存于errno中;
else if(pid != -1) //parent process
{
tprintf("Hello from Parent,pid %d.\n", getpid());
sleep(1);
tprintf("Parent forked process %d.\n",pid);
sleep(1);
tprintf("Parent is waiting for child to exit.\n");
waitpid(pid,NULL,0);
tprintf("Parent had exited.\n");
}
sleep是让线程指定休眠时间,然后继续工作 wait则是等待,直到有线程通知notify()唤醒他才会重新工作;
else
{
tprintf("Everything was done without error.\n");
}
return 0;
}
int tprintf(const char*fmt, ...)
{
va_list args;
struct tm *tstruct;
time_t tsec;
tsec = time(NULL);
tstruct = localtime(&tsec);
printf("%02d:%02d:%02d %5d|", tstruct->tm_hour,tstruct->tm_min,tstruct->tm_sec, getpid());
va_start(args,fmt);
return vprintf(fmt,args);
}
编写一段程序实现以下功能:
使用系统调用fork()创建两个子进程
各个子进程显示和输出一些提示信息和自己的进程标识符。
父进程显示自己的进程ID和一些提示信息,然后调用waitpid()等待多个子进程结束,并在子进程结束后显示输出提示信息表示程序结束。
#include<unistd.h>
#include <stdarg.h>
#include <time.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <stdio.h>
#include <stdlib.h>
int tprintf(const char*fmt,...);
int main()
{
int i=0,j=0;
pid_t pid1;
pid_t pid2;
printf("Hello from Parent Process,PID is %d.\n",getpid());
pid1 = fork();
if(pid1 == 0)
{
sleep(1);
for(i=0;i<3;i++)
{
printf("Hello from Child Process one %d. %d times\n",getpid(),i+1);
sleep(1);
}
}
else if(pid1 !=-1)
{
tprintf("Part forked one child process--%d.\n",pid1);
tprintf("Parent is waiting for child to exit.\n");
waitpid(pid1,NULL,0);
pid2 = fork();
if(pid2 == 0)
{
sleep(1);
for(i=0;i<3;i++)
{
printf("Hello from Child Process two %d. %d times\n",getpid(),i+1);
sleep(1);
}
}
else if(pid2 !=-1)
{
tprintf("Part forked one child process--%d.\n",pid2);
tprintf("Parent is waiting for child to exit.\n");
waitpid(pid2,NULL,0);
tprintf("Child Process has exited.\n");
tprintf("Parent had exited.\n");
}
}
else tprintf("Everything was done without error.\n");
return 0;
}
int tprintf(const char*fmt,...)
{
va_list args;
struct tm *tstruct;
time_t tsec;
tsec = time(NULL);
tstruct = localtime (&tsec);
printf("%02d:%02d:%02d:%5d",tstruct->tm_hour,tstruct->tm_min,tstruct->tm_sec,
getpid());
va_start(args,fmt);
return vprintf(fmt,args);
}
通过父进程创建一个子进程,运行一个子进程,再创建一个子进程,再运行一个子进程的方式,输出各个进程的标识码,可以得到正确的结果。而当两个子进程同时fork()时,会产生3个子进程,这是应为2个fork()放在一起时,会多fork()一个子进程。
创建多个(3个以上)进程并发运行,控制好各个子进程输出自己的进程标识符和一些提示信息
#include<unistd.h>
#include<stdarg.h>
#include<time.h>
#include<sys/types.h>
#include<sys/wait.h>
#include<stdio.h>
#include<stdlib.h>
int tprintf (const char*fmt, ...);
int main(void)
{
int i=0 ,j=0;
pid_t pid,pid1,pid2;
printf("Hello from Parent Process, PID is %d.\n", getpid());
pid = fork();//fork1
if(pid == 0) //child process 1
{
sleep(1);
for(i=0;i<3;i++)
{
printf("Hello from Child Process 11111 %d. %d times\n", getpid(), i+1);
sleep(5);
}
}
else if(pid != -1) //parent process
{
tprintf("Parent forked 111 child process--%d.\n",pid);
pid1 = fork();//fork2
if(pid1 == 0) //child process 2
{
sleep(1);
for(i=0;i<3;i++)
{
printf("Hello from Child Process 22222 %d. %d times\n", getpid(), i+1);
sleep(5);
}
}
else if(pid1 != -1) //parent process
{
tprintf("Parent forked 222 child process--%d.\n",pid);
pid2 = fork();//fork3
if(pid2 == 0) //child process 3
{
sleep(1);
for(i=0;i<3;i++)
{
printf("Hello from Child Process 33333 %d. %d times\n", getpid(), i+1);
sleep(5);
}
}
else if(pid2 != -1) //parent process
{
tprintf("Parent forked 333 child process--%d.\n",pid);
//wait for children
pid_t temp_p;
while((temp_p = waitpid(-1, NULL, 0)) > 0)
{
tprintf("child had exited %d \n",temp_p);
}
tprintf("Parent had exited.\n");
}
else
{
tprintf("3333 Everything was done without error.\n");
}
}
else
{
tprintf("2222 Everything was done without error.\n");
}
}
else
{
tprintf("1111 Everything was done without error.\n");
}
return 0;
}
int tprintf(const char*fmt, ...)
{
va_list args;
struct tm *tstruct;
time_t tsec;
tsec = time(NULL);
tstruct = localtime(&tsec);
printf("%02d:%02d:%02d %5d|", tstruct->tm_hour,tstruct->tm_min,tstruct->tm_sec, getpid());
va_start(args,fmt);
return vprintf(fmt,args);
}
输出结果与设想中的顺序不同。因为占用内存的进程不会因为wait pid中的PID参数而固定不变,而是三个子进程会按照第一次运行的顺序继续运行,也就是第一次运行的顺序固定之后,后面的运行顺序不再改变。因此输出顺序结果如下图:
作者:Nathaneko