您的位置:首页 > 运维架构 > Linux

Linux——IO与文件目录管理 进程 exec函数系列 fork 子进程

2014-09-11 11:55 507 查看

一.IO与文件目录管理

1.pread=lseek+read

pread读取以后不改变读写位置

2.mmap映射:

/proc/${pid}/mem 无法映射

3.IO的有效用户与实际用户

默认情况:实际用户与有效用户一直。

实际用户:执行用户

有效用户:权限用户

uid_t getuid()

uid_t geteuid()

4.目录相关函数

chdir 切换目录

mkdir 创建目录

rmdir 删除目录

unlink 删除文件

umask 设置文件权限屏蔽位

stat 文件目录状态

5.目录的遍历

opendir系列函数

readdir

closedir

seekdir

dirfd

int scandir(const char*dirname,//目录名

struct dirent***namelist,//返回目录列表

int (*)(struct dirent*),//回调函数,过滤目录

//NULL:不过滤

int (*)(struct dirent*,struct dirent*)//排序返回目录

//NULL:不排序

);

返回:

>=0 目录个数

=-1 目录查找失败

二.进程

1.什么是进程

执行的程序:代码->资源->CPU

进程有很多数据维护:进程状态/进程的属性

所有进程属性采用的结构体维护->树性数据结构

ps 察看进程常见属性

top 察看系统进程执行状况

pstree(ptree)

kill 向进程发送信号

kill -s 信号 进程id

kill -l 显示进程能接受的所有信号

知道进程有很多属性:ps可以察看的属性

2.创建进程

1.代码?加载到内存?分配CPU时间片?

代码由独立的程序存在.

2.进程有关的创建函数

int system(const char*filename);

建立独立进程,拥有独立的代码空间,内存空间

等待新的进程执行完毕,system才返回.(阻塞)

案例:

使用system调用一个程序。

观察进程ID。

观察阻塞。

新的返回值与system返回值有关系。

任何进程的返回值:不要超过255。一个字节。

system的返回值中8-15位存放返回码

test.c

<span style="font-size:18px;">
#include <stdio.h>
#include <unistd.h>
int main()
{
printf("%d\n",getpid());
sleep(5);
return 99;
}</span>
gcc test.c -otest

system.c

<span style="font-size:18px;">
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/wait.h>
main()
{
int r;
printf("%d\n",getpid());
r=system("./test");
printf("%d\n",r);//返回25344 为什么不是99?
        //printf("%d\n",r>>8&255);//99
        //printf("%d\n",WEXITSTATUS(r));//99  该宏的作用就是上一行的结果
}
</span>


练习:

使用system调用"ls -l"。"ls -l home" 把上面system函数里面改成"ls -l",可见system函数可以调用shell指令

popen:创建子进程

在父子进程之间建立一个管道 可以看到重定向如何实现

案例:

使用popen调用ls -l,并且建立一个管道读取输出

popen.c

<span style="font-size:18px;">
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/wait.h>
main()
{
char buf[1024];
FILE *f=popen("ls -l","r");
int fd=fileno(f);

int r;
printf("=============\n");

while((r=read(fd,buf,1024))>0)
{
buf[r]=0;
printf("::%s\n",buf);
}
printf("=============\n");

close(fd);
pclose(f);
}
</span>


exec系列函数:execl execlp

作用是替换当前进程的代码空间中的代码数据

函数本身不创建新的进程。

int execl(const char*path,const char *arg,....);

第一个参数:替换的程序,

第二个参数....:命令行

命令行格式:命令名 选项参数

命令行结尾必须空字符串结尾

案例:

使用exec执行一个程序。

体会:1 是否创建新的进程?没有

2 体会execl的参数的命令行的格式

3 体会execl与execlp的区别(execl只当前路径)

execlp 使用系统的搜索路径

4 体会execl替换当前进程的代码

test.c

