linux system


原文链接: linux system

@@@@cpu如何操作内存?
①通过地址总线告诉内存控制器。在什么地方操作数据
②控制总线,发送读/写的信号
③数据总线,传送数据


#makefile文件
.PHONY:clean all
CC=gcc
CFLAGS=-Wall -g
BIN=dm01_basefork

all:$(BIN)

%.o:%.c

$(CC) $(CFLAGS) -c $< -o $@

clean:

rm -f *.o $(BIN)

@@@@父进程与子进程有各自独立的进程地址空间
在创建子进程的时候Linux采用“写时复制”
例子如下:

#include
#include
#include "errno.h"

#include "stdio.h"
#include "stdlib.h"
#include "string.h"

//fork进程
//变量的引申 每一个进程有独立的进程地址空间,做深入理解

//写时复制理解
//1 子进程修改变量的时候copy
//2 只拷贝页 ========》linux内核的内存管理 。。。基本的概念建立

int main(void )
{

int num = 10;

printf("hello...\n");

pid_t   pid;
printf("befor fork pid:%d\n", getpid());

//fork 子进程
//On  success,  the  PID  of the child process
pid = fork();
if (pid == -1)
{
    //On failure, a -1 will be return
    //errno 
    perror("fork err");
    return 0;
}

if (pid > 0) //大于0是父进程
{
    printf("parent :%d\n", getpid());
    num ++;
    printf("parent :num:%d\n", num); //11

}
else if (pid == 0)
{
    printf("child :%d\n", getpid());
    num ++; //11,不是12
    printf("child num:%d\n", num); 
}

printf("after fork \n");

return 0;

}

@@@@压力测试模型如下
例子如下:
#include
#include
#include "errno.h"

#include "stdio.h"
#include "stdlib.h"
#include "string.h"

void LoopFunc(int num)
{

printf("LoopFunc() %d....\n", num);

}

int main(void )
{

int     i , j = 0;

printf("hello...\n");

int     procNum = 0;
int     loopNum = 0; 
printf("\n请输入要运行的进程数");
scanf("%d", &procNum);

printf("\n请输入每个进程运行圈数");
scanf("%d", &loopNum);


pid_t   pid;
printf("befor fork pid:%d\n", getpid());

//fork 子进程

for (i=0; i<procNum; i++)
{

    pid = fork();
    if (pid == -1)
    {
        //On failure, a -1 will be retur
        //errno 
        perror("fork err");
        return 0;
    }

    if (pid == 0)
    {
        for (j=0; j<loopNum; j++)
        {
            LoopFunc(j);
        }
        exit(0); //让子进程跑圈 子进程不参与下一次的fork
    }
    if (pid > 0) //大于0是父进程
    {
        ;   
    }
}


printf("after fork \n");

return 0;

}

@@@@多次fork模型
//打印8次
int main(void )
{

fork(); //fork之后有两个分支

fork(); //2个分支分别调用fork,产生4个分支

fork(); //4个分支分别调用fork系统调用,产生8个分支

printf("hello....\n");

}

@@@@信号:
#include
typedef void (*sighandler_t)(int);
sighandler_t signal(*signal(int signo,sighandler_t handler));

Linux信号填写有三种方式:
①自定义回调函数
②填SIG_IGN,signal(SIGCHILD,SIG_IGN),
表示子进程死后,父进程忽略,交给init
③填SIG_DFL

#include
#include
#include "errno.h"

#include "stdio.h"
#include "stdlib.h"
#include "string.h"
#include

/*
孤儿进程

parent先死,子进程托孤给1号进程

僵死进程

孩子先死,parent没来得急收尸

*/

//方式,创建子进程的时候,可以不管子进程,让操作系统linux内核去管

//信号:异步处理事件,是一种机制。
//言外之意是说:我的程序在顺序的同时,能支持异步的调用信号处理函数。
//======>

int main(void )
{

printf("hello...\n");

pid_t   pid;
printf("befor fork pid:%d\n", getpid());

//避免僵尸进程
//通过信号注册函数,忽略子进程死信号。交给init管
signal(SIGCHLD, SIG_IGN); 

//fork 子进程  
pid = fork();
if (pid == -1)
{

    perror("fork err");
    return 0;
}

if (pid > 0) //大于0是父进程
{
    printf("parent :%d\n", getpid());
    sleep(100);

}
else if (pid == 0)
{
    printf("child :%d\n", getpid());

}

printf("after fork \n");

return 0;

}

@@@@共享文件描述符
#include
#include
#include "errno.h"
#include

  #include <sys/types.h>
   #include <sys/stat.h>
   #include <fcntl.h>

#include "stdio.h"
#include "stdlib.h"
#include "string.h"
#include

//演示 父子进程共享文件描述符情况
//相当于:两个fd指向同一块内存空间 ,而已。。。。
//因为 两个进程共享了文件指针偏移量,所以都能向文件中有序写数据。。。。

int main(void )
{

printf("hello...\n");

pid_t   pid;
printf("befor fork pid:%d\n", getpid());

int fd;
fd = open("test.txt", O_WRONLY);
if (fd == -1)
{
    perror("open err");
    exit(0);
}


//避免僵尸进程
//通过信号注册函数,忽略子进程死信号。。。。
signal(SIGCHLD, SIG_IGN); 

//fork 子进程  
pid = fork();
if (pid == -1)
{

    perror("fork err");
    return 0;
}

if (pid > 0) //大于0是父进程
{


   //ssize_t write(int fd, const void *buf, size_t count);
    write(fd, "parent", 6);
    printf("parent :%d\n", getpid());
    close(fd); //关闭两次。。。。。


}
else if (pid == 0)
{
    write(fd, "child", 5);
    printf("child :%d\n", getpid());
        close(fd);  //关闭两次。。。。。

}

sleep(2);
printf("after fork \n");

return 0;

}

@@@@exec函数族
//execlp自动搜索路径
execlp("ls","ls","-lt",NULL,NULL);

int main(void )
{

pid_t   pid;
printf("befor fork pid:%d\n", getpid());


//避免僵尸进程
//通过信号注册函数,忽略子进程死信号。。。。
signal(SIGCHLD, SIG_IGN); 

//vfork 子进程 
pid = vfork();
if (pid == -1)
{

    perror("fork err");
    return 0;
}

if (pid > 0) //大于0是父进程
{
    printf("parent :%d\n", getpid());   
}
else if (pid == 0)
{

    printf("child :%d\n", getpid());
    char *args[] = {"aa=111","bb=222",NULL};

    execle("./hello","hello",NULL,args);
    printf("没有执行成功");
    exit(0);
}

sleep(2);
printf("after fork \n");

return 0;

}

@@@@execve
int main()
{

char *ars[] = {"aa=111","bb=222",NULL};
char *const argv[] = {"hello",NULL};
execve("./hello",argv,ars);

}

@@@@主函数优雅的退出
方法①:
int main()
{

int Pid;
Pid = fork();
if(Pid == 0)
    ...

int mypid;
//主进程不挂起等待
while ((mypid = waitpid(-1, NULL, WNOHANG)) > 0)
{
    printf("死掉的process id:%d",mypid);
}

return 0;

}

方法②:
int main()
{

int Pid;
Pid = fork();
if(Pid == 0)
    ...

while(1)
{
    ret = wait(NULL);
    if(ret == -1)
    {
        if(error == EINTR)//父进程阻塞过程中有可能被信号中断。需要做异常处理
        {
            continue;
        }
        break;
    }
}
return 0;

}

@@@@waitpid
#include
#include
#include
#include

int
main(int argc, char *argv[])
{
pid_t cpid, w;
int status;

cpid = fork();
if (cpid == -1)

{ 
    perror("fork"); exit(EXIT_FAILURE); 
}

if (cpid == 0)

{
    /* Code executed by child */
   printf("Child PID is %ld\n", (long) getpid());
   if (argc == 1)
       pause();                    /* Wait for signals */
   _exit(atoi(argv[1]));

} else 
{                    /* Code executed by parent */
   do {
       w = waitpid(cpid, &status, WUNTRACED | WCONTINUED);
       if (w == -1) { perror("waitpid"); exit(EXIT_FAILURE); }

       if (WIFEXITED(status)) {
           printf("exited, status=%d\n", WEXITSTATUS(status));
       } else if (WIFSIGNALED(status)) {
           printf("killed by signal %d\n", WTERMSIG(status));
       } else if (WIFSTOPPED(status)) {
           printf("stopped by signal %d\n", WSTOPSIG(status));
       } else if (WIFCONTINUED(status)) {
           printf("continued\n");
       }
   } while (!WIFEXITED(status) && !WIFSIGNALED(status));
   exit(EXIT_SUCCESS);
}

}

