Linux学习第八天

一、昨日Review

1、如何区分两个进程?使用pid

2、如何获取pid和父亲的pid?使用getpid和getppid

3、getuid/getgid/geteuid/getigid

4、内核判断进程能做什么事情时,进程检查的是有效用户id

5、什么是孤儿进程?父进程先于子进程结束,子进程被1号进程接管

6、什么是僵尸进程?子进程先退出,但父进程没有对子进程做清理工作,则已退出的子进程就变成了僵尸进程。

7、进程终止的5种方式?main函数的自然返回、调用exit函数、调用_exit函数、调用abort函数、接收到能导致进程终止的信号ctrl+c SIGINT或者ctrl+\SIGQUIT

 

 

二、今日内容

1、什么是守护进程?

(1)守护进程就是daemon进程,是没有控制终端与之相连的进程,它独立执行某种任务。

(2)为什么要脱离终端控制?因为终端被关闭时,所有该终端启动的进程都会被自动关闭,守护进程可以突破这种现实,它被执行开始运转,直到整个系统关闭时才退出。

(3)一般在企业中,服务器应用都是作为守护进程来运行的。

(4)如何判断一个进程是不是守护进程?在使用ps命令查询时,凡是进程后缀以D结尾的,都是守护进程

(5)daemon进程,为了使当前目录所在的文件不能卸载,所以利用chdir("/"),把当前工作目录切换到根目录。

(6)因为守护进程不需要标注输入、标准输出、标准错误输出,所以守护进程需要关闭三个端口,使用代码

 for(i=0;i<MAXFILE;i++)

close(i);

(7)代码例子daemon.c

 

#include <func.h>

void Daemon()

{

    if(fork())

    {

        exit(0);

    }

    setsid();         //重设会话组

    chdir("/");     //切换到根目录

    umask(0);

    int i;

    for(i=0;i<3;i++)

    {

        close(i);

    }

}

int main(int argc,char* argv[])

{

    Daemon();

    while(1);          //使用了while1)后,如果使用ctrl+c结束进程,实际上进程是不会终止的,进程仍然在后台继续运行

    return 0;

}

 

 

2、

①获取进程组id是getpgid

②创建进程组id是setpgid

③获取会话getsid

④进程组长的pid就是pgid号

 

3、代码例子getpgid.c

 

#include <func.h>

int main(int argc,char* argv[])

{

    if(!fork())

    {

        printf("I am child pid=%d ppid=%d,pgid=%d\n",getpid(),getppid(),getpgid(0));

        setpgid(0,0);//成立新的进程组

        printf("I am child pid=%d ppid=%d,pgid=%d\n",getpid(),getppid(),getpgid(0));

        while(1);

        return 0;

    }else{

        printf("I am parent pid=%d ppid=%d,pgid=%d\n",getpid(),getppid(),getpgid(0));

        wait(NULL);

        return 0;

    }

}

 

 

经过本例,可以验证组id就是组长的pid

 

 

4、Linux中如何获取时间

 (1)一些命令

①time_t 秒数

②ctime 字符串时间

③gmtime 格林尼治

 

(2)代码例子gemtime.c

 

#include <func.h>

 

int main(int argc,char* argv[])

{

    time_t now;

    time(&now);

    struct tm *p;

    p=gmtime(&now);

    printf("%04d-%02d-%02d %02d:%02d:%02d %d %d\n",p->tm_year+1900,p->tm_mon+1,p->tm_mday,\

           p->tm_hour+8,p->tm_min,p->tm_sec,p->tm_wday,p->tm_yday);

    return 0;

}

 

 

 

5、管道

(1)管道的分类:标准流管道、无名管道、命名管道

(2)标准流管道实际是使用无名管道实现的

(3)管道主要使用函数popen和pclose

(4)管道关闭要使用pclose

 

 

6、文件夹popen中

(1)代码例子print.c

 

#include <func.h>

int main(int argc,char* argv[])

{

    printf("I am print\n");

    return 0;

}

 

 

(2)代码例子popen_r.c

 

