一、进程标识符pid
类型pid_t,16位整型数三万多个。2的16次方65535.
进程标识是顺次向下使用。与文件描述符不一样。
pid_t getpid(void);
pid_t getppid(void);
进程正常终止
1.从main函数返回
2.调用exit
3.调用_exit或_Exit
4.最后一个线程从启动例程返回
5.最有一个线程调用了pthread_exit
进程异常终止
调用abort
接到一个信号并终止
最后一个进行对其取消请求做出响应
二、进程的产生
fork
系统调用,只存在linux环境,duplicating意味着拷贝,克隆。
fork后父子进程的区别:fork返回值不一样,pid不同,ppid也不同,未决信号和文件锁不继承,资源利用量归零。
init进程:所有进程的祖先进程(1号 )
调度器的调度策略会决定哪个程序先运行。
cpp
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
int main()
{
pid_t pid;
printf("[%d]:Begin() \n",getpid() );
pid = fork();
if(pid <0)
{
perror("fork()");
exit(1);
}
else if(pid == 0)
{
//子进程
printf("[%d]:Child is working \n",getpid() );
}
else
{
//父进程
printf("[%d]:Parent is working \n",getpid() );
}
printf("[%d]:end() \n",getpid() );
return 0;
}
如果把输出打印在终端上begin会打印一次(行缓存),如果打印在文件中begin会打印两次(全缓冲)fork也会拷贝缓冲区内容。
cpp
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
int main()
{
pid_t pid;
printf("[%d]:Begin() ",getpid() );
fflush(NULL);//需要刷新缓冲区 !!!非常重要
pid = fork();
if(pid <0)
{
perror("fork()");
exit(1);
}
else if(pid == 0)
{
//子进程
printf("[%d]:Child is working \n",getpid() );
}
else
{
//父进程
printf("[%d]:Parent is working \n",getpid() );
}
printf("[%d]:end() \n",getpid() );
return 0;
}
cpp
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#define LEFT 30000000
#define RIGHT 30000200
int main()
{
pid_t pid;
int i,j,mark;
for(i = LEFT;i< RIGHT;i++)
{
pid = fork();
if(pid <0)
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);
}
else
{
}
}
return 0;
}
fork采用写时拷贝技术,读的情况是不会改变的。谁改变的话,谁就拷贝一份。
vfork
vfork在fork时,指定同一个数据块,不会拷贝,已废弃。
三、进程的消亡与释放资源
wait,waitpid,wait3,wait4
#include <sys/types.h>
#include <sys/wait.h>
pid_t wait(int *wstatus);
pid_t waitpid(pid_t pid, int *wstatus, int options);
wait用状态接受,可以用宏进行接受判断。(死等)
waitpid,可以接受指定的pid。(options,可以设置不死等 WNOHANG)
wait,相当于wiatpid(-1,&status,0)
cpp
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#define LEFT 30000000
#define RIGHT 30000200
int main()
{
pid_t pid;
int i,j,mark;
for(i = LEFT;i< RIGHT;i++)
{
pid = fork();
if(pid <0)
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);
}
else
{
}
}
int st;
for(i = LEFT;i<RIGHT;i++)
// wait(&st);
wait(NULL); //收尸
return 0;
}
优化:上面程序使用了200个进程比较消耗资源,采用限制进程的方式对其进行改进。(1.分块法)(2.交叉分配法)(3.池类算法:上游负责仍数据,下游负责抢 )
cpp
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#define LEFT 30000000
#define RIGHT 30000200
#define N 3
int main()
{
pid_t pid;
int i,j,mark;
int n;
for(n = 0;n<N;n++)
{
pid = fork();
if(pid < 0)
{
exit(1);
}
if(pid ==0)
{
//子进程
for(i = LEFT+n;i< RIGHT;i+=N)
{
mark = 1;
for(j=2;j<i/2;j++)
{
if(i%j ==0)
{
mark = 0;
break;
}
}
if(mark)
printf("[%d:%d]%d is a primer \n",n,getpid(),i);
}
exit(0);
}
}
for(n=0;n<N;n++)
{
wait(NULL);
}
return 0;
}
四、exec函数族
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[]);
cpp
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
int main()
{
puts("Begin");
fflush(NULL); //不刷新的话缓冲区会直接被替换
execl("/bin/date","date","+%s",NULL);//出错会继续放下运行,成功就返回
perror("execl()");
exit(1);
puts("End");
return 0;
}
cpp
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
int main()
{
puts("Begin");
int pid;
fflush(NULL);
pid = fork();
if(pid <0)
{
perror("fork");
exit(1);
}
else if(pid == 0)
{
fflush(NULL);
execl("/bin/date","date","+%s",NULL);
perror("execl");
exit(1);
}
else
{
}
wait(NULL);
puts("End");
return 0;
}
cpp
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
int main()
{
puts("Begin");
int pid;
fflush(NULL);
pid = fork();
if(pid <0)
{
perror("fork");
exit(1);
}
else if(pid == 0)
{
fflush(NULL);
//execl("/usr/bin/sleep","sleep","100",NULL);
execl("/usr/bin/sleep","lxj","100",NULL);// ps -axf 可以看到名字
perror("execl");
exit(1);
}
else
{
}
wait(NULL);
puts("End");
return 0;
}
五、用户权限及组权限
-rwsr-xr-x 1 root root 68208 Feb 6 04:49 /usr/bin/passwd*
u+s:别的用户调用可执行文件时,会切换到当前的用户(user)执行。如:passwd命令(红色),sudo命令
g+s:别的用户调用可执行文件时,会切换到同组(group)用户的身份去执行(绿色)
real effective save(exec函数族会鉴定权限)
uid_t getuid(void);
uid_t geteuid(void);
pid_t getgid(void);
pid_t getegid(void);
getuid() returns the real user ID of the calling process.
geteuid() returns the effective user ID of the calling process.
getgid() returns the real group ID of the calling process.
getegid() returns the effective group ID of the calling process.
int setuid(uid_t uid);
int setgid(gid_t gid);
int setreuid(uid_t ruid, uid_t euid);
int setregid(gid_t rgid, gid_t egid);
mysu.c
cpp
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
int main(int argc,char*argv[])
{
puts("Begin");
if(argc <3)
{
printf("Usage...\n");
return 1;
}
pid_t pid;
pid = fork();
if(pid<0)
{
printf("fork\n");
exit(1);
}
else if(pid ==0)
{
setuid(atoi(argv[1]) );
printf("getuid = %d \n",getuid() );
execvp(argv[2],argv+2);
perror("execvp()");
exit(1);
}
else
{
}
wait(NULL);
puts("End");
return 0;
}
chown root mysu(添加root用户权限)
chmod u+s (改变权限为 u+s)
./mysu 0 cat /etc/shadow
六、观摩课:解释器文件
也成为脚本文件。sh后缀的文件 #! 开头。
bash
#!/bin/sh
ls
whoami
cat /etc/shadow
ps
修改/etc/passwd
56 john:x:1001:1001::/home/john:/usr/bin/top
56 john:x:1001:1001::/home/john:/usr/local/bin/mysh
七、system
The system() library function uses fork(2) to create a child process that executes the shell command specified in command using
execl(3) as follows:
execl("/bin/sh", "sh", "-c", command, (char *) NULL);
system() returns after the command has been completed.
cpp
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
int main()
{
system("date +%s > /tmp/out");
return 0;
}
八、进程会计
int acct(const char *filename);
进程消亡时,会将进程信息写入文件中。
man 2 acct
man 5 acct
九、进程时间
#include <sys/times.h>
clock_t times(struct tms *buf);
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 */
};
十、守护进程
守护进程又叫精灵进程。满足脱离控制终端,是一个会话(session,标识sid)的leader和一个进程组的leader
- Process ID(PID)
Linux中标识进程的一个数字,它的值是不确定的,是由系统分配的(但是有一个例外,启动阶段,kernel运行的第一个进程是init,它的PID是1,是所有进程的最原始的父进程),每个进程都有唯一PID,当进程退出运行之后,PID就会回收,可能之后创建的进程会分配这个PID - Parent Process ID(PPID)
字面意思,父进程的PID - Process Group ID(PGID)
PGID就是进程所属的Group的Leader的PID,如果PGID=PID,那么该进程是Group Leader - Session ID(SID)
和PGID非常相似,SID就是进程所属的Session Leader的PID,如果SID==PID,那么该进程是session leader
Session和Group都是管理多个进程的方式,同一个Group的进程属于同一个Session,一个Session里可以包含多个Group
ps -axj 查看tty为?代表脱离控制终端,PID,SID,PGID相同。 PPID为1
pid_t setsid(void);
int setpgid(pid_t pid, pid_t pgid);
pid_t getpgid(pid_t pid);
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 */
cpp
#include<stdlib.h>
#include<stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#define FILENAME "/tmp/out"
static int daemonize()
{
int pid;
int fd;
pid = fork();
if(pid < 0)
{
return -1;
}
if (pid == 0)
{
fd= open("/dev/null",O_RDWR);
if(fd <0)
{
return -1;
}
printf("child fd= %d \n",fd);
dup2(fd,0);//关闭0 ,让0 指向fd的文件表项, 即标准输出置为空设备
printf("child fd= %d \n",fd);
dup2(fd,1);
dup2(fd,2);
if(fd >2)
{
close(fd);
}
setsid(); //实现守护进程的函数
chdir("/"); //设置工作路径为根路径
// umask(0);
return 0;
}
else
{
return -1;
}
}
int main()
{
FILE*fp;
int count;
int ret = daemonize();
printf("ret = %d \n",ret);
if(ret != 0)
exit(1);
fp = fopen(FILENAME,"w");
if(fp == NULL)
{
perror("fopen()");
exit(1);
}
for(count = 0;;count++)
{
fprintf(fp,"%d \n",count);
fflush(fp);
sleep(1);
}
fclose(fp);
return 0;
}
单实例守护进程:锁文件 /var/run/name.pid。
启动脚本文件:/etc/rc.local。进行添加
十一、系统日志
/var/log目录下的文件。syslogd服务进行统一管理。
文件名是:syslog
#include <syslog.h>
void openlog(const char *ident, int option, int facility);
void syslog(int priority, const char *format, ...);
void closelog(void);
cpp
#include<stdlib.h>
#include<stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <syslog.h>
#include <errno.h>
#include <string.h>
#define FILENAME "/tmp/out"
static int daemonize()
{
int pid;
int fd;
pid = fork();
if(pid < 0)
{
return -1;
}
if (pid == 0)
{
fd= open("/dev/null",O_RDWR);
if(fd <0)
{
return -1;
}
printf("child fd= %d \n",fd);
dup2(fd,0);//关闭0 ,让0 指向fd的文件表项, 即标准输出置为空设备
printf("child fd= %d \n",fd);
dup2(fd,1);
dup2(fd,2);
syslog(LOG_INFO,"child fd = %d",fd);
if(fd >2)
{
close(fd);
}
setsid(); //实现守护进程的函数
chdir("/"); //设置工作路径为根路径
// umask(0);
return 0;
}
else
{
exit(1);
}
}
int main()
{
FILE*fp;
int count;
openlog("mydaemon",LOG_PID,LOG_DAEMON);
int ret = daemonize();
printf("ret = %d \n",ret);
if(ret != 0)
{
syslog(LOG_ERR,"daemonize() failed");
exit(1);
}
else
{
syslog(LOG_INFO,"daemonize() successed ret = %d",ret);
}
fp = fopen(FILENAME,"w");
if(fp == NULL)
{
syslog(LOG_ERR,"fopen():%s",strerror( errno));
exit(1);
}
syslog(LOG_INFO,"%s was opened",FILENAME);
for(count = 0;;count++)
{
fprintf(fp,"%d \n",count);
fflush(fp);
syslog(LOG_DEBUG,"%d is printed",count);
sleep(1);
}
fclose(fp);
closelog();
return 0;
}