Linux 多进程/多线程管理
进程状态
TASK_RUNNING | 可执行状态 | 0 |
---|---|---|
TASK_INTERRUPTIBLE | 可中断的睡眠状态 | 1 |
TASK_UNINTERRUPTIBLE | 不可中断的睡眠状态 | 2 |
TASK_STOPPED | 暂停状态 | 4 |
TASK_ZOMBIE | 退出状态 | 8 |
可中断的睡眠状态
处于这个状态的进程因为等待某某事件的发生(比如等待socket连接、等待信号量),而被挂起。这些进程的task_struct结构被放入对应事件的等待队列中。当这些事件发生时(由外部中断触发、或由其他进程触发),对应的等待队列中的一个或多个进程将被唤醒
不可中断的睡眠状态
进程处于睡眠状态,但是此刻进程是不可中断的。不可中断,指的并不是CPU不响应外部硬件的中断,而是指进程不响应异步信号
暂停状态
向进程发送一个SIGSTOP信号,它就会因响应该信号而进入TASK_STOPPED状态(除非该进程本身处于TASK_UNINTERRUPTIBLE状态而不响应信号)。(SIGSTOP与SIGKILL信号一样,是非常强制的。不允许用户进程通过signal系列的系统调用重新设置对应的信号处理函数。)
向进程发送一个SIGCONT信号,可以让其从TASK_STOPPED状态恢复到TASK_RUNNING状态
退出状态
进程在退出的过程中,处于TASK_DEAD状态
在这个退出过程中,进程占有的所有资源将被回收,除了task_struct结构(以及少数资源)以外。于是进程就只剩下task_struct这么个空壳,故称为僵尸
进程基本属性
进程号 PID
- getpid() 获取当前自己的进程号
- getppig() 获取当前自己的父进程号
- getpgid() 获取当前自己的组进程号
用户号
setuid() 把当前文件 临时提升文件拥有者的权限
- UID 当前进程的权限
- EUID 当前进程有效权限
- S 权限 就是 该文件有setuid的属性
会话号 SID
- getsid() 返回调用进程的会话ID
- setsid() 创建新的会话
控制终端
- tty (终端设备的统称)
- pty (虚拟终端)
- ttyn ( 控制台终端 )
- /dev/pts/n (虚拟终端 )
进程
fork() 新起一个进程
函数通过系统调用创建一个与原来进程几乎完全相同的进程
返回值: 大于0 为父进程 等于0为子进程 -1错误
wait(NULL); 可以让父进程等待子进程退出
- 也会对 IOFILE 中的缓存进行拷贝
复制的 是 _IO_FILE_plus 的文件结构体
- _IO_FILE_plus 的文件结构体 中保存的是 fd对应的文件
- ((struct _IO_FILE_plus )&IO_2_1_stdin) 在gdb中用p可以查看
vfork() 等于 线程
创建的子进程与父进程共享数据段,而且由vfork()创建的子进程将先于父进程运行
- 在必要的时候才新申请内存 需要栈是才申请栈,需要堆在申请堆
守护进程
脱离组进程和父进程
控制终端
**一个会话可以有一个控制终端**
- 控制进程就是打开终端的进程
- 前台终端:会占用当前控制台
- 后台终端:不占用当前控制台
终端发送的信号,只有前台进程组的进程会收到,后台进程不会收到
tcgetpgrp() 获取当前前台进程组的进程号
tcpgetsid() 获取终端首进程的会话ID
进程用户
uid 创建进程真是用户号
- 0 为root 用户
- 0 ~ 1000 系统保留用户
- 1001以上 是用户自己添加的用户
euid 当前进程有效用户号
- se tuid() 把当前文件 临时提升文件拥有者的权限
进程的代码执行
启动新的程序,用新程序的代码段来覆盖当前程序的代码段
头文件 #include
l 的是一类
- 是输入文件路径
p 的是一类
- 是环境变量 所指向的文件路径
execl()
是使用文件路径名字
execlp ()
使用文件名 和 参数表
execve() 经常使用
- 使用指针数组
execle()
- 可变长度参数列表 在参数后面添加arg 的参数
回收进程资源
- 使用 exit() 函数退出 比较干净 会刷新缓冲区
- return 退出 只是单单退出
- on_exit() 接收退出码
on_exit()
参数1:收到退出码执行的函数
参数2:函数的第2个参数,第一个参数是退出码
//第一个参数是退出码
void test_exit(int status,void * arg);
on_exit(test_exit,"buf");
waitpid() 等待线程返回
- waitpid分为阻塞状态和非阻塞状态
参数1:pid号
- pid > 0(表示等代次pid进程结束)
- pid=-1(表示等待任意子进程结束)
- pid= 0(表示等待与当前进程组中的进程结束)
- pid<-1(表示等待pid绝对值的pgid组中的任意进程结束)
- 参数2:传递一个 int 用与该函数的修改
- 参数3:0为阻塞等待,1为非阻塞等待
- 返回值:结束的pid
WIFEXITED() 解析 waitpid第2个参数的值
WEXITSTATUS() 解析 waitpid第2个参数的值
设置守护进程
**得到一个相对干净的进程**
守护进程:使用父进程的子进程的子进程
void creat_daemon()
{
int i,fd;
pid_t id;
struct sigaction sa;
umask(0);//第一步:调用umask将文件模式创建屏蔽字设置为0.
if((id = fork()) < 0){
printf("fork error!\n");
return;
}else if(id != 0){
exit(0);//第二步:调用fork,父进程退出。保证子进程不是话首进程,从而保证后续不和其他终端关联。
}
setsid();//第三步:设置新会话。
sa.sa_handler = SIG_IGN;
sigemptyset(&sa.sa_mask);
sa.sa_flags = 0;
if(sigaction(SIGCHLD,&sa,NULL) < 0){
//注册子进程退出忽略信号
return;
}
if((id = fork()) < 0){
printf("fork 2 error!\n");
return;
}else if(id != 0){
exit(0);
}
// 守护进程空间
if(chdir("/") < 0){
//第四步:更改工作目录到根目录
printf("child dir error\n");
return;
}
close(0);
fd = open("/dev/null",O_RDWR);//关闭标准输入,重定向所有标准(输入输出错误)到/dev/NULL
dup2(fd,1);
dup2(fd,2);
}
设置系统开启启动进程
- 安装 xinetd
在 /etc/xinetd.d/ 目录中新建个配置文件如:
- vi /etc/xinetd.d/date_maine
- 输入:
service date_mine
{
disable = no
type = UNLISTED
socket_type = stream
protocol = tcp
user = root
wait = no
port = 8888
bind = 0.0.0.0
server = /bin/date //启动程序路径
}
- service xinetd start 启动
- service xinetd restart 重新启动