#include <func.h>

int main(int argc,char* argv[])

{

    FILE *fp;

    fp=popen("ls","r");

    ERROR_CHECK(fp,NULL,"popen");

    char buf[128];

    fread(buf,sizeof(char),sizeof(buf)-1,fp);

    printf("buf=%s\n",buf);

    pclose(fp);

    return 0;

}

 

 

(3)代码例子add.c

 

#include <func.h>

int main(int argc,char* argv[])

{

    int i,j;

    scanf("%d%d",&i,&j);

    printf("sum=%d\n",i+j);

    return 0;

}

 

 

(4)代码例子popen_w.c

 

#include <func.h>

int main(int argc,char* argv[])

{

    FILE *fp;

    fp=popen("./add","w");

    ERROR_CHECK(fp,NULL,"popen");

    char buf[128]="3 4";

    fputs(buf,fp);

    pclose(fp);

    return 0;

}

 

 

(5)编译文件时,如果想要生成的文件叫做其他名字,可以使用命令gcc -o [想要生成的文件名] [.c文件名]

例如:gcc -o test add.c

13、文件夹pipe中

代码例子pipe.c

 

#include <func.h>

 

int main(int argc,char* argv[])

{

    int fds[2];

    pipe(fds);//管道的读端就存在fds[0],写端存在fds[1]

    if(!fork())

    {

        close(fds[1]);//关闭写端因为子进程要读

        char buf[128]={0};

        read(fds[0],buf,sizeof(buf));

        printf("I am child,gets=%s\n",buf);

        exit(0);

    }else{

        close(fds[0]);//关闭读端,因为父进程要写

        write(fds[1],"I hen niu",9);

        wait(NULL);

    }

    return 0;

}

 

 

因为管道是半双工,所以进程间使用管道实现双向通信,需要使用两个管道

 

 

7、代码示例unlink.c

 

#include <func.h>

int main(int argc,char* argv[])

{

    ARGS_CHECK(argc,2);

    int ret;

    ret=unlink(argv[1]);

    ERROR_CHECK(ret,-1,"unlink");

    return 0;

}

 

①删除fifo文件使用unlink命令

 

 

8、实例代码ftok.c

#include <func.h>

 

int main(int argc,char* argv[])

{

    ARGS_CHECK(argc,2);

    key_t key;

    key=ftok(argv[1],1);

    printf("key=%d\n",key);

    return 0;

}

 

9、创建一个父子进程,让他们通过共享内存,直接通信

代码例子fork_shm.c

#include <func.h>

 

int main(int argc,char* argv[])

{

    int shmid;

    shmid=shmget(1000,1<<20,IPC_CREAT|0600);

    ERROR_CHECK(shmid,-1,"shmget");

    int *p;

    p=(int*)shmat(shmid,NULL,0);

    ERROR_CHECK(p,(int*)-1,"shmat");

    if(!fork())

    {

        printf("I am child %d,%p\n",*p,p);

    }else{

        p[0]=10;

        printf("I am parent %d,%p\n",*p,p);

        wait(NULL);

    }

    return 0;

}

 

①怎么看共享内存?使用命令ipcs

 

10、创建一个父子进程,让父子进程同时对数据进行加法,各加1000万,那结果是2000万吗?

 

代码例子add1000.c

 

#include <func.h>

#define N 10000000

int main(int argc,char* argv[])

{

    int shmid;

    shmid=shmget(1000,1<<20,IPC_CREAT|0600);

    ERROR_CHECK(shmid,-1,"shmget");

    int *p;

    p=(int*)shmat(shmid,NULL,0);

    ERROR_CHECK(p,(int*)-1,"shmat");

    *p=0;

    if(!fork())

    {

        int i;

        for(i=0;i<N;i++)

        {

            p[0]=p[0]+1;

        }

    }else{

        int i;

        for(i=0;i<N;i++)

        {

            p[0]=p[0]+1;

        }

        wait(NULL);

        printf("result=%d\n",p[0]);

    }

    return 0;

}

 

 

