一、进程控制
1、开机后bootloader将系统的第一个进程启动了起来,bootloader是烧制在bios上的
2、第一个例子:getpid
#include"func.h"
int main(int argc,char* argv[])
{
pid_t pid,ppid;
pid=getpid();
ppid=getppid();
printf("pid=%d,ppid=%d",pid,ppid);
return 0;
}
3、一个进程有4个身份:真实用户id、真实组id;有效用户id、有效组id
(1)真实用户id:getuid()
(2)真实组id:getgid()
(3)有效用户id:geteuid()
(4)有效组id:getegid()
4、第二个例子:gid和uid的例子、euid和egid的例子
#include"func.h"
int main()
{
pid_t uid,gid,euid,egid;
uid=getuid();
gid=getgid();
euid=geteuid();
egid=getegid();
printf("uid=%d,gid=%d,euid=%d,egid=%d",uid,gid,euid,egid);
return 0;
}
5、第三个例子modify_file.c
#include <func.h>
int main(int argc,char* argv[])
{
printf("uid=%d,gid=%d,euid=%d,egid=%d\n",getuid(),getgid(),geteuid(),getegid());
int fd=open("file",O_RDWR);
ERROR_CHECK(fd,-1,"open");
write(fd,"hello",5);
close(fd);
return 0;
}
6、s是权限提升
Chmod u+s a.out 该命令可以实现权限提升
权限提升:在权限控制中,使用权限提升后,也就是设置了该属性后,用户运行a.out时,a.out进程的有效用户身份将不再是运行a.out的用户,而是a.out文件的所有者。
7、红色的命令(可以实现权限提升):sudo、su、chmod u+s
命令chmod g+s a.out 对同组用户进行权限提升
命令chmod o+t a.out 增加粘滞键,增加一些权限检查
8、如何修改真实用户id?
接口setuid,该命令只有sudo权限才可以执行
sudo实现的原理,提升euid的权限
使用命令man setuid获取更多帮助
int setuid(uid_t uid);
int setgid(gid_t gid);
int seteuid(uid_t euid);
int setegid(gid_t egid);
9、在执行程序时,如果想让进城在后台执行,可以使用命令 ./a.out & 该命令会返回进程的PID
同一个程序可以启动多次,故还可以继续使用命令./a.out &
10、使用top查看动态进程后,进程状态的标志如下:
R 两次采样之间是不是有时间片消耗
S 睡眠
T 暂停
Z 僵尸进程
11、小技巧:
(1)使用命令ps -elf,可以查看进程状态
(2)通过who命令可以查看当前有几个用户正在登陆及当前登陆用户的详细信息
(3)命令which sudo ,可以查看sudo命令所在的路径
(4)在实际使用中,用户环境分为:生产环境和测试环境
(5)使用命令free可以查看磁盘当前使用状态
(6)查看某进程详细信息的命令ps -elf|grep [pid/进程名]
12、如何将其他用户踢出去?
(1)使用命令ps -elf|grep luke
(2)将某异常用户对应的进程杀掉,即可将该用户退出
(3)杀掉进程使用命令kill -9 [pid]
13、ps的常用命令
(1)ps -elf
(2)ps -aux
看CPU的使用量、没存使用量、stat状态
(3)top
显示前20条进程,动态的改变
(4)搜索进程使用命令ps -elf|grep a.out
(5)Ubuntu下,优先级有140个优先级(PRI)。范围是-40—99
(6)调度策略:
实时 SCHED_FIFO SCHED_RR -40-59 高优先级的先运行
普通调度策略 CFS SCHED_OTHER 60-99
14、如何改变进程优先级,使用命令renice -n +19
降低优先级,优先级号越低,优先级越高。因此如果优先级号变大,那么优先级降低
使用命令renice -n +40 -p -12234
杀死进程kill -9 [pid],或者使用命令pkill [进程名,例如a.out]
15、命令crontab,相当于windows下的定时任务,可以设置定时关机等功能
16、NFS是网络文件系统Network File System
17、在使用wait函数时,会提示缺少wait的定义,其实是缺少头文件,应该在func.h中增加两个头文件#include<sys/wait.h>
#include<sys/types.h>
18、在测试fork的程序中,是父进程输出快,还是子进程输出快?
答案是:父进程输出快,子进程被创建后,需要重新去申请时间片,故还要重新去调度队列末尾等待被调度。
19、代码fork_stack.c
#include"func.h"
int main(int argc,char*argv[])
{
pid_t pid;
int iStack=10;
pid=fork();
if(pid==0)
{
printf("I am child ,my pid =%d,ppid=%d ,iStack=%d, %p\n",getpid(),getppid(),iStack,&iStack);
}
else
{
printf("I am parent my pid=%d,mychildpid=%d,iStack=%d\n",getpid(),pid,iStack);
iStack=5;
printf("I am parent after modify my iStack%d,%p\n",iStack,&iStack);
sleep(1);
}
return 0;
}
20、代码例子fork_malloc.c
#include"func.h"
int main(int argc,char* argv[])
{
pid_t pid;
char *pMalloc=(char*)malloc(20);
strcpy(pMalloc,"hello");
pid=fork();
if(pid==0)
{
printf("I am child,mypid=%d,myppid=%d,%s,%p\n",getpid(),getppid(),pMalloc,pMalloc);
}
else
{
printf("I am parent mypid=%d,mychildpid=%d,%s\n",getpid(),pid,pMalloc);
pMalloc[0]='H';
printf("I am parent after modify %s,%p\n",pMalloc,pMalloc);
sleep(1);
}
return 0;
}
21、如何验证父进程和子进程的文件描述符是否一致,例子fork_open.c
#include <func.h>
int main(int argc,char* argv[])
{
pid_t pid;
int fd=open(argv[1],O_RDWR);
ERROR_CHECK(fd,-1,"open");
pid=fork();
if(0==pid)
{
printf("I am child mypid=%d,myppid=%d\n",getpid(),getppid());
write(fd,"niuniu",6);
}else{
printf("I am parent mypid=%d,mychildpid=%d\n",getpid(),pid);
lseek(fd,5,SEEK_SET);
printf("I am parent seek success\n");
sleep(1);
}
return 0;
}
根据这个例子,证明了父进程运行速度较快,而子进程运行因为需要重新去队列中排队等待处理机,所以慢了一点
22、验证代码例子fork_open2.c,可以提高文件读写速度(但是往往磁盘读写速度才是瓶颈)
#include <func.h>
int main(int argc,char* argv[])
{
pid_t pid;
int fd;
pid=fork();
if(0==pid)
{
fd=open(argv[1],O_RDWR);
ERROR_CHECK(fd,-1,"open");
printf("I am child mypid=%d,myppid=%d\n",getpid(),getppid());
write(fd,"niuniu",6);
}else{
fd=open(argv[1],O_RDWR);
ERROR_CHECK(fd,-1,"open");
printf("I am parent mypid=%d,mychildpid=%d\n",getpid(),pid);
lseek(fd,5,SEEK_SET);
printf("I am parent seek success\n");
sleep(1);
}
return 0;
}
23、多线程写文件,使用mmap,使多个线程或进程,对内存进行操作
24、execl是直接将磁盘上的代码,直接复制到当前进程的代码段。这时候程序就会按照新的代码去执行
25、代码execl.c和print.c文件,两个文件是调用关系
#include <func.h>
int main(int argc,char* argv[])
{
sleep(10);
execl("./print","niuniu","123","456",NULL);
printf("you can't see me\n");
return 0;
}
Execl("./print","niuniu","123","456",NULL);
会使用niuniu作为进程名
execl如果执行失败,是可以看到那句you can't see me
26、execl有6个兄弟,他们实现的功能都相似,只是写法不太一样
27、代码例子execv.c
(1)execv.c内容
#include <func.h>
int main(int argc,char* argv[])
{
sleep(10);
char *const args[]={"niuniu","123","456",NULL};
execv("./print",args);
printf("you can't see me\n");
return 0;
}
(2)print.c内容
#include <func.h>
int main(int argc,char* argv[])
{
int i;
for(i=0;i<argc;i++)
{
puts(argv[i]);
}
printf("I am print process\n");
while(1);
return 0;
}
28、孤儿进程:父进程先于子进程退出,这时子进程就变成了一个孤儿进程,该进程被init进程接管
29、子进程已经结束,但父进程还在忙碌,没有回收子进程的内容,这时候子进程就会编程一个僵尸进程;僵尸进程的状态代码是Z,僵尸进程的进程状态是不可以再改变的
30、如何避免子进程变成僵尸进程?父进程应该是用wait函数定期回收子进程
但是wait一次执行,只能回收一次
31、代码示例wait.c|下午第三个视频
wait的返回值是子进程的pid
WIFEXITED是一个宏操作
怎么看一个参数是宏还是函数?
32、代码例子waitpid.c|下午第三个视频
33、exit和_exit的区别
(1)exit会调用退出处理函数,清空I/O缓冲
(2)_exit不会调用退出处理函数,也不会清空I/O缓冲,会直接调用exit系统调用退出
34、abort函数
用户进程终止
代码例子abort.c
#include <func.h>
void print()
{
printf("I am print");
abort();
}
int main(int argc,char* argv[])
{
print();
printf("after print\n");
return 0;
}