@@@@自己写system
System
功能:system()函数调用“/bin/sh -c command”执行特定的命令,阻塞当前进程直到command命令执行完毕
原型:
int system(const char *command);
返回值:
如果无法启动shell运行命令,system将返回127;出现不能执行system调用的其他错误时返回-1。如果system能够顺利执行,返回那个命令的退出码。
system函数执行时,会调用fork、execve、waitpid等函数。

自动动手写system命令
int my_system(const char *command)
{

pid_t pid;
int status;
if (command == NULL)
    return 1;

if ((pid = fork()) < 0)
    status = -1;
else if (pid == 0)
{
    execl("/bin/sh", "sh", "-c", command, NULL);
    exit(127);
}
else
{
    while (waitpid(pid, &status, 0) < 0)
    {
        if (errno == EINTR)
            continue;
        status = -1;
        break;
    }
}

return status;

}

@@@@守护进程的创建
int main(int argc, char *argv[])
{

mydaemon(1, 1);
//man daemon 可以看到
//0表示改变重定向   1表示不改变
//daemon(1, 1);
printf("test ...\n");
for (;;) ;
return 0;

}

int mydaemon(int nochdir, int noclose)
{

pid_t pid;
pid = fork();
if (pid == -1)
    ERR_EXIT("fork error");

if (pid > 0)
    exit(EXIT_SUCCESS);

setsid();

if (nochdir == 0)
    chdir("/");
if (noclose == 0)
{
    int i;
    for (i=0; i<3; ++i)
        close(i);
     //相当于把0号文件描述符之下/dev/null
    open("/dev/null", O_RDWR); //fd文件描述符fd-0的文件描述符指向  -16
    dup(0,1); //把0号文件描述符 赋值给空闲的文件描述符 1
    dup(0,2); //把0号文件描述符 赋值给空闲的文件描述符 2

}
return 0;

}

@@@@信号
signal函数,作用1:站在应用程序的角度,注册一个信号处理函数。

       作用2:忽略信号、设置信号默认处理  信号的安装和恢复

? typedef void (*sighandler_t) (int);
? #define SIG_ERR ((
sighandler_t) -1)
? #define SIG_DFL ((sighandler_t) 0)
? #define SIG_IGN ((
sighandler_t) 1)
? 函数原型:

__sighandler_t signal(int signum, __sighandler_t handler);

? 参数
? signal是一个带signum和handler两个参数的函数,准备捕捉或屏蔽的信号由参数signum给出,接收到指定信号时将要调用的函数由handler给出
? handler这个函数必须有一个int类型的参数(即接收到的信号代码),它本身的类型是void
? handler也可以是下面两个特殊值:

               SIG_IGN  屏蔽该信号
               SIG_DFL  恢复默认行为

void catch_Signal(int Sign)
{

switch(Sign)
{
    case SIGINT:
            glb = 1;
            //信号的恢复,恢复默认行为,即结束信号
            signal(SIGINT,SIG_DFL);
            break;
    case SIGPIPE:
            ;
            break;
}

}

int glb = 0;

int main(void)
{

setdaemon();

if (signal(SIGINT,catch_Signal)== SIG_ERR)
{
    perror("func signal err\n");
    return 0;
} 


while(1)
{
    if(glb==0)
        puts("!!!Hello World!!!");
    else
    {
        //释放socket套接字等资源
        break;
    }

}

//close(fd)
return EXIT_SUCCESS;

}

@@@@kill和killpg
kill函数
Kill基本用法
发送信号的函数有kill和raise
区别:kill既可以向自身发送信号,也可以向其他进程发送信号;
raise函数向进程自身发送信号。
Int kill(pid_t pid, int siq)
int raise(int signo)

Int kill(pid_t pid, int siq)
参数组合情况解释:
kill(pid_t pid, int siq)

pid>0 将信号sig发给pid进程
pid=0 将信号sig发给同组进程
pid=-1 将信号sig发送给所有进程,调用者进程有权限发送的每一个进程(除了1号进程之外,还有它自身)
pid<-1 将信号sig发送给进程组是pid(绝对值)的每一个进程

实验:

//子进程向父进程发送信号
//子进程向同组进程发送信号 (getpgrp()函数获取进程组pid)

结论:注意,如果在fork之前安装信号,则子进程可以继承信号。
kill和sleep在一起
子进程向父进程发送信号
sleep函数几点说明
1)sleep函数作用,让进程睡眠。
2)能被信号打断,然后处理信号函数以后,就不再睡眠了。直接向下执行代码
3)sleep函数的返回值,是剩余的秒数

raise函数
raise
? raise
? 给自己发送信号。raise(sig)等价于kill(getpid(), sig);
? killpg
? 给进程组发送信号。killpg(pgrp, sig)等价于kill(-pgrp, sig);
? sigqueue
? 给进程发送信号,支持排队,可以附带额外数据信息。
#include
#include

#include
#include
#include
#include
#include
#include

#include
#include
#include
#include
#include

#include
#include
#include
#include
#include

void myhandle(int num)
{

if (num == SIGINT)
{
    printf("recv signal SIGINT \n");
}
else if (num == SIGUSR1)
{
    printf("recv signal SIGINT \n");
}
else
{
    printf("recv signal id num : %d \n", num);
}

}

int main(void)
{

pid_t   pid;
printf("main ....begin\n");

if (signal(SIGINT, myhandle) == SIG_ERR)
{
    perror("func signal err\n");
    return 0;
} 
if (signal(SIGUSR1, myhandle) == SIG_ERR)
{
    perror("func signal err\n");
    return 0;
} 

pid = fork();
if (pid == -1)
{
    printf("fork err....\n");
    return 0;
}

//子进程向父进程发送信号
//子进程向同组进程发送信号
if (pid == 0)
{
    //pid1 = getppid();
    //kill(pid1, SIGUSR1);
    //或者
    pid = getpgrp();
    killpg(pid, SIGUSR1);
    exit(0);
}

int n = 3;

do 
{
    printf("父进程开始睡眠\n");
    n = sleep(n); //让其睡够,不然会被信号打断
    printf("父进程开始唤醒\n");
} while (n > 0);

//sleep(n);
//请思考sleep函数的作用 man sleep
printf("sleep 函数执行完毕以后返回...\n");
return 0;

}

@@@@alarm函数定时调用

pause函数
pause()函数
? 将进程置为可中断睡眠状态。然后它调用内核函数schedule(),使linux进程调度器找到另一个进程来运行。
? pause使调用者进程挂起,直到一个信号被捕获
alarm函数
alarm函数,设置一个闹钟延迟发送信号
告诉linux内核n秒中以后,发送SIGALRM信号;;
手册描述:
NAME

   alarm - set an alarm clock for delivery of a signal

SYNOPSIS

   #include <unistd.h>
   unsigned int alarm(unsigned int seconds);

DESCRIPTION

   alarm() arranges for a SIGALRM signal to be delivered to the process in seconds seconds.
   If seconds is zero, no new alarm() is scheduled.
   In any event any previously set alarm() is cancelled.

#include
#include
#include
#include
#include
#include
#include
#include

void myhandle(int num)
{

printf("recv signal id num : %d \n", num);
//kill -alram ` ps -aux | grep 01aram | grep -v vi | awk '{print $2}' ` 
alarm(1);//调用自身

}

int main(void)
{

printf("main ....begin\n");
//注册信号处理函数
if (signal(SIGALRM, myhandle) == SIG_ERR)
{
    perror("func signal err\n");
    return 0;
} 

//调用装载的信号函数
//alarm--->myhandle--->alarm
alarm(1);
while(1) 
{
    pause();
    printf("pause return\n");
}
return 0;

}