(1)Wait(NULL),父进程必须回收子进程的资源,所以在执行到这一句时,父进程和子进程都已经执行完毕了

 

(2)

①为什么最终的结果不等于2000万?这是遇到的第一个并发问题

②如何解决并发问题?使用负载均衡,降低并发量。

③为什么会不等于2000万?改成双核后,并发度更高,所以值会更小

④如何让他等于2000万?需要加保护

⑤资源保护的方法有?让大家轮流操作。

⑥资源保护需要用到信号量

 

11、解除链接使用函数shmdt,将共享内存段与进程空间分离。

代码示例shmdt.c,就可以解除链接

#include <func.h>

 

int main(int argc,char* argv[])

{

    int shmid;

    shmid=shmget(1000,1<<20,IPC_CREAT|0600);

    ERROR_CHECK(shmid,-1,"shmget");

    char *p;

    p=(char*)shmat(shmid,NULL,0);

    ERROR_CHECK(p,(char*)-1,"shmat");

    strcpy(p,"hello");

    int ret;

    sleep(10);

    ret=shmdt(p);

    ERROR_CHECK(ret,-1,"shmdt");

    while(1);

    return 0;

 

12、函数shmctl是共享内存的控制函数,可以用来删除共享内存段

 

(1)加入有人正在使用共享内存,可以删除吗?不可以

(2)代码例子 shmctl_rmid.c

 

#include <func.h>

 

int main(int argc,char* argv[])

{

    int shmid;

    shmid=shmget(1000,1<<20,IPC_CREAT|0600);

    ERROR_CHECK(shmid,-1,"shmget");

    int ret;

    ret=shmctl(shmid,IPC_RMID,NULL);

    ERROR_CHECK(ret,-1,"shmctl");

    return 0;

}

 

 

(3)代码例子shmctl_stat.c

 

#include <func.h>

 

int main(int argc,char* argv[])

{

    int shmid;

    shmid=shmget(1000,1<<20,IPC_CREAT|0600);

    ERROR_CHECK(shmid,-1,"shmget");

    int ret;

    struct shmid_ds buf;

    ret=shmctl(shmid,IPC_STAT,&buf);

    ERROR_CHECK(ret,-1,"shmctl");

    printf("%d %o %ld %ld\n",buf.shm_perm.uid,buf.shm_perm.mode,buf.shm_segsz,buf.shm_nattch);

    return 0;

}

 

 

(4)代码例子shmctl_set.c

 

#include <func.h>

 

int main(int argc,char* argv[])

{

    int shmid;

    shmid=shmget(1000,1<<20,IPC_CREAT|0600);

    ERROR_CHECK(shmid,-1,"shmget");

    int ret;

    struct shmid_ds buf;

    ret=shmctl(shmid,IPC_STAT,&buf);

    ERROR_CHECK(ret,-1,"shmctl");

    printf("%d %o %ld %ld\n",buf.shm_perm.uid,buf.shm_perm.mode,buf.shm_segsz,buf.shm_nattch);

    buf.shm_perm.mode=0666;

    shmctl(shmid,IPC_SET,&buf);

    return 0;

}

 

①命令ipcs查看共享内存

②命令ipcrm -m shmid删除共享内存

 

 

13、虚拟地址和物理地址的转换

 

(1)问题:

①不同进程相同的虚拟地址,是否可以映射不同的物理地址?

可以,每个进程都有自己的段基址寄存器。

②不同进程相同的虚拟地址,是否可以映射到相同的物理地址?

可以,共享内存

③不同进程不同的虚拟地址,是否可以映射到相同的物理地址?

可以,通过mmap可以实现共享没存

 

(2)转换过程

 

(3)新的用法

①怎样提高虚拟地址->物理地址的速度?使用快表,但是快表中存放的都是高频数据,因为快表的容量有限

②怎么降低TLB miss?使用大页

③什么地方使用大页hugepages?大页=2M;普通页=4K

④用K值创建共共享内存,再让其他人使用K值调用共享内存,这种设计有什么好处?便于分享

(4)私有方式创建共享内存

代码例子shmget_rpivate.c
 

#include <func.h>

#define SHM_HUGE_2MB    (1<<21)

 

int main(int argc,char* argv[])

{

    int shmid;

    shmid=shmget(1000,1<<21,IPC_CREAT|0600|SHM_HUGETLB|SHM_HUGE_2MB);

    ERROR_CHECK(shmid,-1,"shmget");

    return 0;

}

 

(5)怎么使用大页?

代码例子shmget_hugepage.c

 

#include <func.h>

#define SHM_HUGE_2MB    (1<<21)

 

int main(int argc,char* argv[])

{

    int shmid;

    shmid=shmget(1000,1<<21,IPC_CREAT|0600|SHM_HUGETLB|SHM_HUGE_2MB);

    ERROR_CHECK(shmid,-1,"shmget");

    return 0;

}

 

(6)小问题:

①proc是什么?/proc是内存文件系统,是存在内存中的内容,一旦关机,内容就被清空。里边每个文件夹都是一个进程,文件夹名就是一个pid

②如何查看内核版本?使用命令uname -a

③sudo只是权限提升, 和直接使用su还是有区别的

④使用命令cat /proc/meminfo,可以查看当前大页的使用情况

⑤SWAP分区是物理内存,名字叫做虚拟内存。

 

(7)使用mmap实现共享内存

代码示例mmap_sharememory.c

 

#include <func.h>

 

int main(int argc,char* argv[])

{

    ARGS_CHECK(argc,2);

    int fd=open(argv[1],O_RDWR);

    ERROR_CHECK(fd,-1,"open");

    char *p=(char*)mmap(NULL,10,PROT_READ|PROT_WRITE,MAP_SHARED,fd,0);

    ERROR_CHECK(p,(char*)-1,"mmap");

    if(!fork())

    {

        printf("I am child %s\n",p);

    }else{

        strcpy(p,"hello");

        wait(NULL);

    }

    return 0;

}

 

 

(8)MS_ASYNC和MS_SYNC,用于将内存中的数据同步到磁盘中

MS_ASYNC:

MS_SYNC:

 

(9)mmap实现大页

代码例子mmap_hugepage.c

 

#include <func.h>

//要把文件系统挂载为大页形式的文件系统

#define MAP_HUGE_2MB 1<<21

int main(int argc,char* argv[])

{

    ARGS_CHECK(argc,2);

    int fd=open(argv[1],O_RDWR|O_CREAT,0666);

    ERROR_CHECK(fd,-1,"open");

    int ret;

    char *p=(char*)mmap(NULL,MAP_HUGE_2MB,PROT_READ|PROT_WRITE,MAP_SHARED,fd,0);

    ERROR_CHECK(p,(char*)-1,"mmap");

    strcpy(p,"hello");

    sleep(20);

    ret=munmap(p,MAP_HUGE_2MB);

    ERROR_CHECK(ret,-1,"munmap");

    return 0;

}

 

 

①任何目录都必须挂载一下,使用命令

mount none /mnt/huge -t hugetlbfs

 

来自 <https://www.ibm.com/developerworks/cn/linux/l-cn-hugetlb/index.html>

②在执行mmap_hugepage时,要先把系统挂载成大页形式,使用上面的命令

③怎么更改大页数量?Echo 20 > /proc/sys/vm/nr_hugepages

④SWAP分区不可以设置太小

⑤使用命令mlock可以将内存锁住,这样程序就不会被交换到SWAP空间了

 

14、使用信号量,实现1000万+1000万=2000万

 

(1)使用信号量,需要往头文件里增加#include<sys/sem.h>

(2)实例代码semget.c

 

#include <func.h>

 

int main(int argc,char* argv[])

{

    int semArrId=semget(1000,1,IPC_CREAT|0600);

    ERROR_CHECK(semArrId,-1,"semget");                                                                                   

    return 0;

}

 

 

(3)semget里可以创建多把锁

(4)第二段代码在sem_add1000.c文件中,再增加锁的相关操作

(5)锁应该加到for循环里边

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值