进程
- 进程标识符PID
- 父子进程的产生
fork()、vfork() - 进程的消亡及资源释放
- exec函数族
- 用户权限及组权限
- 观摩:什么是解释器文件
- system()函数
- 进程会计
- 进程时间
- 守护进程
- 系统日志
进程PID
类型pid_t ,有符号16位整型数 进程号顺序向下使用 命令pd getpid() getppid()
进程创建
man fork
NAME
fork - create a child process //复制当前进程,一模一样,执行到的位置都一样
SYNOPSIS
#include <sys/types.h>
#include <unistd.h>
pid_t fork(void); //
返回值:
成功:在父进程中返回的是子进程的PID,在子进程中返回的是0
失败:-1,返回给父进程,并设置errno
duplicating:代码一式两份,一摸一样,除了以下几点: 父子进程的区别:
- PID不同,PPID不同
- fork()的返回值不同
- 未决信号(还没来得及响应的信号)和文件锁不继承
- 资源利用量清零
init进程:所有进程的父进程,PID为1
调度器调度策略,来决定哪个进程先运行。
#include <sys/types.h>
#include <unistd.h>
#include<stdio.h>
#include<stdlib.h>
int main(int argc, char const *argv[]) {
pid_t pid;
printf("[%d]Begin!\n",getpid());
//fflush(NULL); //fork()之前一定要刷新缓冲区,如果不刷新,子进程缓冲区同样会有"[父进程号]:Begin!",从而会导致进程输出重定向至文件时,出现两次“[父进程号]:Begin!”
//标准输出行缓冲机制,文件全缓冲机制
pid=fork();
if (pid<0) {
perror("fork()");
exit(1);
}
if (pid==0) {
//子进程
printf("[%d]:Child is working\n",getpid());
}else{
//父进程
printf("[%d]:Parent is working!\n",getpid());
}
printf("[%d]:End!\n",getpid());
}
===================未刷新流=============
lry@ubuntu:~/codes/procrdd$ cat /tmp/out
[2506]Begin! -------------注意哦
[2507]:Child is working
[2507]:End!
[2506]Begin! ------------注意哦,父进程Begin
[2506]:Parent is working!
[2506]:End!
查看进程树 ps -afx 顶格表示父进程是init进程,此时子进程是孤儿进程
fork()之前一定要刷新缓冲区!!!!fflush()
fork-demo
fork-demo1
#include <sys/types.h>
#include <unistd.h>
#include<stdio.h>
#include<stdlib.h>
#define LEFT 30000000
#define RIGHT 30000200
int main(int argc, char const *argv[]){
int i=0,j,mark;
for (i= LEFT; i <= RIGHT; i++) {
mark=1;
for (j = 2; j < i/2; j++) {
if (i%j==0) {
mark=0;
break;
}
}
if (mark) {
printf("%d is a primer\n",i);
}
}
exit(0);
}
fork-demo2
#include <sys/types.h>
#include <unistd.h>
#include<stdio.h>
#include<stdlib.h>
#define LEFT 30000000
#define RIGHT 30000200
int main(int argc, char const *argv[]){
int i=0,j,mark;
pid_t pid;
for (i= LEFT; i <= RIGHT; i++) {//201次
pid=fork();//子进程与父进程执行的位置(状态)一样
if (pid<0) {
perror("fork()");
exit(1);
}else if(pid==0){
mark=1;
for (j = 2; j < i/2; j++) {
if (i%j==0) {
mark=0;
break;
}
}
if (mark) {
printf("%d is a primer\n",i);
}
exit(0);
}
}
exit(0);
}
时间统计
lry@ubuntu:~/codes/procrdd$ time ./getprime > /dev/null
real 0m0.789s
user 0m0.784s
sys 0m0.004s
lry@ubuntu:~/codes/procrdd$ time ./getprime2 > /dev/null
real 0m0.070s
user 0m0.000s
sys 0m0.012s
谁打开谁关闭,谁申请谁释放 父进程给子进程收尸,否组子进程会成为僵尸进程 僵尸态:应该是一闪而过,若操作系统或父进程比较忙,僵尸状态可能会持续一会, 僵尸进程会浪费PID
#include <sys/types.h>
#include <unistd.h>
#include<stdio.h>
#include<stdlib.h>
#define LEFT 30000000
#define RIGHT 30000200
int main(int argc, char const *argv[]){
int i=0,j,mark;
pid_t pid;
for (i= LEFT; i <= RIGHT; i++) {//201次
pid=fork();//子进程与父进程执行的位置(状态)一样
if (pid<0)
{
perror("fork()");
exit(1);
}else if(pid==0){
//printf("我正在找素数,我的PID:%d",getpid());
mark=1;
for (j = 2; j < i/2; j++) {
if (i%j==0) {
mark=0;
break;
}
}
if (mark) {
printf("%d is a primer\n",i);
}
// sleep(1000); //子进程执行完直接退出,子进程没有人收尸,于是便成为了僵尸进程
exit(0);
}
}//谁打开谁关闭,谁申请谁释放
sleep(1000); //父进程睡眠态
exit(0);
}
父进程创建完子进程后sleep(1000),子进程执行完之后因无人收尸变成僵尸进程,父进程醒后直接退出,子进程变成孤儿进程,挂靠init进程
vfork
fork
写时拷贝 父子进程,谁写谁拷贝,写的时候才拷贝
进程的消亡及资源释放
5个正常终止,3个异常终止
wait()
waitpid()
waitid() wait3() wait4()
man 2 wait
NAME
wait, waitpid, waitid - wait for process to change state //等待进程状态发生变化
SYNOPSIS
#include <sys/types.h>
#include <sys/wait.h>
pid_t wait(int *wstatus); //把当前子进程收尸收回来的状态放到wstatus中
参数:wstatus 进程状态存储的地址
返回值:pid_t
成功:已终止的子进程的pid
失败:-1
pid_t waitpid(pid_t pid, int *wstatus, int options);
int waitid(idtype_t idtype, id_t id, siginfo_t *infop, int options);
WIFEXITED(status) 返回true,如果正常退出:exit() / _exit() / return /
WEXITSTASTUS(status) 只有WIFEXITED返回true,才能调用这个函数。返回子进程的退出码
WIFSIGNALED(status) 如果子进程被信号终止,那么返回true
WTREMSIG(status) 返回导致子进程结束的信号的编号,只有WIFSIGNALED返回true才能调用
WCOREDUPM(status) 如果子进程产生了core文件,返回true,只有WIFSIGNALED返回true才能调用
pid_t waitpid(pid_t pid, int *wstatus, int options);
参数:
pid,
< -1,给任一子进程收尸,进程组ID为pid绝对值的进程组
= 0,给进程自己的任一子进程收尸,进程组ID,等于当前进程的进程组ID,同组ID子进程
> 0,给指定pid进程收尸
wstatus,
option
WNOHANG 立即返回,如果子进程没有退出(阻塞-->非阻塞)
wait()的封装:waitpid(-1, &wstatus, 0);
待计算 进程数
201 --> 201
201 --> N 找N个子进程进行计算
方法①:分块法
进程1:[1-67]
进程2:[68-135]
进程3:[136-201]
方法②:交叉分配法
有一个进程永远拿不到素数,N的倍数那个
方法③:池内算法
exec函数族
FEW:fork、exec、wait 同样需要注意刷新流fflush()
NAME
execl, execlp, execle, execv, execvp, execvpe - execute a file //运行一个二进制可执行文件
The exec() family of functions replaces the current process image with a new process image.
替换进程映像
SYNOPSIS
#include <unistd.h>
extern char **environ;
int execl(const char *pathname, const char *arg, ... /* (char *) NULL */);
int execlp(const char *file, const char *arg, .../* (char *) NULL */);
int execle(const char *pathname, const char *arg, ... /*, (char *) NULL, char *const envp[] */);
int execv(const char *pathname, char *const argv[]);
int execvp(const char *file, char *const argv[]);
int execvpe(const char *file, char *const argv[], char *const envp[]);
返回值:如果有返回值,则表示出错了
返回值是-1,设置errno
示例:
#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/wait.h>
int main(int argc, char const *argv[])
{
pid_t pid;
puts("Begin!");
fflush(NULL);
pid=fork();
if (pid<0)
{
perror("fork()");
exit(1);
}
if (pid==0)
{
execl("/bin/date","date","+%s",NULL); //木马可伪装称date
perror("execl()");
exit(1);
}
wait(NULL);
puts("End!");
exit(0);
}
文件描述符继承: 外部命令:能够在磁盘上找到二进制可执行文件的命令 内部命令: 外部命令实现:
myshell 命令实现
#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <string.h>
#include <glob.h>
#define DELIMS " \t\n"
struct cmd_st
{
glob_t globres;
};
static void prompt(void){
printf("mysh-0.0.1$ ");
}
static void parse(char *line,struct cmd_st *res){
char *tok;
int i=0;
while (1)
{
tok=strsep(&line,DELIMS);
if(tok==NULL)
break;
if(tok[0]=='\0')
continue;
glob(tok,GLOB_NOCHECK|GLOB_APPEND*i,NULL,&res->globres);
i=1;
}
}
int main(int argc, char const *argv[])
{
/**
* 1. 打印命令提示符
* 2. 获取输入的命令
* 3. 解析得到的命令
* 4. 判断外部、内部命令
* 如果是内部命令,什么也不做
* 如果是外部命令,
* fork();
* 子进程执行execl
* 父进程wait收尸
**/
char *linebuf=NULL;
size_t linebuf_size=0;
struct cmd_st cmd;
pid_t pid;
while (1)
{
prompt();
if(getline(&linebuf,&linebuf_size,stdin)<0)
break;
parse(linebuf,&cmd);
if (0)
{
//暂不处理内部命令
}else{
pid=fork();
if (pid<0)
{
perror("fork()");
exit(1);
}
if (pid==0)
{
execvp(cmd.globres.gl_pathv[0],cmd.globres.gl_pathv);
perror("execvp()");
exit(1);
}else{
wait(NULL);//父进程
}
}
}
exit(0);
}
UNIX–三个函数few组成
用户权限和组权限
执行命令的时候带着执行者的身份。 u+s ,如果一个可执行文件有u+s 权限,那么当其他用户执行这个可执行文件的时候,该用户会切换成这个可执行文件的user身份,然后再执行 g+s ,同理u+s u+s和g+s用来将root权限下放。
UID: 存储了三份: real 真实的 effective 有效的,鉴定权限看的是effective save(可以没有save)
GID 由exec 来鉴定权限
lry@ubuntu:~/codes$ ls -l /usr/bin/passwd
-rwsr-xr-x 1 root root 68208 Jul 15 06:08 /usr/bin/passwd
's': 保存当前文件是否有u+s、 g+s的权限
常用函数
getuid() geteuid() getgid() getegid() setuid() setgid() 设置effective gid setreuid() 交换ruid和euid setregid() 交换rgid和egid seteuid() setegid()
脚本文件,解释器文件
#!+解释器 #!/bin/bash 看到脚本文件标记,直接把/bin/bash装载进来 解释器:
可以修改登录的shell为/usr/bin/top
system()函数
fork、exec*、wait的简单封装
man system
NAME
system - execute a shell command
SYNOPSIS
#include <stdlib.h>
int system(const char *command);
进程会计
略。 man 5 acct 不可移植
进程时间
man 2 times
AME
times - get process times
SYNOPSIS
#include <sys/times.h>
clock_t times(struct tms *buf);
DESCRIPTION
times() stores the current process times in the struct tms that buf points to. The struct tms is as
defined in <sys/times.h>:
struct tms {
clock_t tms_utime; /* user time */
clock_t tms_stime; /* system time */
clock_t tms_cutime; /* user time of children */
clock_t tms_cstime; /* system time of children */
};
守护进程
- 脱离控制终端
- 一般是会话leader,进程组leader
会话 session,标识sid 终端(虚拟终端)
最多只有一个前台进程组,可以没有前台进程组,前台进程组能够接收标准输入或者进行标准输出。 后台进程组则不可以,如果试图将标准输入的内容发送给后台进程,那么将会导致后台进程退出。后台进程需要重定向IO
重要函数setsid
NAME
setsid - creates a session and sets the process group ID
SYNOPSIS
#include <sys/types.h>
#include <unistd.h>
pid_t setsid(void);
ps -axj PPID PID PGID SID TTY PPID为1,PID=PGID=SID,TTY=?的进程,就是守护进程
lry@ubuntu:~/codes/procrdd$ ps -axj
PPID PID PGID SID TTY TPGID STAT UID TIME COMMAND
1 32558 32558 32558 ? -1 Ss 0 0:00 /sbin/iscsid
1 32559 32559 32559 ? -1 S<Ls 0 0:00 /sbin/iscsid
函数getpgrp()
man getpgrp
NAME
setpgid, getpgid, setpgrp, getpgrp - set/get process group
SYNOPSIS
#include <sys/types.h>
#include <unistd.h>
int setpgid(pid_t pid, pid_t pgid); 把pid进程放到进程组pgid
pid_t getpgid(pid_t pid); //获取进程的进程组ID
pid_t getpgrp(void); /* POSIX.1 version */
pid_t getpgrp(pid_t pid); /* BSD version */
int setpgrp(void); /* System V version */
int setpgrp(pid_t pid, pid_t pgid); /* BSD version */
守护进程实例
系统日志
/var/log 系统主日志文件/etc/log/messages syslogd 服务 man openlog
NAME
closelog, openlog, syslog, vsyslog - send messages to the system logger
SYNOPSIS
#include <syslog.h>
void openlog(const char *ident, int option, int facility);
参数:
ident, 人物
option,选项
facility, 消息来源
void syslog(int priority, const char *format, ...);
参数:
priority,日志级别
void closelog(void);
配置文件:/etc/sys
#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <syslog.h>
#include <string.h>
#include <errno.h>
#define FNAME "/tmp/out"
static int daemonize(void) {
int fd;
pid_t pid;
pid = fork();
if (pid < 0) {
//perror("fork()");
return -1;
}
if (pid > 0) {
exit(0);
}
fd = open("/dev/null", O_RDWR);
if (fd < 0) {
//perror("open");
return -1;
}
dup2(fd, 0);
dup2(fd, 1);
dup2(fd, 2);
if (fd > 2) {
close(fd);
}
setsid();
chdir("/");
umask(0);
return 0;
}
#define FNAME "/tmp/out"
int main(int argc, char const *argv[]) {
FILE *fp;
int i = 0;
openlog("mydaemon",LOG_PID,LOG_DAEMON);
if (daemonize()) {
syslog(LOG_ERR,"daemonize() has failed!");
exit(1);
}else {
syslog(LOG_INFO,"daemonize() has succeeded");
};
fp = fopen(FNAME, "w");
if (fp == NULL) {
// perror("fopen()");
syslog(LOG_ERR,"fopen():%s",strerror(errno));
exit(1);
}
syslog(LOG_INFO,"%s has been opened.",FNAME);
for (i = 0;; i++) {
fprintf(fp, "%d\n", i);
fflush(fp);
syslog(LOG_DEBUG,"%d printed.",i);
sleep(1);
}
fclose(fp); //执行不到
closelog(); //执行不到
exit(0);
}
单实例的守护进程
使用锁文件来实现/var/run/name.pid 开机启动脚本文件/etc/rc*
lry@ubuntu:/var/run$ cat /etc/rc.d/rc.local
#!/bin/bash
# secu-tcs-agent bootstart, install at Sun 03 Oct 2021 12:57:11 PM CST
/usr/local/sa/agent/secu-tcs-agent-mon-safe.sh > /dev/null 2>&1
|