@@@@可重入和不可重入函数
可重入函数概念
? 为了增强程序的稳定性,在信号处理函数中应使用可重入函数。所谓可重入函数是指一个可以被多个任务调用的过程,
任务在调用时不必担心数据是否会出错。因为进程在收到信号后,就将跳转到信号处理函数去接着执行。如果信号处理函数
中使用了不可重入函数,那么信号处理函数可能会修改原来进程中不应该被修改的数据,这样进程从信号处理函数中返回接
着执行时,可能会出现不可预料的后果。不可再入函数在信号处理函数中被视为不安全函数。
? 满足下列条件的函数多数是不可再入的:
(1)使用静态的数据结构,如getlogin(),gmtime(),getgrgid(),getgrnam(),getpwuid()以及getpwnam()等等;
(2)函数实现时,调用了malloc()或者free()函数;
(3)实现时使用了标准I/O函数的
man手册实践

man 7 signal 查找可重入函数和不可重入函数,下一个内核用文件描述符来优化信号

结论:
在信号处理函数中,尽量不使用全局变量和静态变量的函数。特别是这个变量在程序中随时读写。

@@@@信号的阻塞和未达
信号在内核中的表示
? 执行信号的处理动作称为信号递达(Delivery),信号从产生到递达之间的状态,称为信号未决(Pending)。进程可以选择阻塞(Block)某个信号。被阻塞的信号产生时将保持在未决状态,直到进程解除对此信号的阻塞,才执行递达的动作。
? 注意,阻塞和忽略是不同,只要信号被阻塞就不会递达,而忽略是在递达之后可选的一种处理动作。信号在内核中的表示可以看作是这样的:

说明1)PCB进程控制块中结构体中有信号屏蔽状态字(block)信号未决状态字(pending)还有是否忽略标志;
说明2)信号屏蔽状态字(block),1代表阻塞、0代表不阻塞;信号未决状态字(pending)的1代表未决(表示有未达信号),0代表信号可以抵达了;
说明3)向进程发送SIGINT,内核首先判断信号屏蔽状态字是否阻塞,信号未决状态字(pending相应位制成1;若阻塞解除,信号未决状态字(pending)相应位制成0;表示信号可以抵达了。
说明4)block状态字、pending状态字 64bit;
说明5)block状态字用户可以读写,pending状态字用户只能读;这是信号设计机制。
思考1:状态字都64bit,编程时,如何表示状态字那?
思考2:block状态字信息如何获取或者操作那?哪些api?
思考3:pending状态字信息如何获取或者操作那?哪些api?

信号集操作函数(状态字表示)
? #include
? int sigemptyset(sigset_t *set); 把信号集清空 64bit/8=8个字节
? int sigfillset(sigset_t *set); 把信号集置成1
? int sigaddset(sigset_t *set, int signo); 根据signo,把信号集中的对应bit置成1
? int sigdelset(sigset_t *set, int signo); 根据signo,把信号集中的对应bit置成0
? int sigismember(const sigset_t *set, int signo);//判断signo是否在信号集中
sigprocmask读取或更改进程的信号屏蔽状态字(block)
? #include
? int sigprocmask(int how, const sigset_t *set, sigset_t *oset);
? 功能:读取或更改进程的信号屏蔽字。
? 返回值:若成功则为0,若出错则为-1
? 如果oset是非空指针,则读取进程的当前信号屏蔽字通过oset参数传出。如果set是非空指针,则更改进程的信号屏蔽字,参数how指示如何更改。如果oset和set都是非空指针,则先将原来的信号屏蔽字备份到oset里,然后根据set和how参数更改信号屏蔽字。假设当前的信号屏蔽字为mask,下表说明了how参数的可选值。

how含义

说明:SIG_BLOCK ,讲信号集set添加到进程block状态字中。
sigpending获取信号未决状态字(pending)信息
NAME

   sigpending - examine pending signals

SYNOPSIS

   #include <signal.h>
   int sigpending(sigset_t *set);

DESCRIPTION

   sigpending()  returns the set of signals that are pending for delivery to the calling thread (i.e., the signals which have been raised while blocked).  The mask of pending signals is returned in set.

#include
#include
#include
#include
#include

#include
#include
#include
#include
#include

//演示信号从产生到抵达的整个过程
//信号的阻塞和解除阻塞综合实验
//设置信号阻塞和非阻塞,设置ctrl+q来解除信号阻塞

#define ERR_EXIT(m)

do \
{ \
    perror(m); \
    exit(EXIT_FAILURE); \
} while(0)

void handler(int sig);

void handler(int sig)
{

if (sig == SIGINT)
    printf("recv a sig=%d\n", sig);
else if (sig == SIGQUIT)
{
    sigset_t uset;
    sigemptyset(&uset);
    sigaddset(&uset, SIGINT);
    //ctr + \ 用来接触  SIGQUIT 信号
    //解除阻塞
        sigprocmask(SIG_UNBLOCK, &uset, NULL);

}

}

void printsigset(sigset_t *set)
{

int i;
for (i=1; i<NSIG; ++i)
{
    if (sigismember(set, i))
        putchar('1');
    else
        putchar('0');
}
printf("\n");

}

//连续的按ctrl+c键盘,虽然发送了多个SIGINT信号,但是因为信号是不稳定的,只保留了一个。
//不支持排队
int main(int argc, char *argv[])
{

sigset_t pset; //用来打印的信号集
sigset_t bset; //用来设置阻塞的信号集

sigemptyset(&bset);
sigaddset(&bset, SIGINT);

if (signal(SIGINT, handler) == SIG_ERR)
    ERR_EXIT("signal error");

if (signal(SIGQUIT, handler) == SIG_ERR)
    ERR_EXIT("signal error");

//读取或更改进程的信号屏蔽字 这里用来阻塞ctrl+c信号
//ctrl+c信号被设置成阻塞,即使用户按下ctl+c键盘,也不会抵达
sigprocmask(SIG_BLOCK, &bset, NULL);

for (;;)
{
    //获取未决 字信息
    sigpending(&pset);

    //打印信号未决  sigset_t字
    printsigset(&pset);
    sleep(1);
}
return 0;

}

@@@@信号的高级用法
sigaction函数
? 包含头文件
? 功能:sigaction函数用于改变进程接收到特定信号后的行为。
? 原型:
int sigaction(int signum,const struct sigaction *act,const struct sigaction *old);
? 参数
? 该函数的第一个参数为信号的值,可以为除SIGKILL及SIGSTOP外的任何一 个特定有效的信号(为这两个信号定义自己的处理函数,将导致信号安装错误)
? 第二个参数是指向结构sigaction的一个实例的指针,在结构 sigaction的实例中,指定了对特定信号的处理,可以为空,进程会以缺省方式对信号处理
? 第三个参数oldact指向的对象用来保存原来对相应信号的处理,可指定oldact为NULL。
? 返回值:函数成功返回0,失败返回-1

signal(num., handle)

sigaction结构体
? 第二个参数最为重要,其中包含了对指定信号的处理、信号所传递的信息、信号处理函数执行过程中应屏蔽掉哪些函数等等
struct sigaction {

void (*sa_handler)(int);   //信号处理程序 不接受额外数据
void (*sa_sigaction)(int, siginfo_t *, void *); //信号处理程序 能接受额外数据,和sigqueue配合使用 
sigset_t sa_mask; //
int sa_flags; //影响信号的行为 SA_SIGINFO表示能接受数据
void (*sa_restorer)(void); //废弃

};
注意1:回调函数句柄sa_handler、sa_sigaction只能任选其一。
注意2:The sigaction structure is defined as something like 思考如何测试?
会查找、会用man手册,是通往高手的必经之路。

   The siginfo_t parameter to sa_sigaction is a struct with the following elements

          siginfo_t {
              int      si_signo;  /* Signal number */
              int      si_errno;  /* An errno value */
              int      si_code;   /* Signal code */
              pid_t    si_pid;    /* Sending process ID */
              uid_t    si_uid;    /* Real user ID of sending process */
              int      si_status; /* Exit value or signal */
              clock_t  si_utime;  /* User time consumed */
              clock_t  si_stime;  /* System time consumed */
              sigval_t si_value;  /* Signal value */ 
              int      si_int;    /* POSIX.1b signal */
              void *   si_ptr;    /* POSIX.1b signal */
              void *   si_addr;   /* Memory location which caused fault */
              int      si_band;   /* Band event */
              int      si_fd;     /* File descriptor */
          }


        typedef union sigval
        { 
          int sival_int; 
          void *sival_ptr; 
        }sigval_t; 

