Linux多线程编程—进程
一.进程
- 含义:装载程序运行的载体 正在运行的程序 动态的 进程分配资源的最小单位
- 进程标识 PID(Process ID) init 1
- 查看进程
ps aux 查看所有用户的所有进程信息
pstree 查看进程树
top 动态查看进程 - 进程状态
就绪态
执行态
阻塞态 - 父:fork–
子 exec(任务)
死 --exit (int)
wait() - 孤儿进程:父进程消亡 子进程没有消亡 子进程就是孤儿进程 init领养并收尸
僵尸进程:子进程消亡 父进程没有消亡 但却不为子进程收尸 子进程是僵尸进程 僵尸进程会造成资源浪费 避免僵尸进程 - 进程PID
#include <sys/types.h>
#include <unistd.h>
(1) pid_t getpid(void);
功能:获取当前调用进程PID
#include<stdio.h>
#include<unistd.h>
#include<sys/types.h>
int main()
{
printf("%d\n",getpid());
}
(2) pid_t getppid(void);
功能:获取当前调用进程的父进程PID
#include<stdio.h>
#include<unistd.h>
#include<sys/types.h>
int main()
{
printf("child:%d par:%d\n",getpid(),getppid());
}
- 创建进程
#include <unistd.h>
pid_t fork(void);
功能:创建子进程
成功返回值:返回两次
父进程中:返回子进程PID
子进程中:返回0
失败返回-1
1)代码
#include<stdio.h>
#include<unistd.h>
#include<sys/types.h>
int main()
{
fork();
printf("child:%d par:%d\n",getpid(),getppid());//printf会执行两次 说明有两个进程调用printf
}
- 如何区分父子进程
int main()
{
pid_t pid = fork();
if(pid<0)
{
perror("fork");
}
else if(pid>0)
{
printf("father:%d ",getpid());
}
else
{
printf("child:%d ",getpid());
}
}
注意:父子进程执行的先后顺序不确定 取决于系统调度策略
sleep控制父子进程执行顺序
练习1:实现父进程打印"par is running …" 子进程打印"child is running "
间隔1s
#include<stdio.h>
#include<unistd.h>
#include<sys/types.h>
int main()
{
pid_t pid = fork();
if(pid<0)
{
perror("fork");
}
else if(pid>0)//父亲
{
sleep(1);//保证子先运行
while(1)
{
printf("father:%d\n",getpid());
sleep(1);
}
}
else //子
{
while(1)
{
printf("child:%d\n",getpid());
sleep(1);
}
}
}
练习2:父进程中有一个变量count=0 创建子进程后 子进程count++
打印父进程的count和子进程count
3) 循环创建多进程
#include<stdio.h>
#include<unistd.h>
#include<sys/types.h>
int main()
{
int i;
for(i=0;i<3;i++)
{
fork();
printf("pid:%d ppid:%d\n",getpid(),getppid());
}
}
i=0 父 子1
i=1 子2 子1-1
i=2 子3 子1-2 子2-1 子1-1-1
4)进程消亡
(1)#include <stdlib.h>
void exit(int status);
功能:退出进程 会刷新IO缓存
(2)#include <unistd.h>
void _exit(int status);
功能:立即退出进程 不会刷新IO缓存
#include<stdio.h>
#include<unistd.h>
int fun()
{
//return 0;//返回到调用者
printf("day day up");
//exit(-1);//退出进程
_exit(-1);
}
int main()
{
fun();·
puts("************");
}
5)进程收尸
#include <sys/types.h>
#include <sys/wait.h>
(1)pid_t wait(int *status);
功能:等待一个子进程结束 取得子进程结束的状态 将其保存在status中
返回值:返回被收尸的子进程PID 如果没有子进程返回-1
注意:
a、父进程调用wait时 父进程会阻塞等待子进程结束
b、如果父进程不关心子进程退出的状态 wait参数可以传NULL
c、wait后 子进程占用的资源会被释放
#include<stdio.h>
#include<unistd.h>
#include<sys/types.h>
int main()
{
int count=10;
pid_t pid = fork();
if(pid<0)
{
perror("fork");
}
else if(pid>0)//父
{
pid_t waitpid = wait(NULL);
printf("father-waitpid:%d pid:%d\n",waitpid,pid);
}
else //子
{
sleep(1);
printf("child:%d\n",getpid());
}
}
(2) waitpid
pid_t waitpid(pid_t pid, int *status, int options);
功能:同wait
参数1:
-1 等待任意子进程收尸
>0 为进程编号为pid的子进程收尸
参数2:同wait
参数3:
WNOHANG 没有子进程消亡则不阻塞等待
0 阻塞等待子进程消亡
返回值:
成功收尸返回被收尸的子进程PID 没有字进程消亡返回0 失败-1
#include<stdio.h>
#include<unistd.h>
#include<sys/types.h>
#include<sys/wait.h>
int main()
{
int count=10;
pid_t pid = fork();
if(pid<0)
{
perror("fork");
}
else if(pid>0)//父
{
//pid_t wait_pid = waitpid(-1,NULL,0);
pid_t wait_pid = waitpid(-1,NULL,WNOHANG);
printf("father-waitpid:%d pid:%d\n",wait_pid,pid);
}
else //子
{
sleep(1);
printf("child:%d\n",getpid());
}
}
6)进程执行任务
exec族函数:在本进程中加载另一个程序 并且从头开始执行 本进程完全由新程序替换
(1)int execl(const char *path, const char *arg, …);
格式:execl(可执行程序的路径,可执行程序的名称,参数1,参数2,参数3,…,NULL);
注意:可执行文件路径 必须是which结果 which ls
#include<unistd.h>
int main()
{
puts("come-------------------");
execl("/bin/ls","ls","-i",NULL);
puts("bye----------------------");//不会输出 因为加载完新程序 原来程序会被替换
}
练习:创建一个文件
#include<unistd.h>
int main()
{
puts("come-------------------");
//execl("/bin/ls","ls","-i",NULL);
execl("/usr/bin/touch","touch","happy.txt",NULL);
puts("bye----------------------");
}
(2)int execvp(const char *file, char *const argv[]);
格式:execvp(可执行文件名,指针数组)
#include<unistd.h>
int main()
{
puts("come-------------------");
//execl("/bin/ls","ls","-i",NULL);
char *cmd[]={"ls","-i",NULL};
execvp("ls",cmd);
puts("bye----------------------");
}
#include<unistd.h>
int main()
{
puts("come-------------------");
//execl("/bin/ls","ls","-i",NULL);
char *cmd[]={"touch","smile",NULL};
execvp("touch",cmd);
puts("bye----------------------");
}
//str.c
#include<stdio.h>
int main()
{
puts("aaa----");
}
//exec.c
#include<unistd.h>
int main()
{
execl("./str","str",NULL);
}
练习: 编程实现 执行ls -l 执行完毕后 打印finished
子进程 ls -l
父 收尸并打印
#include<unistd.h>
#include<stdio.h>
#include<sys/types.h>
int main()
{
pid_t pid = fork();
if(pid<0)
{
perror("fork");
}
else if(pid>0)
{
wait(NULL);
puts("finished!");
}
else
{
execl("/bin/ls","ls","-l",NULL);
}
}
- 守护进程编写
前台进程:依附终端 终端结束 进程结束
后台进程:不依附终端 自己独立存在
进程组:多个进程
会话:多个进程组
1.创建子进程 父进程结束(init领养子进程)
2.创建新会话 setsid()
3.改变当前工作目录
chdir("/tmp")
4.修改文件掩码
掩码:掩掉权限
umask(0)
5.关闭所有打开的文件描述符
int getdtablesize(void);//获取当前进程打开的文件总个数
int n=getdtablesize();
for(i=0;i<n;i++)
{
close(i);
}
文件描述符分配规则:
当前尚未分配最小非负 0 1 2 3 4 5
#include<time.h>
#include<stdio.h>
int main()
{
time_t t = time(NULL);
printf("%s",ctime(&t));
}
#include<stdio.h>
#include<sys/types.h>
#include<unistd.h>
#include<time.h>
#include<stdlib.h>
void init_daemon()
{
pid_t pid = fork();
if(pid>0)
{
exit(0);
}
setsid();
chdir("/tmp");
umask(0);
int n = getdtablesize();
int i;
for(i=0;i<n;i++)
{
close(i);
}
}
int main()
{
init_daemon();//创建守护进程
FILE *fp=NULL;
time_t t;
while(1)
{
fp = fopen("time.log","a");
if(fp!=NULL)
{
t = time(NULL);
fprintf(fp,"%s",ctime(&t));
fclose(fp);
sleep(1);
}
}
}
练习:编写一个守护进程 每隔1s向文件中写入一个字母 字母从’a’开始 ‘a’~‘z’
#include<stdio.h>
#include<sys/types.h>
#include<unistd.h>
#include<time.h>
#include<stdlib.h>
void init_daemon()
{
pid_t pid = fork();
if(pid>0)
{
exit(0);
}
setsid();
chdir("/tmp");
umask(0);
int n = getdtablesize();
int i;
for(i=0;i<n;i++)
{
close(i);
}
}
int main()
{
init_daemon();//创建守护进程
FILE *fp=NULL;
time_t t;
char ch='a';
while(1)
{
fp = fopen("char.txt","a");
if(fp!=NULL)
{
t = time(NULL);
fprintf(fp,"%c",ch);
fclose(fp);
if(ch=='z')
{
ch='a';
continue;
}
ch++;
sleep(1);
}
}
}