进程的概念:进程是一个具有独立功能的程序的一次运行活动、 资源分配的最小单元。
进程与程序:1、程序是放到磁盘里的可执行文件(静态的、长久的)
2、进程是指程序执行的实例(动态的、短暂的)一个状态的变化过程
通过多次执行,一个程序可对应多个进程;通过调用关系,一个进程可包括多个程序。
创建:父进程(PPID)—>子进程(PID)——>孙进程 启动进程的用户ID(UID)
在Linux系统中,进程具有并行性、互不干扰等特点。一个进程发生异常不会影响其他进程。
(1)、“数据段”存放的是全局变量、常数以及动态数据分配的数据空间;
(2)、“代码段”存放的是程序代码的数据。
(3)、“堆栈段”存放的是子程序的返回地址、子程序的参数以及程序的局部变量等。
进程互斥:是指当有若干进程都要使用某一共享资源时,任何时刻最多允许一个进程使用,其他要使用该资源的进程必须等待,直到占用该资源者释放了该资源为止。
#include
#include
pid_t getpid(void) 获取本进程ID。
pid_t getppid(void) 获取父进程ID
#include
#include
#include
int main()
{
printf("pid : %d\n", getpid());
printf("ppid : %d\n", getppid());
while (1);
return 0;
}
【注】:加while死循环是为了让这个进程不结束,才可以获取id
#include
pid_t fork(void)
功能:创建子进程
fork的奇妙之处在于它被调用一次,却返回两次,它可能有三种不同的返回值:
返回值: 0: 子进程 // 子进程ID(大于0):父进程 // -1: 出错
(1)、父进程创建子进程
#include
#include
#include
#include
int main()
{
pid_t pid;
int a = 0;
pid = fork(); //创建子进程
if (-1 == pid) //失败返回-1
{
perror("fork");
exit(1);
}
else if (0 == pid) //子进程
{
a++;
printf("Child a = %d %p\n", a, &a);
printf("This is ChildProcess pid %d ppid %d!\n", getpid(), getppid());
printf("%d\n", pid);
}
else //父进程
{
a++;
printf("Parent a = %d %p\n", a, &a);
printf("This is ParentProcess %d!\n", getpid());
printf("%d\n", pid);
}
return 0;
}
值得注意的是fork( )创建子进程是大多数情况是先返回0,再返回子进程的ID。
(2)、父进程写,子进程读 fork实现
#include
#include
#include
#include
#include
#include
#include
#include
void ReadFile()
{
char buf[32] = {0};
int fd = open("file.tmp", O_RDONLY);
if (-1 == fd)
{
perror("open");
exit(1);
}
int ret = read(fd, buf, sizeof(buf));
if (-1 == ret)
{
perror("read");
exit(1);
}
printf("Child read from txt : %s\n", buf);
}
void WriteFile()
{
char buf[32] = "helloworld";
int fd = open("file.tmp", O_WRONLY | O_CREAT | O_EXCL, S_IRWXU);
if (-1 == fd)
{
perror("open");
exit(1);
}
int ret = write(fd, buf, strlen(buf));
if (-1 == ret)
{
perror("write");
exit(1);
}
}
int main()
{
pid_t pid;
pid = fork();
if (-1 == pid)
{
perror("fork");
exit(1);
}
else if (0 == pid) //子进程读数据
{
sleep(1);
ReadFile();
}
else //父进程写数据
{
WriteFile();
sleep(2);
}
return 0;
}
解决方法在wait处。
(3)、第二种方法
#include
#include
#include
#include
#include
#include
#include
#include
void ReadFile(int fd)
{
char buf[32] = {0};
lseek(fd, 0, SEEK_SET);
int ret = read(fd, buf, sizeof(buf));
if (-1 == ret)
{
perror("read");
exit(1);
}
printf("Child read from txt : %s\n", buf);
}
void WriteFile(int fd)
{
char buf[32] = "helloworld";
int ret = write(fd, buf, strlen(buf));
if (-1 == ret)
{
perror("write");
exit(1);
}
}
int main()
{
pid_t pid;
int fd;
fd = open("file.tmp", O_RDWR | O_CREAT | O_EXCL, S_IRWXU);
if (-1 == fd)
{
perror("open");
exit(1);
}
pid = fork();
if (-1 == pid)
{
perror("fork");
exit(1);
}
else if (0 == pid) //子进程读数据 子进程可以继承父进程已经打开的文件描述符
{
sleep(1);
ReadFile(fd);
}
else //父进程写数据
{
WriteFile(fd);
sleep(2);
}
return 0;
}
count++被父进程、子进程一共执行了两次,为什么count的第二次输出为什么不为2?
图解:
子进程的数据空间、堆栈空间都会从父进程得到一个拷贝,而不是共享。 在子进程中对count进行加1的操作,并没有影响到父进程中的count值,父进程中的count值仍然为0
#include
#include
pid_t vfork(void)
功能:创建子进程
#include
#include
#include
#include
int main()
{
pid_t pid;
int a = 0;
pid = vfork();
if (-1 == pid)
{
perror("vfork");
exit(1);
}
else if (0 == pid) //子进程 子进程先运行
{
sleep(2);
a++;
printf("Child Process a = %d\n", a);
exit(1); //子进程指定退出方式
}
else //父进程
{
a++;
printf("Parent Process a = %d!\n", a);
}
return 0;
}
区别: 1. fork:子进程拷贝父进程的数据 vfork:子进程与父进程共享数据
2. fork:父、子进程的执行次序不确定 vfork:子进程先运行,父进程后运行
Int execl(const char *path, const char *arg,….)
(1)、execl execl(“可执行进程的绝对路劲”);
#include
#include
#include
#include
int main()
{
pid_t pid;
//char *argv[] = {"./test", "hello", "world", "123", NULL};
pid = vfork();
if (-1 == pid)
{
perror("vfork");
exit(1);
}
else if (0 == pid)
{
execl("/mnt/hgfs/share/0815/Process/test", "./test", "hello", "world", "123", NULL);
sleep(2);
}
else
{
printf("This is Parent Process %d!\n", getpid());
}
return 0;
}
可以看出孤儿进程出现了,这是因为在vfork中子进程遇到execl( ),父进程就不用等子进程结束,遇到execl父进程也开始运行了,至于父和子谁先输出,这就看概率了。
被调用函数
#include
int main(int argc, char *argv[])
{
int i;
printf("%d\n", getpid());
for (i = 1; i < argc; i++)
{
printf("%s\n", argv[i]);
}
return 0;
}
(2)、execv
#include
#include
#include
#include
int main()
{
pid_t pid;
char *argv[] = {"./test", "hello", "world", "123", NULL};
pid = vfork();
if (-1 == pid)
{
perror("vfork");
exit(1);
}
else if (0 == pid)
{
execv("/mnt/hgfs/share/0815/Process/test", argv);
sleep(2);
}
else
{
printf("This is Parent Process %d!\n", getpid());
}
return 0;
}
(1)、pid_t wait (int * status)
功能:阻塞该进程,直到其某个子进程退出。
#include
#include
#include
#include
#include
#include
#include
#include
int main()
{
pid_t pid;
pid = fork(); //创建子进程
if(-1 == pid) //失败返回-1
{
perror("fork");
exit(1);
}
else if( 0 == pid) //子进程
{
sleep(1);
printf("this is child process!\n");
exit(100);
}
else //父进程
{
int status;
printf("this is Parent process\n");
wait(&status); //1、等待子进程结束(阻塞) 2、回收子进程资源
if(WIFEXITED(status)) //判断子进程是否正常退出
{
printf("child exit normally %d !\n", WEXITSTATUS(status)); //获取子进程退出状态
}
}
return 0;
}
(2)、waitpid
pid_t waitpid (pid_t pid, int * status, int options) // options一般为0
功能: 会暂时停止目前进程的执行,直到有信号来到或子进程结束
#include
#include
#include
#include
#include
#include
int main()
{
pid_t pid;
pid = fork(); //创建子进程
if (-1 == pid) //失败返回-1
{
perror("fork");
exit(1);
}
else if (0 == pid) //子进程
{
sleep(1);
printf("This is ChildProcess pid!\n");
exit(100);
}
else //父进程
{
int status;
printf("This is ParentProcess!\n");
waitpid(pid, &status, 0);
if (WIFEXITED(status)) //判断子进程是否正常退出
{
printf("Child Exit Normally! %d\n", WEXITSTATUS(status)); //获取子进程退出状态
}
}
return 0;
}
参数:如果不在意结束状态值,则参数status可以设成NULL。
参数pid为欲等待的子进程识别码:
pid<-1 等待进程组识别码为pid绝对值的任何子进程。
pid=-1 等待任何子进程,相当于wait()。
pid=0 等待进程组识别码与目前进程相同的任何子进程。
pid>0 等待任何子进程识别码为pid的子进程。
首先写一个死循环:
#include
int main()
{
printf("%d\n", getpid());
while(1)
{
;
}
return 0;
}
#include
#include
#include
int main()
{
int id;
scanf("%d", &id);
kill(id, SIGKILL);
return 0;
}
这里明确一点:目前说的信号 1, 2, 3 啊之类的不是数字1,2,3,是kill的选项:
SIGKILL 即 ‘9’
执行结果:
#include
int main()
{
printf("%d\n", getpid());
raise(9);
while(1)
{
;
}
return 0;
}
自杀!!!
此时执行ctrl -c或者是 kill -2 都会继续出现“helloworld” (13683是 ./signal的PID || 通过 ps -elf | grep signal查找);
#include
#include
void print(int num)
{
printf("heloworld! %d\n", num);
}
int main()
{
signal(2, SIG_IGN);
while(1);
return 0;
}
signal后面不调用函数换成SIG_IGN就能忽略信号
此时按 Ctrl-c是没有用的。
不可被忽略