实验1:sigaction的函数注册信号,基本用法
void handler(int sig)
{

printf("recv a sig=%d\n", sig); 

}

__sighandler_t my_signal(int sig, __sighandler_t handler)
{

struct sigaction act;
struct sigaction oldact;
act.sa_handler = handler;
sigemptyset(&act.sa_mask);
act.sa_flags = 0;

if (sigaction(sig, &act, &oldact) < 0)
    return SIG_ERR;

return oldact.sa_handler;

}

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

struct sigaction act;
sigset_t sa_mask;

act.sa_handler = handler;
act.sa_flags = 0;
sigemptyset(&act.sa_mask);

//测试信号安装函数
//sigaction(SIGINT, &act, NULL);

//模拟signal函数
my_signal(SIGINT, handler);

for (;;)
{
    pause();
}
return 0;

}

实验2:测试sigaction结构体第三个参数sigset_t sa_mask的作用
/*
struct sigaction {

  void (*sa_handler)(int);
  void (*sa_sigaction)(int, siginfo_t *, void *);
  sigset_t sa_mask;  
  int sa_flags;  
  void (*sa_restorer)(void);

} */
//测试sigaction结构体第三个参数sigset_t sa_mask的作用
//作用 sigaddset(&act.sa_mask, SIGQUIT); 加入到sa_mask中的信号,被阻塞(信号处理函数执行的过程中被阻塞)。
//注意:SIGQUIT信号最终还会抵达

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

struct sigaction act;
act.sa_handler = handler;

sigemptyset(&act.sa_mask);
sigaddset(&act.sa_mask, SIGQUIT);
act.sa_flags = 0;

/*
//注意sigaction.sa_mask中的mask和信号阻塞的区别

struct sigaction act;
act.sa_handler = handler;
sigemptyset(&act.sa_mask);
sigaddset(&act.sa_mask, SIGQUIT);
act.sa_flags = 0;

sigset_t blokset;
sigemptyset(&blokset);
sigaddset(&blokset, SIGINT);

//ctrl + \ 用来接触  SIGINT 信号
//阻塞SIGINT 信号 让信号处于信号未决
sigprocmask(SIG_BLOCK, &blokset, NULL);

*/

if (sigaction(SIGINT, &act, NULL) < 0)
    ERR_EXIT("sigaction error");

for (;;)
    pause();
return 0;

}

void handler(int sig)
{

printf("recv a sig=%d 信号处理函数执行的时候,阻塞sa_mask中的信号\n", sig);
sleep(5);

}

实验3:演示两个进程间通过 sigqueue 发送数据
#include
#include
#include
#include
#include

#include
#include
#include
#include
#include

#define ERR_EXIT(m)

do \
{ \
    perror(m); \
    exit(EXIT_FAILURE); \
} while(0)

typedef struct Teacher
{

char name[64];
int age ;

}Teacher;

//演示两个进程间通过 sigqueue 发送数据
//struct sigaction act;
// act.sa_sigaction = myHandle_forsigaction; 接受数据

/*
void myHandle_forsigaction(int signum, siginfo_t *s_t, void *p)
{

    Teacher *pt = NULL;
    printf("recv signum : %d \n", signum);
    pt = s_t->si_value.sival_ptr;
    if (pt == NULL)
    {
        printf("pt is null\n"); 
    }
    else
    {
        printf("pt.name:%s pt.age:%d \n", pt->name, pt->age);   
    }

}
*/
void myHandle_forsigaction(int signum, siginfo_t *s_t, void *p)
{

//printf("recv a sig=%d data=%d data=%d\n", sig, info->si_value.sival_int, info->si_int);

if(signum == SIGINT)
{
    int myint = 0;
    printf("recv signum : %d \n", signum);
    myint = s_t->si_value.sival_int;
    printf("%d %d \n", myint, s_t->si_int );
}

}

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

pid_t   pid;
int ret = 0;

struct sigaction act;
act.sa_sigaction = myHandle_forsigaction;
sigemptyset(&act.sa_mask);
//重要!!开启传参数模式
act.sa_flags = SA_SIGINFO;

if (sigaction(SIGINT, &act, NULL) < 0)
    ERR_EXIT("sigaction error");

pid = fork();

if (pid == -1)
{
    printf("fork err...\n");
    return 0;
}

if (pid == 0)
{
    /*
    Teacher *t = (Teacher *)malloc(sizeof(Teacher));
    memset(t, 0, sizeof(Teacher));
    strcpy(t->name, "name");
    t->age = 33;
    */

   union sigval  mysigval;
   //mysigval.sival_ptr = (void *)&t;
   mysigval.sival_int = 111;    

    //kill(getppid(), SIGINT);

    ret  = sigqueue(getppid(), SIGINT, mysigval);
    if (ret != 0)
    {
        printf("sigqueue ....failed\n");
        exit(0);
    }
    else
    {
        printf("sigqueue...successs\n");
        sleep(2);
    }

}
else if (pid > 0)
{
    for (;;)
        pause();
}

return 0;

}

void handler(int sig)
{

printf("recv a sig=%d\n", sig);
sleep(5);

}

sigqueue新的信号发送函数
sigqueue函数

功能:新的发送信号系统调用,主要是针对实时信号提出的支持信号带有参数,与函数sigaction()配合使用。
注意:和kill函数相比Int kill(pid_t pid, int siq)多了参数
原型:
int sigqueue(pid_t pid, int sig, const union sigval value);
参数
第1个参数sigqueue是指定接收信号的进程id,
第2个参数确定即将发送的信号,
第3个参数是一个联合数据结构union sigval,指定了信号传递 的参数,即通常所说的4字节值。
返回值成功返回0,失败返回-1 

sigqueue()比kill()传递了更多的附加信息,但sigqueue()只能向一个进程发送信号,而不能发送信号给一个进程组。
sigval联合体

typedef union sigval

       { 
            int sival_int; 
             void *sival_ptr; 

}sigval_t;

======================综合============================
#include
#include
#include
#include
#include

#include
#include
#include
#include
#include

#define ERR_EXIT(m)

do \
{ \
    perror(m); \
    exit(EXIT_FAILURE); \
} while(0)

void myHandle(int sig, siginfo_t * s_t, void * p)
{

if (sig == SIGINT || sig == SIGRTMIN)
{
    printf("recv a sig=%d\n", sig);
    printf("The Same Para:%d  %d\n", s_t->si_value.sival_int, s_t->si_int );

}
else if (sig == SIGUSR1)  //发送用户自定义信号,用来解除阻塞
{
    sigset_t s;
    sigemptyset(&s);
    sigaddset(&s, SIGINT);
    sigaddset(&s, SIGRTMIN);
    sigprocmask(SIG_UNBLOCK, &s, NULL);
}

}

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

pid_t   pid;
int ret = 0;
//void (*sa_sigaction)(int, siginfo_t *, void *);
struct sigaction act;
act.sa_sigaction = myHandle;
sigemptyset(&act.sa_mask);
//打开传参flag
act.sa_flags = SA_SIGINFO;

//SIGINT非实时信号  SIGRTMIN 实时信号 添加到到进程阻塞中

sigset_t s;
sigemptyset(&s);
sigaddset(&s, SIGINT);
sigaddset(&s, SIGRTMIN);
sigprocmask(SIG_BLOCK, &s, NULL);

if (sigaction(SIGINT, &act, NULL) < 0)
    ERR_EXIT("sigaction error");

if (sigaction(SIGRTMIN, &act, NULL) < 0)
    ERR_EXIT("sigaction error");

if (sigaction(SIGUSR1, &act, NULL) < 0)
    ERR_EXIT("sigaction error");


pid = fork();

if (pid == -1)
{
    printf("fork err...\n");
    return 0;
}

if (pid == 0)
{
    int i = 0;
    int ret = 0;

    union sigval v;
    v.sival_int = 100;

    //发三次不可靠的信号
    for (i=0; i<3; i++)
    {
        v.sival_int = i+100;
        ret  = sigqueue(getppid(), SIGINT, v);
        if (ret != 0)
        {
            printf("sigqueue发送不可靠信号失败:%d .....\n", i+100);
            exit(0);
        }
        else
        {
            printf("sigqueue发送不可靠信号成功:%d .....\n", i+100);
        }
    }

    //发三次可靠的信号
    for (i=0; i<3; i++)
    {
        v.sival_int = i+200;
        ret  = sigqueue(getppid(), SIGRTMIN, v);
        if (ret != 0)
        {
            printf("sigqueue发送可靠信号失败:%d .....\n", i+200);
            exit(0);
        }
        else
        {
            printf("sigqueue发送可靠信号成功:%d .....\n", i+200);
        }
    }
    //解除信号阻塞
    printf("3秒后将解除信号阻塞,请观察可靠信号和非可靠信号区别 ctrl + \\退出 \n");
    sleep(3);
    kill(getppid(), SIGUSR1);
}
else if (pid > 0)
{
    for (;;)
        pause();
}

return 0;

}

============================================================

@@@@一块磁盘上模拟swap

dd if=/dev/zero of=/root/swap bs=1M count=200

mkswap /root/swap

swapon /root/swap

@@@@socket基础函数
1、man 7 ip

IPv4套接口地址结构

struct sockaddr_in {

       sa_family_t    sin_family; /* address family: AF_INET */
       in_port_t      sin_port;   /* port in network byte order */
       struct in_addr sin_addr;   /* internet address */
   };

/* Internet address. */
struct in_addr {

       uint32_t       s_addr;     /* address in network byte order */
   };

通用地址结构
struct sockaddr {

uint8_t  sin_len;
sa_family_t  sin_family;
char sa_data[14];

};

unix域协议 需要统一 网络字节序
x86平台四小端字节序

字节序转换函数
? uint32_t htonl(uint32_t hostlong);
? uint16_t htons(uint16_t hostshort);
? uint32_t ntohl(uint32_t netlong);
? uint16_t ntohs(uint16_t netshort);
? 说明:在上述的函数中,h代表host;n代表network s代表short;l代表long

地址转换函数
int inet_aton(const char *cp, struct in_addr *inp);
in_addr_t inet_addr(const char *cp);
char *inet_ntoa(struct in_addr in);

   #include <sys/socket.h>
   #include <netinet/in.h>
   #include <arpa/inet.h>

   int inet_aton(const char *cp, struct in_addr *inp); //1
   in_addr_t inet_addr(const char *cp); //2
   in_addr_t inet_network(const char *cp);
   char *inet_ntoa(struct in_addr in); //3
   struct in_addr inet_makeaddr(int net, int host);
   in_addr_t inet_lnaof(struct in_addr in);
   in_addr_t inet_netof(struct in_addr in);

调用listen函数

本套接字将变成被动套接字
注意:监听套接字属于被动套接字
客户端连接服务器比较简单
    创建一个套接字,默认情况下,是无连接的。
    调用connet函数,建立连接
表示一个连接  
    client ip、port===server ip和port

让服务器端支持多客户端连接
[root@localhost ~]$ netstat -an | grep 8001
tcp 0 0 192.168.6.249:8001 0.0.0.0:* LISTEN
tcp 3 0 192.168.6.249:8001 192.168.6.249:64824 ESTABLISHED
tcp 0 0 192.168.6.249:8001 192.168.6.249:64823 ESTABLISHED
tcp 0 0 192.168.6.249:64823 192.168.6.249:8001 ESTABLISHED
tcp 0 0 192.168.6.249:64824 192.168.6.249:8001 ESTABLISHED
[root@localhost ~]$

tcp 字节流 无边界
udp 消息、数据报 有边界

对等方,一次读操作,不能保证完全把消息读完。
对方接受数据包的个数是不确定的。
产生粘包问题的原因

1、SQ_SNDBUF 套接字本身有缓冲区 发送缓冲区
2、tcp传送的端 mss大小显示
3、链路层也有MTU大小限制。。。。,如果数据包大于>MTU要在IP层进行分片,导致消息分割
4、tcp的流量控制和拥塞控制,也可能导致粘包
5、tcp延迟发送机制 。。。等。。

tcp/ip协议,在传输层没有处理粘包问题。。

粘包解决方案
如果把程序放在广域网上,不处理粘包问题,程序基本上不能用。

ssize_t recv(int s, void *buf, size_t len, int flags);
与read相比

只能用于套接字文件描述符
多了一个flags


   MSG_OOB
          This  flag requests receipt of out-of-band data that would not be received in the normal data stream.  Some protocols place expedited data at the
          head of the normal data queue, and thus this flag cannot be used with such protocols.

带外数据 紧急指针

   MSG_PEEK
          This flag causes the receive operation to return data from the beginning of the receive queue without removing that data from the queue.  Thus, a
          subsequent receive call will return the same data.
    可以读数据,不从缓存区中读走

按行读取数据

一个一个字符的读,方法不好;多次调用系统调用read方法
使用readline 

//tcp
LISTEN 被动套接口 只能用来接受连接,处于关闭状态
CONNECT 主动套接口

TCP段    

@@@@TCP在后台开发中的特殊状态
①:FIN_WAIT_2
服务器端处于closed状态,不等于客户端也处理closed状态
所以,当主动关闭的一方等待被动关闭方的FIN状态,即等待被动方执行close(),
主动关闭将进入FIN_WAIT_2状态,被动关闭将是CLOSE_WAIT状态

②:TIME_WAIT
主动关闭的一方,会产生TIME_WAIT状态
两端同时关闭,将产生closing状态,最后双方都进程TIME_WAIT状态。
TIME_WAIT 2MSL 2倍的最大生命期时间
原因:(ACK y+1)如果发送失败可以重发。

③:RST重置
RST段(TCP/IP重置 )
1)服务器端启动、客户端启动

2)服务器端先kill与客户端通讯的子进程,服务器端会给客户端发送FIN flag
此时:只代表服务器端不发送数据了,不能代表客户端不能往套接字中写数据。
3)如果子进程此时写数据给服务器端(解除屏幕阻塞,输入字符aaaa),
将要导致TCP/IP协议重置,产生RST段;产生SIGPIPE信号。
4)所以,一般情况下,需要我们处理SIGPIPE信号,忽略即可。
SIGPIPE 13 Term Broken pipe: write to pipe with no readers

讲阻塞提前到select

驱动IO 不经常使用

8/10 =======>

并行:
并发:select无法实现并行,因为它也是select也是顺序的检测事件的到来,
对单核cpu是没有并行而言;
对多核cpu是有并行。。。。。
int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout);

void FD_CLR(int fd, fd_set *set);
int FD_ISSET(int fd, fd_set *set);
void FD_SET(int fd, fd_set *set);
void FD_ZERO(fd_set *set);
四个宏

select管理者
用select来管理多个IO

一旦其中的一个I/O或者多个IO检测到我们所感兴趣的事件,
select函数返回,返回值为检测到的事件个数;
并且返回哪些事件有变化。
变量这些事件,进而处理

用单进程处理多个IO事件
程序可以同时检测两种事件

1个是标准输入、一个网络可读事件。。。。

应用场景:用单进程处理多个I/O事件

单进程可以轮询select,有IO事件,就处理 
    用单进程处理多个IO事件

方案1
void handler(int sig)
{

return 0;

}

//用闹钟来还有其他用途,冲突的解决比较麻烦
//但是一个app只能有一个闹钟,会和其他
SIGALRM

signal(SIGALRM, handle);
alarm(5)
int ret = read(fd, buf, sizeof(buf));
if (ret==-1 && errno=EINTR)
{
    errno = ETIMEDOUT;
}
else if (ret >=0)
{
    alarm(0)';
}

套接字方案

SO_SNDTIME 发送的时间
SO_RCVTIME 接受的时间
setsockop (sock, SOL_SOCKET, SO_RCVTIMEO, 5);
if (ret==-1 && errno=EWOULDBLOCK)
{
    errno = ETIMEDOUT;
}

因为:有些tcpip协议不支持

用select来实现超时

为什么要做连接超时的select实现

RTT 一次连接往返时间 需要1.5RTT

connect 需要1.5超时时间

在广域网上,经常出现网络拥塞,RTT默认返回事件是75妙

connect 超时时间

//ulimit -n 获取最大文件描述符
通过命令和通过程序都可以调整内核参数
不过要root用户
ulimit -n 2048
一个套接字就要占用一个文件描述符。。。

@@@@防止黏包

//1一次全部读走 //2次读完数据 //出错分析 //对方已关闭
//思想:tcpip是流协议,不能保证1次读操作,能全部把报文读走,所以要循环读指定长度的数据。
//按照count大小读数据,
//若读取的长度ssize_t<count 说明读到了一个结束符,对方已关闭。

//@ssize_t:返回读的长度 若ssize_t<count 读失败失败
//@buf:接受数据内存首地址
//@count:接受数据长度
ssize_t readn(int fd, void *buf, size_t count)
{

size_t nleft = count;
ssize_t nread;
char *bufp = (char*)buf;

while (nleft > 0)
{
    if ((nread = read(fd, bufp, nleft)) < 0)
    {
        if (errno == EINTR)
            continue;
        return -1;
    }
    else if (nread == 0) //若对方已关闭
        return count - nleft;

    bufp += nread;
    nleft -= nread;
}

return count;

}

//1一次全部读走 //2次读完数据 //出错分析 //对方已关闭
//思想:tcpip是流协议,不能1次把指定长度数据,全部写完
//按照count大小写数据
//若读取的长度ssize_t<count 说明读到了一个结束符,对方已关闭。
//@ssize_t:返回写的长度 -1失败
//@buf:待写数据首地址
//@count:待写长度

ssize_t writen(int fd, const void *buf, size_t count)
{

size_t nleft = count;
ssize_t nwritten;
char *bufp = (char*)buf;

while (nleft > 0)
{
    if ((nwritten = write(fd, bufp, nleft)) < 0)
    {
        if (errno == EINTR)
            continue;
        return -1;
    }
    else if (nwritten == 0)
        continue;

    bufp += nwritten;
    nleft -= nwritten;
}

return count;

}

@@@@消息队列
消息队列函数
#include
#include
#include
int msgget(key_t key, int msgflg);
int msgctl(int msqid, int cmd, struct msqid_ds *buf);
int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);
ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp, int msgflg);

msgget函数
功能:用来创建和访问一个消息队列
原型
int msgget(key_t key, int msgflg);
参数
key: 某个消息队列的名字
msgflg:由九个权限标志构成,它们的用法和创建文件时使用的mode模式标志是一样的
返回值:成功返回一个非负整数,即该消息队列的标识码;失败返回-1

msgctl函数
功能:消息队列的控制函数
原型
int msgctl(int msqid, int cmd, struct msqid_ds *buf);
参数
msqid: 由msgget函数返回的消息队列标识码
cmd:是将要采取的动作,(有三个可取值)
IPC_STAT

  Copy  information  from the kernel data structure associated with msqid into the msqid_ds structure pointed to by buf.  The caller
  must have read permission on the message queue.

IPC_SET

  Write the values of some members of the msqid_ds structure pointed to by buf to the kernel data  structure  associated  with  this
  message  queue, updating also its msg_ctime member.  The following members of the structure are updated: msg_qbytes, msg_perm.uid,
  msg_perm.gid, and (the least significant 9 bits of) msg_perm.mode.  The effective UID of the calling process must match the  owner
  (msg_perm.uid)  or  creator (msg_perm.cuid) of the message queue, or the caller must be privileged.  Appropriate privilege (Linux:
  the CAP_SYS_RESOURCE capability) is required to raise the msg_qbytes value beyond the system parameter MSGMNB.

IPC_RMID

  Immediately remove the message queue, awakening all waiting reader and writer processes (with an error return  and  errno  set  to
  EIDRM).  The calling process must have appropriate privileges or its effective user ID must be either that of the creator or owner
  of the message queue.  The third argument to msgctl() is ignored in this case.

返回值:成功返回0,失败返回-1

/*
struct msqid_ds {

           struct ipc_perm msg_perm;     // Ownership and permissions 
           time_t          msg_stime;    // Time of last msgsnd(2) 
           time_t          msg_rtime;    // Time of last msgrcv(2) 
           time_t          msg_ctime;    // Time of last change 
           unsigned long   __msg_cbytes; // Current number of bytes in
                                         // queue (nonstandard) 
           msgqnum_t       msg_qnum;     // Current number of messages
                                         // in queue 
           msglen_t        msg_qbytes;   // Maximum number of bytes
                                         // allowed in queue 
           pid_t           msg_lspid;    // PID of last msgsnd(2) 
           pid_t           msg_lrpid;    // PID of last msgrcv(2) 
       };

   The ipc_perm structure is defined as follows (the highlighted fields are settable using IPC_SET):

       struct ipc_perm {
           key_t          __key;       // Key supplied to msgget(2) 
           uid_t          uid;         // Effective UID of owner 
           gid_t          gid;         // Effective GID of owner 
           uid_t          cuid;        // Effective UID of creator 
           gid_t          cgid;        // Effective GID of creator 
           unsigned short mode;        // Permissions 
           unsigned short __seq;       // Sequence number 
       };

*/
msgsnd函数
功能:把一条消息添加到消息队列中
原型
int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);
参数
msgid: 由msgget函数返回的消息队列标识码
msgp:是一个指针,指针指向准备发送的消息,
msgsz:是msgp指向的消息长度,这个长度不含保存消息类型的那个long int长整型
msgflg:控制着当前消息队列满或到达系统上限时将要发生的事情
返回值:成功返回0;失败返回-1
msgflg=IPC_NOWAIT表示队列满不等待,返回EAGAIN错误。
消息结构在两方面受到制约。首先,它必须小于系统规定的上限值;其次,它必须以一个long int长整数开始,接收者函数将利用这个长整数确定消息的类型
消息结构参考形式如下:
struct msgbuf {

long  mtype;
char mtext[100];

}

msgrcv函数
功能:是从一个消息队列接收消息
原型
ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp, int msgflg);
参数
msgid: 由msgget函数返回的消息队列标识码
msgp:是一个指针,指针指向准备接收的消息,
msgsz:是msgp指向的消息长度,这个长度不含保存消息类型的那个long int长整型
msgtype:它可以实现接收优先级的简单形式
msgflg:控制着队列中没有相应类型的消息可供接收时将要发生的事
返回值:成功返回实际放到接收缓冲区里去的字符个数,失败返回-1
msgtype=0返回队列第一条信息
msgtype>0返回队列第一条类型等于msgtype的消息 
msgtype<0返回队列第一条类型小于等于msgtype绝对值的消息,并且是满足条件的消息类型最小的消息

msgflg=IPC_NOWAIT,队列没有可读消息不等待,返回ENOMSG错误。
msgflg=MSG_NOERROR,消息大小超过msgsz时被截断
msgtype>0且msgflg=MSG_EXCEPT,接收类型不等于msgtype的第一条消息。

#include
#include
#include

int main()
{

int msqid;

//若不存在就创建,否则,使用旧的
msqid = msgget(0x1234, 0666| IPC_CREAT);

//如果不存在则创建,如果存在则提示已经存在
//msqid = msgget(0x1234, 0666| IPC_CREAT | IPC_EXCL);
//返回EEXIST错误码

//创建私有的消息队列,只可以在相关联的进程间使用
//msqid = msgget(IPC_PRIVATE, 0666);

if( msgid == -1)
{
    if(errno == ENOENT)
    {
        printf("不存在\n");

    }
    /*
    if(errno == EEXIST)
    {
        printf("已经存在,会被覆盖\n");

    }*/
    perror("msgget err\n");
    return -1;
}
printf("创建共享内存成功\n");


struct msqid_ds buf;
memset(&buf, 0, sizeof(buf));

//msgctl获取信息
int ret = msgctl(msgid, IPC_STAT, &buf);
if (ret == -1)
{
     perror("msgctl err\n");
     return -1;
}

printf("权限信息:%o\n",buf.msg_perm.mode);
printf("当前消息队列有多少字节:%ld\n",buf.__msg_cbytes);
printf("当IQ按消息队列的个数:%d\n",buf.msg_qnum);

//msgctl修改信息
buf.msg_perm.mode = 0644;
ret = msgctl(msgid, IPC_SET, &buf);
if (ret == -1)
{
     perror("msgctl err\n");
     return -1;
}
printf("修改信息:%d\n",buf.msg_perm.mode);

//msgctl删除信息
ret = msgctl(msgid, IPC_SET, NULL);
if (ret == -1)
{
     perror("msgctl err\n");
     return -1;
}
printf("删除信息");
return 0;

}
//综合演示
struct msg_buf
{

long mtype;
char data[255];

};