<span style="font-size:18px;">
#include <stdio.h>
#include <unistd.h>
int main()
{
printf("%d\n",getpid());
sleep(5);
return 99;
}</span>
gcc test.c -otest

exec.c

<span style="font-size:18px;">
include <stdio.h>
#include <unistd.h>
int main()
{
printf("main:%d\n",getpid());//没有创建新的进程
int r=execl("test","ls","-l",NULL);//
printf("结束%d\n",r);//execl函数用test替换当前代码 所以该行没有被执行
return 0;
}
</span>


fork pid_t fork();

//1.创建进程

//2.新进程的代码是什么:克隆父进程的代码而且克隆了执行的位置所以子进程也执行到了fork位置之后.

//3.在子进程不调用fork所以返回值=0;

//4.父子进程同时执行.

fork.c

<span style="font-size:18px;">
#include <stdio.h>
#include <unistd.h>
int main()
{
int pid;

printf("创建进程前!\n");
pid=fork();
if(pid==0)
{
while(1)
{
printf("子进程\n");
sleep(1);
}
}
else
{
while(1)
{
printf("父进程\n");
sleep(1);
}
}
return 0;
}

</span>


3.应用进程

使用fork创建新的进程有什么应用价值呢?

使用fork实现多任务.(Unix系统本身是不支持线程)

1.进程

2.线程

3.信号

4.异步

5.进程池与线程池

案例:

使用进程创建实现多任务

1.UI

2.建立多任务框架

3.分别处理不同的任务

demo.c

<span style="font-size:18px;">#include <curses.h>
#include <unistd.h>
#include <time.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <math.h>

WINDOW *wtime,*wnumb;
main()
{
initscr();
wtime=derwin(stdscr,3,10,0,(COLS-10));
wnumb=derwin(stdscr,3,11,(LINES-3)/2,(COLS-11)/2);
box(wtime,0,0);
box(wnumb,0,0);
refresh();
wrefresh(wtime);
wrefresh(wnumb);
if(fork())
{//show time
time_t tt;
struct tm *t;
while(1)
{
time(&tt);
t=localtime(&tt);
mvwprintw(wtime,1,1,"%02d:%02d:%02d",
t->tm_hour,t->tm_min,t->tm_sec);
refresh();
wrefresh(wtime);
//wrefresh(wnumb);
sleep(1);
}
}
else
{//show number
int num=0;
int i;
while(1)
{
num=0;
for(i=0;i<7;i++)
{
num=num*10+rand()%10;
}
mvwprintw(wnumb,1,2,"%07d",num);
refresh();
//wrefresh(wtime);
wrefresh(wnumb);
usleep(100000);
}
}
endwin();
}
</span>
gcc demo1.c -lcurses -omain

4.理解进程

1.父子进程的关系

独立的两个进程

互为父子关系 pstree命令

hah.c

<span style="font-size:18px;">#include <stdio.h>
#include <unistd.h>
main()
{
if(fork()){
while(1){
printf("parent:%d\n",getpid());//3650
sleep(1);
}
}
else{
while(1){
printf("child:%d\n",getpid());//3651
sleep(1);
}
}
}
</span>


2.问题:

2.1.父进程先结束子进程怎么办?

子进程就依托根进程init:孤儿进程

孤儿进程没有任何危害.

2.2.子进程先结束?

子进程会成为僵死进程.

僵死进程不占用内存,CPU.但在进程任务管理树占用一个节点.

僵死进程造成进程名额资源浪费.

所以处理僵死进程.

parentchild.c

<span style="font-size:18px;">#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <signal.h>
main()
{
if(fork())
{
//parent
printf("parent!\n");
sleep(20);
printf("退出!\n");
}
else
{
//child
sleep(20000);
printf("child!\n");
}
}
</span>


3.僵死进程使用wait回收

在父进程中使用 执行到wait时会阻塞,直到子进程结束,然后会回收子进程,父进程才继续执行

4.父进程怎么知道子进程退出?

子进程结束通常会向父进程发送一个信号

