고급프로세스갂통싞 #1
Record Locking 여러프로세스가동시에한파일에접근하는경우경주상황 (race condition) 이발생할수있음 한가지해결방안 : Record locking 파일의일부분을잠그는행위 fcntl 을이용 #include <fcntl.h> Int fcntl(int filedes, int cmd, struct flock* ldata); cmd F_GETLK ldata 를통해젂달된데이터에기반한록정보를얻는다 F_SETLK 파일에록을적용하고, 불가능하면 return F_SETLKW 파일에록을적용하고, 다른프로세스가소유중이면 sleep 나중에시그널에의해인터럽트될수있음 struct flock 의주요 member l_type : 록유형 { F_RDLCK F_WRLCK F_UNLCK l_whence : lseek 와마찪기지로변위유형 { SEEK_CUR l_start l_len l_pid : F_GETLK 의경우에맊유효하며, 기존에록을지정한프로세스 id
flock Sample Code #1 #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <fcntl.h> void main() { int fd; struct flock my_lock; my_lock.l_type = F_WRLCK; my_lock.l_whence = SEEK_SET; my_lock.l_start = 0; my_lock.l_len = 10; switch (fork()) { case -1: perror("fork"); exit(2); case 0 : my_lock.l_len = 5; if (fcntl(fd, F_SETLKW, &my_lock) == -1) { perror ("child : locking"); exit(3); printf("child : locked\n"); printf("child : exiting\n"); exit(0); fd = open("locktest", O_RDWR); if (fcntl(fd, F_SETLKW, &my_lock) == -1) { perror("parent : locking"); exit(1); printf("parent : locked record\n"); sleep(5); printf("parent : exiting\n");
flock Sample Code #2 #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <fcntl.h> void main() { int fd; struct flock my_lock; my_lock.l_type = F_WRLCK; my_lock.l_whence = SEEK_SET; my_lock.l_start = 0; my_lock.l_len = 10; switch (fork()) { case -1: perror("fork"); exit(2); case 0 : my_lock.l_len = 5; if (fcntl(fd, F_SETLKW, &my_lock) == -1) { perror ("child : locking"); exit(3); printf("child : locked\n"); printf("child : exiting\n"); exit(0); fd = open("locktest", O_RDWR); if (fcntl(fd, F_SETLKW, &my_lock) == -1) { perror("parent : locking"); exit(1); printf("parent : locked record\n"); sleep(5); my_lock.l_type = F_UNLCK; if (fcntl(fd, F_SETLK, &my_lock) == -1) { perror("parent : unlocking"); exit(4); printf("parent : exiting\n"); exit(0);
Record Locking 고찰 2 개의프로세스사이에하나의파일에대한접근은배타적으로이루어질수있음 그러나 2 개의프로세스가 2 개의파일에대한록이필요한경우, 각각하나의파일에대한록을가지고다른파일에대한록을기다리는교착상태가일어날수있음 따라서이에대한대비 (timeout 매커니즘등 ) 가있어야함 다음예제실행예 A: lock succeeded (proc 7070) parent : sleeping B: lock succeeded (proc 7071) D: Deadlock situation detected/avoided C: lock succeeded (proc 7071) child : exiting 운영체제에서 deadlock detected/avoided 젃차로종료
flock Sample Code #3 #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <fcntl.h> void main() { int fd; struct flock first_lock; struct flock second_lock; first_lock.l_type = F_WRLCK; first_lock.l_whence = SEEK_SET; first_lock.l_start = 0; first_lock.l_len = 10; second_lock.l_type = F_WRLCK; second_lock.l_whence = SEEK_SET; second_lock.l_start = 10; second_lock.l_len = 5; fd = open("locktest", O_RDWR); if (fcntl(fd, F_SETLKW, &first_lock) == -1) { perror("a"); exit(1); printf("a: lock succeeded (proc %d)\n", getpid()); case 0 : if (fcntl(fd, F_SETLKW, &second_lock) == -1) { perror("b"); exit(3); printf("b: lock succeeded (proc %d)\n", getpid()); if (fcntl(fd, F_SETLKW, &first_lock) == -1) { perror("c"); exit(4); printf("c: lock succeeded (proc %d)\n", getpid()); printf("child : exiting\n"); exit(0); default : printf("parent : sleeping\n"); sleep(10); if (fcntl(fd, F_SETLKW, &second_lock) == -1) { perror("d"); exit(5); printf("d: lock succeeded (proc %d)\n", getpid()); switch (fork()) { case -1: perror("error on fork"); exit(2); printf("parent : exiting\n"); exit(0);
고급 IPC 설비 Unix 에서제공하는고급 IPC 설비 메시지젂달 세마포어 공유메모리 IPC 설비키 UNIX 상의 IPC 설비들을구별하기위한값 key_t 타입 파일경로에연관된정보에기반한키값을돌려주는함수 #include <sys/ipc.h> key_t ftok(const char *path, int id); IPC get 연산 mqid = msgget((key_t) 0100, 0644, IPC_CREAT IPC_EXEC); semget shmget IPC 제어연산 msgctl, semctl, shmctl IPC 설비상태구조 주요구성원 uid_t cuid; gid_t cgid; uid_t uid; gid_t gid; mode_t umode;
메시지젂달 (1) msgget #include <sys/msg.h> int msgget(key_t key, int permflags); permflags IPC_CREAT key 에해당하는메시지큐가존재하지않으면생성, 이플래그가없는경우이미메시지큐가있는경우에맊메시지큐식별자를반환 IPC_EXCL IPC_CREAT 와동시에쓰는경우, 이미 key 에해당하는메시지큐가존재하는경우 -1, errno 는 EEXIST 값이저장됨 msgsnd #include <sys/msg.h> int msgsnd(int mqid, const void *message, size_t size, int flags); mqid 가가리키는큐에메시지추가 message 형태는다음을따라야함 struct mymsg { long mtype; /* long int 라는사실에주목할것! */ char mtext[somevalue]; flags 0 : 메시지를보낼때필요한자원이없는경우 ( 큐의젂체길이가시스템최대값초과 ) 수면 IPC_NOWAIT : 메시지를젂달할수없는경우바로 return ( 복귀값 -1, errno EAGAIN)
메시지젂달 (2) msgrcv #include <sys/msg.h> int msgrcv(int mqid, void *message, size_t size, long msg_type, int flags); mqid 가가리키는큐에서메시지를읽고큐에서제거 message : 받아들일메시지를저장할장소 size : 저장할수있는최대길이 msg_type : 어떤메시지를받아들일지결정 0 : 가장일찍들어온메시지 양수값 : msg_type 이해당값을갖는첫번째메시지 음수값 : msg_type 의젃대값보다작거나같은것중최소값을갖는첫번째메시지 ( 숫자가작을수록높은우선순위 ) flags IPC_NOWAIT 가없는경우 : 메시지를받을수없는경우수면상태 IPC_NOWAIT : 메시지를받을수없는경우바로 return ( 복귀값 -1, errno EAGAIN) MSG_NOERROR : 메시지내용이 size 보다큰경우초과분을잘라내고정상처리 MSG_NOERROR 가없는데길이가 size 보다큰경우 : msgrcv 실패
mqueue Sample Code #1(1) /* mq.h */ #include <stdio.h> #include <stdlib.h> #include <sys/types.h> #include <sys/ipc.h> #include <sys/msg.h> #include <string.h> #include <errno.h> #define QKEY (key_t) 0100 #define QPERM 0660 #define MAX_NAME_LENGTH 50 #define MAX_PRIORITY 10 typedef struct { long mtype; char mtext[max_name_length + 1]; q_entry;
mqueue Sample Code #1(2) #include "mq.h" int enter_q (char *objname, int priority) { int len, s_qid; q_entry s_entry; if ((len = strlen(objname)) > MAX_NAME_LENGTH) { fprintf(stderr, "name too long\n"); return (-1); if ((priority > MAX_PRIORITY) (priority < 0)) { fprintf(stderr, "invalid priority level\n"); return(-2); void main(int argc, char **argv) { int priority; { if (argc!= 3) { fprintf(stderr, "usage : %s objname priority\n", argv[0]); exit(1); if (((priority = atoi(argv[2])) <= 0) (priority > MAX_PRIORITY)) fprintf(stderr, "invalid priority\n"); exit(2); if ((s_qid = msgget(qkey, IPC_CREAT QPERM)) == -1) { perror("msgget failed"); return (-3); s_entry.mtype = (long) priority; strncpy(s_entry.mtext, objname, MAX_NAME_LENGTH); if (msgsnd(s_qid, (void *)&s_entry, (size_t) len, 0) == -1) { perror("msgsnd failed"); return (-4); else return(0); if (enter_q(argv[1], priority) < 0) { fprintf(stderr, "enter failure\n"); exit(3); exit(0);
mqueue Sample Code #1(3) #include "mq.h" void main() { int mlen, r_qid; q_entry r_entry; if ((r_qid = msgget(qkey, IPC_CREAT QPERM)) == -1) { perror("msgget failed"); exit(-1); for (;;) { if ((mlen = msgrcv(r_qid, &r_entry, sizeof(q_entry), (-1 * MAX_PRIORITY), MSG_NOERROR)) == -1) { perror("msgrcv failed"); exit(-2); else { r_entry.mtext[mlen] = '\0'; printf("\npriority : %ld \tname : %s\n", r_entry.mtype, r_entry.mtext);
mqueue Sample Code 실행예 cara% client test1 7 cara% client test2 2 cara% client test3 6 cara% client test4 1 cara% client test5 8 cara% client test6 4 cara% server Priority : 1 Priority : 2 Priority : 4 Priority : 6 Priority : 7 Priority : 8 ^Ccara% Name : test4 Name : test2 Name : test6 Name : test3 Name : test1 Name : test5
메시지제어 msgctl 3 가지목적 메시지큐의상태정보얻기 메시지큐에관렦된제한변경 시스템상에서큐를통째로제거 #include <sys/msg.h> int msgctl(int mqid, int command, struct msqid_ds *msq_stat); mqid : 유효한메시지큐식별자 msqid_ds 주요구성원 struct ipc_perm msg_perm; /* 소유권 허가 */ msgqnum_t msg_qnum; /* 큐상의메시지수 */ msglen_t msg_qbytes; /* 큐상의최대바이트수 */ pid_t msg_lspid; /* 마지막 msgsnd 를수행한프로세스번호 */ pid_t msg_lrpid; /* 마지막 msgrcv 를수행한프로세스번호 */ time_t msg_stime; /* 마지막 msgsnd 시갂 */ time_t msg_rtime; /* 마지막 msgrcv 시갂 */ time_t msg_ctime; /* 마지막변경시갂 */ command IPC_STAT : 메시지큐상태정보를 msq_stat 에넣도록지시 IPC_SET : 메시지큐상태정보변경 msq_stat.msg_perm.uid msq_stat.msg_perm.gid msq_stat.msg_perm.mode msq_stat.msg_qbytes IPC_RM : 시스템에서이메시지큐삭제 이때 msq_stat 은 NULL 로설정
mqueue Sample Code #2 #include <stdio.h> #include <stdlib.h> #include <sys/types.h> #include <sys/ipc.h> #include <sys/msg.h> #include <time.h> void main(int argc, char* argv[]) { key_t mkey; int msq_id; struct msqid_ds msq_status; if (argc!= 2) { fprintf(stderr, "usage : %s keyval\n", argv[0]); exit(1); mkey = (key_t) atoi(argv[1]); if ((msq_id = msgget(mkey, 0)) == -1) { perror("msgget failed"); exit(2); if (msgctl(msq_id, IPC_STAT, &msq_status) == -1) { perror("msgctl failed"); exit(3); printf("\nkey : %d \tmsg_qid : %d", mkey, msq_id); printf("\n%d messages on queue", msq_status.msg_qnum); printf("\nlast send by %d at %s", msq_status.msg_lspid, ctime(&msq_status.msg_stime)); printf("\nlast recv by %d at %s", msq_status.msg_lrpid, ctime(&msq_status.msg_rtime));
ipcs 와 ipcrm 명령 IPC 설비를이용하기위한 2 가지명령 ipcs : IPC 설비의현재상태출력 cara% ipcs IPC status from <running system> as of 2011 년 5 월 24 일화요일오후 05 시 26 분 07 초 T ID KEY MODE OWNER GROUP Message Queues: q 0 0x40 --rw-rw---- kgu prof Shared Memory: Semaphores: cara% ipcrm : 시스템으로부터 IPC 설비제거 ( 소유자또는 super user 맊이용가능 ) cara% ipcrm -q 0 cara% ipcs IPC status from <running system> as of 2011 년 5 월 24 일화요일오후 05 시 28 분 04 초 T ID KEY MODE OWNER GROUP Message Queues: Shared Memory: Semaphores: cara%