/* 注意long 和 int 在32bit 和 64bit系统之下是不一样的
struct msg_buf
{

long mtype;
char data[255];

};
*/

int main()
{

key_t key;
int msgid;
int ret;
struct msg_buf msgbuf;
int msgtype =  getpid();

key=ftok("./msgfile",'a');
printf("key =[%x]\n",key);

printf("sizeof(long):%ld, sizeof(int):%d \n", sizeof(long), sizeof(int));

msgid=msgget(key, IPC_CREAT |IPC_EXCL|0666); //通过文件对应

if(msgid==-1)
{
    if (errno == EEXIST)
    {
        printf("EEXIST:.....\n");
        key=ftok("./msgfile",'a');
        msgid=msgget(key, IPC_CREAT|0666); //通过文件对应
    }
    else
    {
        printf("create error\n");
        perror("msget: \n");
        return -1;
    }

}
printf("msgid:%d \n", msgid);

msgbuf.mtype = msgtype; //        getpid();

printf("getpid(): %d \n", getpid());
strcpy(msgbuf.data,"test haha");
ret = msgsnd(msgid,&msgbuf, sizeof(msgbuf.data), IPC_NOWAIT);
if(ret==-1)
{
        printf("send message err\n");
        perror("senderr");
        return -1;
}
sleep(1);

memset(&msgbuf,0,sizeof(msgbuf));

ret=msgrcv(msgid, &msgbuf, sizeof(msgbuf.data), msgtype, IPC_NOWAIT);
if(ret==-1)
{
        printf("recv message err\n");
        perror("dd");
        return -1;
}
printf("recv msg =[%s]\n",msgbuf.data);

}

@@@@共享内存
共享内存函数
#include
#include
int shmget(key_t key, size_t size, int shmflg);
void *shmat(int shmid, const void *shmaddr, int shmflg);
int shmdt(const void *shmaddr);
int shmctl(int shmid, int cmd, struct shmid_ds *buf);

shmget函数
功能:用来创建共享内存
原型
int shmget(key_t key, size_t size, int shmflg);
参数
key:这个共享内存段名字
size:共享内存大小
shmflg:由九个权限标志构成,它们的用法和创建文件时使用的mode模式标志是一样的
返回值:成功返回一个非负整数,即该共享内存段的标识码;失败返回-1

shmat函数
功能:将共享内存段连接到进程地址空间
原型
void *shmat(int shmid, const void *shmaddr, int shmflg);
参数
shmid: 共享内存标识
shmaddr:指定连接的地址
shmflg:它的两个可能取值是SHM_RND和SHM_RDONLY
返回值:成功返回一个指针,指向共享内存第一个节;失败返回-1

shmat函数(续)
shmaddr为NULL,核心自动选择一个地址
shmaddr不为NULL且shmflg无SHM_RND标记,则以shmaddr为连接地址。
shmaddr不为NULL且shmflg设置了SHM_RND标记,则连接的地址会自动向下调整为SHMLBA的整数倍。公式:shmaddr - (shmaddr % SHMLBA)
shmflg=SHM_RDONLY,表示连接操作用来只读共享内存

shmdt函数
功能:将共享内存段与当前进程脱离
原型
int shmdt(const void *shmaddr);
参数
shmaddr: 由shmat所返回的指针
返回值:成功返回0;失败返回-1
注意:将共享内存段与当前进程脱离不等于删除共享内存段

shmctl函数
功能:用于控制共享内存
原型
int shmctl(int shmid, int cmd, struct shmid_ds *buf);
参数
shmid:由shmget返回的共享内存标识码
cmd:将要采取的动作(有三个可取值)
buf:指向一个保存着共享内存的模式状态和访问权限的数据结构
返回值:成功返回0;失败返回-1

typedef struct
{
char name[20];
int age;
}Teacher;

int main()
{

int shmid;

//如果存在就报错
shmid = shmget(0x1234, sizeof(Teacher),0666| IPC_CREAT |IPC_EXCL);

if( shmid == -1)
{
    if(errno == ENOENT)
    {
        printf("不存在\n");            
    }

    if(errno == EEXIST)
    {

       shmid = shmget(0x1234, sizeof(Teacher), 0666| IPC_CREAT);
    }
    perror("shmget err\n");
    return -1;
}

Teacher t1 = {"Alex",34};
Teacher *p = NULL;

p = shmat(shmid, NULL, 0);
int i;
for(i=0; i<5; i++)
{
    memcpy(p+sizeof(Teacher)*i, &t1, sizeof(t1));
}

printf("键入1删除 共享内存\n");
int num = 0;
scanf("%d",&num);

if(num == 1)
{
    //断掉共享内存
    shmdt(p);
    //清理
    shmctl(shmid, IPC_RMID, NULL);
}else
{
    ;
}

}

@@@@信号量
信号量

信号量和P、V原语由Dijkstra(迪杰斯特拉)提出
信号量
互斥:P、V在同一个进程中
同步:P、V在不同进程中
信号量值含义
S>0:S表示可用资源的个数
S=0:表示无可用资源,无等待进程
S<0:|S|表示等待队列中进程个数

struct semaphore
{

int value; 
pointer_PCB queue;

}

P原语
P(s)
{

s.value = s.value--;
if (s.value < 0)
{
    该进程状态置为等待状状态
    将该进程的PCB插入相应的等待队列s.queue末尾
}

}

V原语
V(s)
{

s.value = s.value++;
if (s.value < =0)
{
    唤醒相应等待队列s.queue中等待的一个进程
    改变其状态为就绪态
    并将其插入就绪队列
}

}

struct semid_ds {

struct ipc_perm sem_perm;  /* Ownership and permissions */
time_t       sem_otime; /* Last semop time */
time_t       sem_ctime; /* Last change time */
unsigned short  sem_nsems; /* No. of semaphores in set */

};

信号量集函数
#include
#include
#include
int semget(key_t key, int nsems, int semflg);
int semctl(int semid, int semnum, int cmd, ...);
int semop(int semid, struct sembuf *sops, unsigned nsops);?

semget函数
功能:用来创建和访问一个信号量集
原型
int semget(key_t key, int nsems, int semflg);
参数
key: 信号集的名字
nsems:信号集中信号量的个数
semflg: 由九个权限标志构成,它们的用法和创建文件时使用的mode模式标志是一样的
返回值:成功返回一个非负整数,即该信号集的标识码;失败返回-1

semctl函数
功能:用于控制信号量集
原型
int semctl(int semid, int semnum, int cmd, ...);
参数
semid:由semget返回的信号集标识码
semnum:信号集中信号量的序号
cmd:将要采取的动作(有三个可取值)
最后一个参数根据命令不同而不同
返回值:成功返回0;失败返回-1

shmctl函数续

semop函数
功能:用来创建和访问一个信号量集
原型
int semop(int semid, struct sembuf *sops, unsigned nsops);
参数
semid:是该信号量的标识码,也就是semget函数的返回值
sops:是个指向一个结构数值的指针
nsops:信号量的个数
返回值:成功返回0;失败返回-1

semop函数续
sembuf结构体:
struct sembuf {

short sem_num; 
short sem_op; 
short sem_flg; 

};
sem_num是信号量的编号。
sem_op是信号量一次PV操作时加减的数值,一般只会用到两个值,一个是“-1”,也就是P操作,等待信号量变得可用;另一个是“+1”,也就是我们的V操作,发出信号量已经变得可用
sem_flag的两个取值是IPC_NOWAIT或SEM_UNDO

/信号量API示例及工具/

//创建信号量
#include
#include
#include
#include
#include
#include
#include

#define ERR_EXIT(m)

        do                             \
        {                              \
            perror(m);                 \
            exit(EXIT_FAILURE);        \
                                       \
        }while(0)

union semun {

       int              val;    /* Value for SETVAL */
       struct semid_ds *buf;    /* Buffer for IPC_STAT, IPC_SET */
       unsigned short  *array;  /* Array for GETALL, SETALL */
       struct seminfo  *__buf;  /* Buffer for IPC_INFO
                                   (Linux-specific) */
   };

int sem_create(key_t key)
{

/* 错误的书写方式示:注意是 IPC_CREAT | IPC_EXCL不是  O_CREAT|O_EXCL);
int semid = 0;
semid = semget(key, 1, 0666 | O_CREAT|O_EXCL);
if (semid == -1)
    ERR_EXIT("semget");

return semid;
*/


int semid = semget(key, 1, 0666 | IPC_CREAT );
if (semid == -1)
    ERR_EXIT("semget");

return semid;

}

int sem_open(key_t key)
{

int semid = semget(key, 0, 0);
if (semid == -1)
    ERR_EXIT("semget");
return semid;

}

int sem_setval(int semid, int val)
{

union semun su;
su.val = val;//定义有几个资源
int ret;
ret = semctl(semid, 0, SETVAL, su);
if (ret == -1)
    ERR_EXIT("sem_setval");

return 0;

}

int sem_getval(int semid)
{

union semun su;

int ret;
ret = semctl(semid, 0, GETVAL, su);

if (ret == -1)
    ERR_EXIT("sem_setval");
int val = su.val;
printf("val:%d\n",val);
return 0;

}

int sem_d(int semid)
{

int ret;
ret = semctl(semid, 0, IPC_RMID, 0);
if (ret == -1)
    ERR_EXIT("semctl");

return 0;

}

int sem_p(int semid)
{

 int ret = 0;
 struct sembuf sp = {0, -1, 0};
 ret =  semop(semid, &sp, 1); //第三个参数是信号量的参数
 if (ret == -1)
    ERR_EXIT("semctl");
 return ret;

}

int sem_v(int semid)
{

 struct sembuf sp = {0, 1, 0};
 int ret =  semop(semid, &sp, 1); //第三个参数是信号量的参数
 if (ret == -1)
    ERR_EXIT("semctl");
 return ret;

}

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

int semid;
semid = sem_create(0x1234);
semid = sem_open(0x1234);
printf("semid:%d \n", semid);

sem_setval(semid, 2);
sem_getval(semid);

fork();

//因为有两个资源,所以两个进程并发访问
sem_p(semid);
    int i = 0;
    printf("i:%d pid:%d \n",i++, getpid());
    sleep(2);
    printf("i:%d pid:%d \n",i++, getpid());
sem_v(semid);

sem_d(semid);

printf("the end!!\n");
return 0;

}

/SemTool/
#include
#include
#include
#include
#include
#include
#include

#define ERR_EXIT(m)

    do \
    { \
            perror(m); \
            exit(EXIT_FAILURE); \
    } while(0)

union semun
{

int val;                  /* value for SETVAL */
struct semid_ds *buf;     /* buffer for IPC_STAT, IPC_SET */
unsigned short *array;    /* array for GETALL, SETALL */
/* Linux specific part: */
struct seminfo *__buf;    /* buffer for IPC_INFO */

};

int sem_create(key_t key)
{

int semid = semget(key, 1, 0666 | IPC_CREAT | IPC_EXCL);
if (semid == -1)
    ERR_EXIT("semget");

return semid;

}

int sem_open(key_t key)
{

int semid = semget(key, 0, 0);
if (semid == -1)
    ERR_EXIT("semget");

return semid;

}

int sem_p(int semid)
{

struct sembuf sb = {0, -1, /*IPC_NOWAIT*/SEM_UNDO};
int ret = semop(semid, &sb, 1);
if (ret == -1)
    ERR_EXIT("semop");

return ret;

}

int sem_v(int semid)
{

struct sembuf sb = {0, 1, /*0*/SEM_UNDO};
int ret = semop(semid, &sb, 1);
if (ret == -1)
    ERR_EXIT("semop");

return ret;

}

int sem_d(int semid)
{

int ret = semctl(semid, 0, IPC_RMID, 0);
if (ret == -1)
    ERR_EXIT("semctl");
return ret;

}

int sem_setval(int semid, int val)
{

union semun su;
su.val = val;
int ret = semctl(semid, 0, SETVAL, su);
if (ret == -1)
    ERR_EXIT("semctl");

printf("value updated...\n");
return ret;

}

int sem_getval(int semid)
{

int ret = semctl(semid, 0, GETVAL, 0);
if (ret == -1)
    ERR_EXIT("semctl");

printf("current val is %d\n", ret);
return ret;

}

int sem_getmode(int semid)
{

union semun su;
struct semid_ds sem;
su.buf = &sem;
int ret = semctl(semid, 0, IPC_STAT, su);
if (ret == -1)
    ERR_EXIT("semctl");

printf("current permissions is %o\n", su.buf->sem_perm.mode);
return ret;

}

int sem_setmode(int semid, char *mode)
{

union semun su;
struct semid_ds sem;
su.buf = &sem;

int ret = semctl(semid, 0, IPC_STAT, su);
if (ret == -1)
    ERR_EXIT("semctl");

printf("current permissions is %o\n", su.buf->sem_perm.mode);
sscanf(mode, "%o", (unsigned int *)&su.buf->sem_perm.mode);
ret = semctl(semid, 0, IPC_SET, su);
if (ret == -1)
    ERR_EXIT("semctl");

printf("permissions updated...\n");

return ret;

}

void usage(void)
{

fprintf(stderr, "usage:\n");
fprintf(stderr, "semtool -c\n");
fprintf(stderr, "semtool -d\n");
fprintf(stderr, "semtool -p\n");
fprintf(stderr, "semtool -v\n");
fprintf(stderr, "semtool -s <val>\n");
fprintf(stderr, "semtool -g\n");
fprintf(stderr, "semtool -f\n");
fprintf(stderr, "semtool -m <mode>\n");

}

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

int opt;

opt = getopt(argc, argv, "cdpvs:gfm:");
if (opt == '?')
    exit(EXIT_FAILURE);
if (opt == -1)
{
    usage();
    exit(EXIT_FAILURE);
}

key_t key = ftok(".", 's');
int semid;
switch (opt)
{
case 'c':
    sem_create(key);
    break;
case 'p':
    semid = sem_open(key);
    sem_p(semid);
    sem_getval(semid);
    break;
case 'v':
    semid = sem_open(key);
    sem_v(semid);
    sem_getval(semid);
    break;
case 'd':
    semid = sem_open(key);
    sem_d(semid);
    break;
case 's':
    semid = sem_open(key);
    sem_setval(semid, atoi(optarg));
    break;
case 'g':
    semid = sem_open(key);
    sem_getval(semid);
    break;
case 'f':
    semid = sem_open(key);
    sem_getmode(semid);
    break;
case 'm':
    semid = sem_open(key);
    sem_setmode(semid, argv[2]);
    break;
}

return 0;

}

@@@@pthread线程库

POSIX线程库
与线程有关的函数构成了一个完整的系列,绝大多数函数的名字都是以“pthread_”打头的
要使用这些函数库,要通过引入头文
链接这些线程函数库时要使用编译器命令的“-lpthread”选项

pthread_create函数
功能:创建一个新的线程
原型
int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(start_routine)(void), void *arg);
参数
thread:返回线程ID
attr:设置线程的属性,attr为NULL表示使用默认属性
start_routine:是个函数地址,线程启动后要执行的函数
arg:传给线程启动函数的参数
返回值:成功返回0;失败返回错误码
错误检查
传统的一些函数是,成功返回0,失败返回-1,并且对全局变量errno赋值以指示错误。
pthreads函数出错时不会设置全局变量errno(而大部分其他POSIX函数会这样做)。而是将错误代码通过返回值返回
pthreads同样也提供了线程内的errno变量,以支持其它使用errno的代码。对于pthreads函数的错误,建议通过返回值业判定,因为读取返回值要比读取线程内的errno变量的开销更小

pthread_exit函数
功能:线程终止
原型
void pthread_exit(void *value_ptr);
参数
value_ptr:value_ptr不要指向一个局部变量。
返回值:无返回值,跟进程一样,线程结束的时候无法返回到它的调用者(自身)

pthread_join函数
功能:等待线程结束
原型
int pthread_join(pthread_t thread, void **value_ptr);
参数
thread:线程ID
value_ptr:它指向一个指针,后者指向线程的返回值
返回值:成功返回0;失败返回错误码

pthread_self函数
功能:返回线程ID
原型
pthread_t pthread_self(void);
返回值:成功返回0

pthread_cancel函数
功能:取消一个执行中的线程
原型
int pthread_cancel(pthread_t thread);
参数
thread:线程ID
返回值:成功返回0;失败返回错误码

pthread_detach函数
功能:将一个线程分离
原型
int thread);
参数
thread:线程ID
返回值:成功返回0;失败返回错误码

`