SIGCHLD 17

parentchild1.c

<span style="font-size:18px;">#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <signal.h>
void deal(int s)
{
int status;
wait(&status);
printf("回收中.....\n");
sleep(5);
printf("回收完毕:%d!\n",WEXITSTATUS(status));

}
main()
{
if(fork()==0)
{
printf("child!\n");
sleep(6);
printf("退出!\n");
exit(88);
}
else
{

signal(17,deal);
while(1){
printf("parent!\n");
sleep(1);
}
}
}
</span>


5.父进程处理子进程退出信号

signal(int sig,void(*fun)(int));

向系统注册:只要sig信号发生,系统停止进程,并调用函数fun

当函数执行完毕,继续原来进程 称为中断

5.1.实现处理函数

5.2.使用signal邦定信号与函数

僵死进程回收案例:

6.父子进程的资源访问

6.1.内存资源

6.2.文件资源

案例:

说明:子进程克隆整个内存区域,(虚拟地址一样但是指向不同的物理空间)

但内存区域指向不同的物理空间

尽管克隆,但内存独立. 不能相互访问.

promem.c

<span style="font-size:18px;">#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <signal.h>
#include <sys/mman.h>
main()
{
//int *a=mmap(0,4,PROT_READ|PROT_WRITE,
//		MAP_ANONYMOUS|MAP_SHARED,0,0);//这种情况是映射到同一块物理空间
        //                                             输出90
        //int *a=sbrk(4);//输出40

        int a=40;
if(fork())
{
printf("parent:%d\n",*a);//40
*a=90;
}
else
{
printf("child:%d\n",*a);//40
sleep(3);
printf("child:%d\n",*a);//40
}

}
</span>


多进程实现多任务,进程之间的数据交换是大问题.(IPC)

Inter-Process Commucation

映射内存:

MAP_SHARED:映射到同一物理内存

MAP_PRIVATE:映射到不同的物理内存.

案例:

两个进程之间,文件描述符号指向的是同一个文件内核对象.

procfile1.c

<span style="font-size:18px;">#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <signal.h>
#include <sys/mman.h>
#include <fcntl.h>
main()
{//两个文件描述符指向同一个文件 一个写入时影响另一个指针的位置

int fd=open("test.txt",O_RDWR);

if(fork())
{
printf("%d\n",fd);//跟下面的输出一样
sleep(5);
write(fd,"Killer",6);
close(fd);
}
else
{
printf("%d\n",fd);
write(fd,"Clinton",7);
sleep(8);
close(fd);
}
}
</span>

procfile2.c

<span style="font-size:18px;">#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <signal.h>
#include <sys/mman.h>
#include <fcntl.h>
main()
{

if(fork())
{
int fd=open("test.txt",O_RDWR);//读写指针不影响下面指针
printf("%d\n",fd);//3
sleep(5);
write(fd,"Killer",6);
close(fd);
}
else
{
int fd=open("test.txt",O_RDWR);
printf("%d\n",fd);//3
write(fd,"Clinton",7);
sleep(8);
close(fd);
}
}
</span>


结论:

进程的数据交换,基于两种方式:

内存:有序/无序:mmap

文件:有序/无序:普通文件

基于内核对象:文件.内存.队列

回顾:

1.目录遍历

2.进程创建system popen exec fork

3.僵死进程出现的条件以及回收

4.利用多进程实现简单的多任务

5.理解进程的克隆.

作业:

1.使用两个进程,查找素数:(多任务)

A进程查找1-5000

B进程查找5001-10000

把素数写入文件.

2.写一个多任务:(两个进程数据共享)

A.进程查找素数,放入mmap分配的空间

B.进程把mmap的数据取出来,判定两个数据是否相邻.

相邻就打印这两个素数.

思考:

3.使用opendir/readir遍历指定目录下的所有*.c文件.

scandir

接下来会讲:

一.进程的基本控制

二.进程的高级控制-信号
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: